Compare commits
78 Commits
Author | SHA1 | Date | |
---|---|---|---|
0743cb5ce5 | |||
971f026d55 | |||
774ac54e71 | |||
54a632f018 | |||
d63a2c9714 | |||
73aed233b2 | |||
2493b6e659 | |||
fc5a4946af | |||
b7a194f824 | |||
6c914be933 | |||
8b0255991e | |||
b22ce018c8 | |||
a1ccd6c700 | |||
297fd4f2ef | |||
ecb8817972 | |||
6119c05d7d | |||
2a318149f8 | |||
4a17c682e9 | |||
5252919799 | |||
85556fd38b | |||
da757ff09f | |||
fed81a77c9 | |||
c144c53039 | |||
0d4c5f27b5 | |||
9b194c2d99 | |||
d090f7a927 | |||
ad563b095c | |||
b5c48b97f6 | |||
8bdb53d91c | |||
ced0ff6765 | |||
1ff1a0b860 | |||
867e2d875b | |||
a729ccfe0f | |||
4c81b31652 | |||
0a0050746b | |||
06157f1659 | |||
c6abf6cd3f | |||
9a202fb8c2 | |||
a949f1de2d | |||
f89958d824 | |||
8e258a1933 | |||
0691ae20d8 | |||
c9b0a675e4 | |||
315b2222a8 | |||
bc401b3cbf | |||
92a4765a7e | |||
c76b8bf340 | |||
cdec109331 | |||
096b0eaed2 | |||
b24229cbbc | |||
55239c4ca2 | |||
a47df351ca | |||
4125ace620 | |||
aa32d179d0 | |||
9a362bd149 | |||
8366ff0ce0 | |||
ca1186946d | |||
26f4493405 | |||
e31340c243 | |||
5a5aa66ada | |||
0cc2ebeda2 | |||
6064932abf | |||
8bf9efad15 | |||
ff85c98683 | |||
57ec282d4f | |||
6e37f37c4e | |||
b8ad0dfc22 | |||
e47b68df57 | |||
e74894baeb | |||
589a744585 | |||
8ac052ad6f | |||
b34c1d5746 | |||
3f9907f93d | |||
ce1a78887a | |||
8ce8bebb7d | |||
00ae7eb234 | |||
d353e92425 | |||
6a4d14d5c0 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@
|
||||
build/
|
||||
depcomp
|
||||
install/
|
||||
subprojects/*/
|
||||
|
251
.gitlab-ci.yml
251
.gitlab-ci.yml
@ -10,6 +10,16 @@
|
||||
# as part of the build stage as there doesn't seem to be significant value to
|
||||
# splitting the stages at the moment.
|
||||
|
||||
# Create merge request pipelines for open merge requests, branch pipelines
|
||||
# otherwise. This allows MRs for new users to run CI, and prevents duplicate
|
||||
# pipelines for branches with open MRs.
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
|
||||
stages:
|
||||
- container
|
||||
- build
|
||||
@ -19,8 +29,8 @@ variables:
|
||||
# CI runs, for example when adding new packages to FDO_DISTRIBUTION_PACKAGES.
|
||||
# The tag is an arbitrary string that identifies the exact container
|
||||
# contents.
|
||||
BASE_TAG: '2020-11-27.1'
|
||||
FDO_DISTRIBUTION_VERSION: '20.10'
|
||||
BASE_TAG: '2024-12-26.0'
|
||||
FDO_DISTRIBUTION_VERSION: '41'
|
||||
FDO_UPSTREAM_REPO: 'pulseaudio/webrtc-audio-processing'
|
||||
|
||||
include:
|
||||
@ -30,10 +40,10 @@ include:
|
||||
# failures, this might be one cause.
|
||||
- project: 'freedesktop/ci-templates'
|
||||
ref: 'master'
|
||||
file: '/templates/ubuntu.yml'
|
||||
file: '/templates/fedora.yml'
|
||||
|
||||
# Common container build template
|
||||
.ubuntu-container-build:
|
||||
.fedora-container-build:
|
||||
variables:
|
||||
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
||||
|
||||
@ -41,20 +51,24 @@ include:
|
||||
# Otherwise the changes won't have effect since an old container image will
|
||||
# be used.
|
||||
FDO_DISTRIBUTION_PACKAGES: >-
|
||||
ca-certificates
|
||||
g++
|
||||
gcc
|
||||
libabsl-dev
|
||||
git-core
|
||||
cmake
|
||||
abseil-cpp-devel
|
||||
meson
|
||||
ninja-build
|
||||
pkg-config
|
||||
python3-setuptools
|
||||
|
||||
# Used to extend both container and build jobs
|
||||
.ubuntu-x86_64:
|
||||
.fedora-x86_64:
|
||||
variables:
|
||||
FDO_DISTRIBUTION_TAG: "x86_64-$BASE_TAG"
|
||||
|
||||
# Used to extend both container and build jobs
|
||||
.ubuntu-aarch64:
|
||||
.fedora-aarch64:
|
||||
tags:
|
||||
- aarch64
|
||||
variables:
|
||||
@ -62,37 +76,224 @@ include:
|
||||
|
||||
build-container-x86_64:
|
||||
extends:
|
||||
- .fdo.container-build@ubuntu@x86_64
|
||||
- .ubuntu-container-build
|
||||
- .ubuntu-x86_64
|
||||
- .fdo.container-build@fedora@x86_64
|
||||
- .fedora-container-build
|
||||
- .fedora-x86_64
|
||||
stage: container
|
||||
|
||||
build-container-aarch64:
|
||||
extends:
|
||||
- .fdo.container-build@ubuntu@aarch64
|
||||
- .ubuntu-container-build
|
||||
- .ubuntu-aarch64
|
||||
- .fdo.container-build@fedora@aarch64
|
||||
- .fedora-container-build
|
||||
- .fedora-aarch64
|
||||
stage: container
|
||||
|
||||
# Common build template
|
||||
.build:
|
||||
.build-distro-absl:
|
||||
stage: build
|
||||
extends:
|
||||
- .fdo.distribution-image@ubuntu
|
||||
- .fdo.distribution-image@fedora
|
||||
script:
|
||||
- meson build
|
||||
- cd build
|
||||
- ninja
|
||||
- meson setup --wrap-mode=nofallback --prefix=/usr --libdir=lib builddir
|
||||
- ninja -C builddir
|
||||
- DESTDIR=$PWD/_install ninja install -C builddir
|
||||
# Test that the pc files are usable
|
||||
- PKG_CONFIG_PATH=$PWD/_install/usr/lib/pkgconfig pkg-config --cflags --libs webrtc-audio-processing-2
|
||||
artifacts:
|
||||
expire_in: '5 days'
|
||||
when: 'always'
|
||||
paths:
|
||||
- build/
|
||||
- "builddir/meson-logs/*txt"
|
||||
|
||||
build-x86_64:
|
||||
.build-vendored-absl:
|
||||
stage: build
|
||||
extends:
|
||||
- .build
|
||||
- .ubuntu-x86_64
|
||||
- .fdo.distribution-image@fedora
|
||||
script:
|
||||
- meson setup --force-fallback-for=abseil-cpp --prefix=/usr --libdir=lib builddir
|
||||
- ninja -C builddir
|
||||
- DESTDIR=$PWD/_install ninja install -C builddir
|
||||
# Test that the pc files are usable
|
||||
- PKG_CONFIG_LIBDIR=$PWD/_install/usr/lib/pkgconfig pkg-config --cflags --libs webrtc-audio-processing-2
|
||||
artifacts:
|
||||
expire_in: '5 days'
|
||||
when: 'always'
|
||||
paths:
|
||||
- "builddir/meson-logs/*txt"
|
||||
|
||||
build-aarch64:
|
||||
build-distro-absl-x86_64:
|
||||
extends:
|
||||
- .build
|
||||
- .ubuntu-aarch64
|
||||
- .build-distro-absl
|
||||
- .fedora-x86_64
|
||||
|
||||
build-vendored-absl-x86_64:
|
||||
extends:
|
||||
- .build-vendored-absl
|
||||
- .fedora-x86_64
|
||||
|
||||
build-distro-absl-aarch64:
|
||||
extends:
|
||||
- .build-distro-absl
|
||||
- .fedora-aarch64
|
||||
|
||||
build-vendored-absl-aarch64:
|
||||
extends:
|
||||
- .build-vendored-absl
|
||||
- .fedora-aarch64
|
||||
|
||||
# Update from:
|
||||
# https://gitlab.freedesktop.org/gstreamer/gstreamer/-/blob/main/.gitlab-ci.yml
|
||||
# https://gitlab.freedesktop.org/gstreamer/orc/-/blob/main/.gitlab-ci.yml
|
||||
vs2019 amd64:
|
||||
# Update from https://gitlab.freedesktop.org/gstreamer/gstreamer/container_registry
|
||||
image: 'registry.freedesktop.org/gstreamer/gstreamer/amd64/windows:2023-04-21.0-main'
|
||||
stage: 'build'
|
||||
tags:
|
||||
- 'docker'
|
||||
- 'windows'
|
||||
- '2022'
|
||||
artifacts:
|
||||
expire_in: '5 days'
|
||||
when: 'always'
|
||||
paths:
|
||||
- "builddir/meson-logs/*txt"
|
||||
variables:
|
||||
# Make sure any failure in PowerShell scripts is fatal
|
||||
ErrorActionPreference: 'Stop'
|
||||
WarningPreference: 'Stop'
|
||||
ARCH: 'amd64'
|
||||
PLAT: 'Desktop'
|
||||
before_script:
|
||||
# Make sure meson is up to date, so we don't need to rebuild the image with each release
|
||||
- pip3 install -U meson ninja
|
||||
script:
|
||||
# Gitlab executes PowerShell in docker, but VsDevCmd.bat is a batch script.
|
||||
# Environment variables substitutions is done by PowerShell before calling
|
||||
# cmd.exe, that's why we use $env:FOO instead of %FOO%
|
||||
- cmd.exe /C "C:\BuildTools\Common7\Tools\VsDevCmd.bat -host_arch=amd64 -arch=$env:ARCH -app_platform=$env:PLAT &&
|
||||
meson setup builddir -Dcpp_std=c++20 &&
|
||||
meson compile --verbose -C builddir"
|
||||
|
||||
# Update from:
|
||||
# https://gitlab.freedesktop.org/gstreamer/cerbero/-/blob/main/.gitlab-ci.yml
|
||||
# https://gitlab.freedesktop.org/gstreamer/orc/-/blob/main/.gitlab-ci.yml
|
||||
macos x86_64:
|
||||
stage: 'build'
|
||||
tags:
|
||||
- gst-macos-13
|
||||
artifacts:
|
||||
expire_in: '5 days'
|
||||
when: 'always'
|
||||
paths:
|
||||
- "builddir/meson-logs/*txt"
|
||||
before_script:
|
||||
- pip3 install --upgrade pip
|
||||
# Need to install certificates for python
|
||||
- pip3 install --upgrade certifi
|
||||
# Anther way to install certificates
|
||||
- open /Applications/Python\ 3.8/Install\ Certificates.command
|
||||
# Make sure meson and ninja are up to date
|
||||
- pip3 install -U meson ninja
|
||||
script:
|
||||
- CERT_PATH=$(python3 -m certifi) && export SSL_CERT_FILE=${CERT_PATH} && export REQUESTS_CA_BUNDLE=${CERT_PATH}
|
||||
- meson setup builddir
|
||||
- meson compile --verbose -C builddir
|
||||
|
||||
# Update from:
|
||||
# https://gitlab.freedesktop.org/gstreamer/cerbero/-/blob/main/.gitlab-ci.yml
|
||||
# https://gitlab.freedesktop.org/gstreamer/orc/-/blob/main/.gitlab-ci.yml
|
||||
ios arm64:
|
||||
stage: 'build'
|
||||
tags:
|
||||
- gst-ios-16
|
||||
artifacts:
|
||||
name: "${CI_JOB_NAME}_${CI_COMMIT_SHA}"
|
||||
expire_in: '5 days'
|
||||
when: 'always'
|
||||
paths:
|
||||
- "builddir/meson-logs/*txt"
|
||||
before_script:
|
||||
- pip3 install --upgrade pip
|
||||
# Need to install certificates for python
|
||||
- pip3 install --upgrade certifi
|
||||
# Anther way to install certificates
|
||||
- open /Applications/Python\ 3.8/Install\ Certificates.command
|
||||
# Make sure meson and ninja are up to date
|
||||
- pip3 install -U meson ninja
|
||||
script:
|
||||
- CERT_PATH=$(python3 -m certifi) && export SSL_CERT_FILE=${CERT_PATH} && export REQUESTS_CA_BUNDLE=${CERT_PATH}
|
||||
- |
|
||||
cat > ios-cross-file.txt <<EOF
|
||||
[host_machine]
|
||||
system = 'darwin'
|
||||
cpu_family = 'aarch64'
|
||||
cpu = 'aarch64'
|
||||
endian = 'little'
|
||||
|
||||
[properties]
|
||||
c_args = ['-arch', 'arm64', '--sysroot=$(xcrun --sdk iphoneos --show-sdk-path)', '-miphoneos-version-min=11.0']
|
||||
objc_args = ['-arch', 'arm64', '--sysroot=$(xcrun --sdk iphoneos --show-sdk-path)', '-miphoneos-version-min=11.0']
|
||||
cpp_args = ['-arch', 'arm64', '--sysroot=$(xcrun --sdk iphoneos --show-sdk-path)', '-miphoneos-version-min=11.0']
|
||||
objcpp_args = ['-arch', 'arm64', '--sysroot=$(xcrun --sdk iphoneos --show-sdk-path)', '-miphoneos-version-min=11.0']
|
||||
c_link_args = ['-arch', 'arm64', '--sysroot=$(xcrun --sdk iphoneos --show-sdk-path)', '-miphoneos-version-min=11.0']
|
||||
objc_link_args = ['-arch', 'arm64', '--sysroot=$(xcrun --sdk iphoneos --show-sdk-path)', '-miphoneos-version-min=11.0']
|
||||
cpp_link_args = ['-arch', 'arm64', '--sysroot=$(xcrun --sdk iphoneos --show-sdk-path)', '-miphoneos-version-min=11.0']
|
||||
objcpp_link_args = ['-arch', 'arm64', '--sysroot=$(xcrun --sdk iphoneos --show-sdk-path)', '-miphoneos-version-min=11.0']
|
||||
|
||||
[binaries]
|
||||
ar = '$(xcrun --find --sdk iphoneos ar)'
|
||||
c = '$(xcrun --find --sdk iphoneos clang)'
|
||||
objc = '$(xcrun --find --sdk iphoneos clang)'
|
||||
cpp = '$(xcrun --find --sdk iphoneos clang++)'
|
||||
objcpp = '$(xcrun --find --sdk iphoneos clang++)'
|
||||
ranlib = '$(xcrun --find --sdk iphoneos ranlib)'
|
||||
strip = '$(xcrun --find --sdk iphoneos strip)'
|
||||
pkgconfig = 'false'
|
||||
cmake = 'false'
|
||||
EOF
|
||||
- meson setup --cross-file ios-cross-file.txt builddir
|
||||
- meson compile --verbose -C builddir
|
||||
|
||||
# Update from:
|
||||
# https://gitlab.freedesktop.org/gstreamer/cerbero/-/blob/main/.gitlab-ci.yml
|
||||
# https://gitlab.freedesktop.org/gstreamer/orc/-/blob/main/.gitlab-ci.yml
|
||||
android fedora arm64:
|
||||
# Update from https://gitlab.freedesktop.org/gstreamer/cerbero/container_registry
|
||||
image: 'registry.freedesktop.org/gstreamer/cerbero/amd64/android-fedora:2021-10-22.0-1.18'
|
||||
stage: 'build'
|
||||
artifacts:
|
||||
expire_in: '5 days'
|
||||
when: 'always'
|
||||
paths:
|
||||
- "builddir/meson-logs/*.txt"
|
||||
before_script:
|
||||
- dnf install -y python3-pip gcc ninja-build
|
||||
- pip3 install --user meson
|
||||
script:
|
||||
- export PATH="$HOME/.local/bin:$PATH"
|
||||
- |
|
||||
cat > android-cross-file.txt <<EOF
|
||||
[constants]
|
||||
ndk_path = '/android/ndk'
|
||||
toolchain = ndk_path + '/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android'
|
||||
api = '28'
|
||||
|
||||
[host_machine]
|
||||
system = 'android'
|
||||
cpu_family = 'aarch64'
|
||||
cpu = 'aarch64'
|
||||
endian = 'little'
|
||||
|
||||
[properties]
|
||||
sys_root = ndk_path + '/sysroot'
|
||||
c_link_args = ['-fuse-ld=gold']
|
||||
cpp_link_args = ['-fuse-ld=gold']
|
||||
|
||||
[binaries]
|
||||
c = toolchain + api + '-clang'
|
||||
cpp = toolchain + api + '-clang++'
|
||||
ar = toolchain + '-ar'
|
||||
strip = toolchain + '-strip'
|
||||
EOF
|
||||
- meson setup --cross-file android-cross-file.txt builddir
|
||||
- meson compile --verbose -C builddir
|
||||
|
63
NEWS
63
NEWS
@ -1,3 +1,66 @@
|
||||
Release 2.0
|
||||
-----------
|
||||
|
||||
Bump to code from WebRTC M131 version.
|
||||
|
||||
Changes include:
|
||||
|
||||
* Minor (breaking) API changes upstream
|
||||
* Various improvements to the AEC implementation
|
||||
* Transient suppression is removed
|
||||
* ExperimentalAgc and ExperimentalNs are removed
|
||||
* iSAC and the webrtc-audio-coding library were removed
|
||||
* abseil-cpp dependency bumped to 20240722
|
||||
* NEON runtime detection dropped following upstream
|
||||
* Fixes for building on i686 and MIPS
|
||||
* Support for BSDs is added
|
||||
* Other build-system cleanups
|
||||
* Patches to upstream are now also tracked in patches/
|
||||
|
||||
|
||||
Release 1.3
|
||||
-----------
|
||||
|
||||
Fix for generate pkg-config file.
|
||||
|
||||
|
||||
Release 1.2
|
||||
-----------
|
||||
|
||||
Improvements for building with abseil-cpp as a subproject, and pkg-config
|
||||
improvements for abseil dependency detection.
|
||||
|
||||
|
||||
Release 1.1
|
||||
-----------
|
||||
|
||||
Build fixes for various platforms.
|
||||
|
||||
|
||||
Release 1.0
|
||||
-----------
|
||||
|
||||
This is an API breaking release (as a reminder, the AudioProcessing module does
|
||||
not provide a stable public API, so we expose whatever API exists in the
|
||||
upstream project).
|
||||
|
||||
In order to make package management easier with these inevitable breakages, the
|
||||
package is now suffixed with a version (currently it is
|
||||
webrtc-audio-processing-1). When the next API break happens, we will bump the
|
||||
major version, allowing incompatible versions to coexist. This also means that
|
||||
the previous version can also coexist with this one. Non-breaking changes will
|
||||
see a minor version update only.
|
||||
|
||||
Changes:
|
||||
* The code base is now updated to correspond to the version shipping with the
|
||||
Chromium 88.0.4290.1 tag
|
||||
* There are a very large number changes to the underlying AEC implementation
|
||||
since the last update was a while ago. Most visibly the use of the AEC3
|
||||
canceller by default, the deletion of the beamformer code
|
||||
* The autotools build system is replaced by meson
|
||||
* The pkg-config name is changed as described above
|
||||
|
||||
|
||||
Release 0.3
|
||||
-----------
|
||||
|
||||
|
42
README.md
42
README.md
@ -1,9 +1,9 @@
|
||||
About
|
||||
=====
|
||||
# About
|
||||
|
||||
This is meant to be a more Linux packaging friendly copy of the AudioProcessing
|
||||
module from the WebRTC[1] project. The ideal case is that we make no changes to
|
||||
the code to make tracking upstream code easy.
|
||||
module from the [ WebRTC ](https://webrtc.googlesource.com/src) project. The
|
||||
ideal case is that we make no changes to the code to make tracking upstream
|
||||
code easy.
|
||||
|
||||
This package currently only includes the AudioProcessing bits, but I am very
|
||||
open to collaborating with other projects that wish to distribute other bits of
|
||||
@ -11,25 +11,33 @@ the code and hopefully eventually have a single point of packaging all the
|
||||
WebRTC code to help people reuse the code and avoid keeping private copies in
|
||||
several different projects.
|
||||
|
||||
[1] https://webrtc.googlesource.com/src
|
||||
# Building
|
||||
|
||||
Feedback
|
||||
========
|
||||
This project uses the [Meson build system](https://mesonbuild.com/). The
|
||||
quickest way to build is:
|
||||
|
||||
Patches, suggestions welcome. You can send them to the PulseAudio mailing
|
||||
list[2] or to me at the address below.
|
||||
```sh
|
||||
# Initialise into the build/ directory, for a prefixed install into the
|
||||
# install/ directory
|
||||
meson . build -Dprefix=$PWD/install
|
||||
|
||||
-- Arun Raghavan <mail@arunraghavan.net>
|
||||
# Run the actual build
|
||||
ninja -C build
|
||||
|
||||
[2] http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
|
||||
# Install locally
|
||||
ninja -C build install
|
||||
|
||||
Notes
|
||||
====
|
||||
# The libraries, headers, and pkg-config files are now in the install/
|
||||
# directory
|
||||
```
|
||||
|
||||
1. Some files need to be patch to avoid pulling in the gtest framework. This
|
||||
should ideally be pushed upstream in some way so we're able to just pull
|
||||
in what we need without changing anything.
|
||||
# Feedback
|
||||
|
||||
2. It might be nice to try LTO on the library. We build a lot of code as part
|
||||
Patches, suggestions welcome. You can file an issue on our Gitlab
|
||||
[repository](https://gitlab.freedesktop.org/pulseaudio/webrtc-audio-processing/).
|
||||
|
||||
# Notes
|
||||
|
||||
1. It might be nice to try LTO on the library. We build a lot of code as part
|
||||
of the main AudioProcessing module deps, and it's possible that this could
|
||||
provide significant space savings.
|
||||
|
15
RELEASING.md
15
RELEASING.md
@ -25,16 +25,19 @@ git tag -s -m 'WebRTC AudioProcessing v<X.y>' v<X.y>
|
||||
## Make a tarball
|
||||
|
||||
```sh
|
||||
git archive --format 'tar.gz' \
|
||||
--prefix webrtc-audio-processing-X.y -9 vX.y \
|
||||
> webrtc-audio-processing-X.y.tar.gz
|
||||
# The output will be in build/meson-dist/
|
||||
meson dist -C build --formats=gztar,xztar --include-subprojects
|
||||
```
|
||||
|
||||
## Checksum the tarball
|
||||
## Do a test build
|
||||
|
||||
```sh
|
||||
sha256sum webrtc-audio-processing-X.y.tar.gz \
|
||||
> webrtc-audio-processing-X.y.tar.gz.sha256
|
||||
tar xvf webrtc-audio-processing-X.y.tar.xz
|
||||
cd webrtc-audio-processing-X.y
|
||||
meson . build -Dprefix=$PWD/install
|
||||
ninja -C build
|
||||
ninja -C build install
|
||||
cd ..
|
||||
```
|
||||
|
||||
## Publish the files
|
||||
|
8
examples/meson.build
Normal file
8
examples/meson.build
Normal file
@ -0,0 +1,8 @@
|
||||
top_incdir = include_directories('..')
|
||||
|
||||
executable('run-offline',
|
||||
'run-offline.cpp',
|
||||
install: false,
|
||||
include_directories: top_incdir,
|
||||
dependencies: [audio_processing_dep, absl_dep]
|
||||
)
|
67
examples/run-offline.cpp
Normal file
67
examples/run-offline.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Asymptotic Inc. All Rights Reserved.
|
||||
* Author: Arun Raghavan <arun@asymptotic.io>
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can 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/scoped_refptr.h"
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <webrtc/modules/audio_processing/include/audio_processing.h>
|
||||
|
||||
#define DEFAULT_BLOCK_MS 10
|
||||
#define DEFAULT_RATE 32000
|
||||
#define DEFAULT_CHANNELS 1
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 4) {
|
||||
std::cerr << "Usage: " << argv[0] << " <play_file> <rec_file> <out_file>" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::ifstream play_file(argv[1], std::ios::binary);
|
||||
std::ifstream rec_file(argv[2], std::ios::binary);
|
||||
std::ofstream aec_file(argv[3], std::ios::binary);
|
||||
|
||||
rtc::scoped_refptr<webrtc::AudioProcessing> apm = webrtc::AudioProcessingBuilder().Create();
|
||||
|
||||
webrtc::AudioProcessing::Config config;
|
||||
config.echo_canceller.enabled = true;
|
||||
config.echo_canceller.mobile_mode = false;
|
||||
config.gain_controller1.enabled = true;
|
||||
config.gain_controller1.mode = webrtc::AudioProcessing::Config::GainController1::kAdaptiveAnalog;
|
||||
|
||||
config.gain_controller2.enabled = true;
|
||||
|
||||
config.high_pass_filter.enabled = true;
|
||||
|
||||
apm->ApplyConfig(config);
|
||||
|
||||
webrtc::StreamConfig stream_config(DEFAULT_RATE, DEFAULT_CHANNELS);
|
||||
|
||||
while (!play_file.eof() && !rec_file.eof()) {
|
||||
int16_t play_frame[DEFAULT_RATE * DEFAULT_BLOCK_MS / 1000 * DEFAULT_CHANNELS];
|
||||
int16_t rec_frame[DEFAULT_RATE * DEFAULT_BLOCK_MS / 1000 * DEFAULT_CHANNELS];
|
||||
|
||||
play_file.read(reinterpret_cast<char *>(play_frame), sizeof(play_frame));
|
||||
rec_file.read(reinterpret_cast<char *>(rec_frame), sizeof(rec_frame));
|
||||
|
||||
apm->ProcessReverseStream(play_frame, stream_config, stream_config, play_frame);
|
||||
apm->ProcessStream(rec_frame, stream_config, stream_config, rec_frame);
|
||||
|
||||
aec_file.write(reinterpret_cast<char *>(rec_frame), sizeof(rec_frame));
|
||||
}
|
||||
|
||||
play_file.close();
|
||||
rec_file.close();
|
||||
aec_file.close();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
163
meson.build
163
meson.build
@ -1,10 +1,10 @@
|
||||
project('webrtc-audio-processing-1', 'c', 'cpp',
|
||||
version : '1.0',
|
||||
meson_version : '>= 0.54',
|
||||
project('webrtc-audio-processing', 'c', 'cpp',
|
||||
version : '2.0',
|
||||
meson_version : '>= 0.63',
|
||||
default_options : [ 'warning_level=1',
|
||||
'buildtype=debugoptimized',
|
||||
'c_std=c11',
|
||||
'cpp_std=c++14',
|
||||
'cpp_std=c++17',
|
||||
]
|
||||
)
|
||||
|
||||
@ -19,41 +19,58 @@ minor_version = version_split[1]
|
||||
|
||||
apm_major_version = major_version
|
||||
apm_minor_version = minor_version
|
||||
apm_version = apm_major_version + '.' + apm_minor_version
|
||||
apm_project_name = 'webrtc-audio-processing-' + apm_major_version
|
||||
|
||||
ac_major_version = major_version
|
||||
ac_minor_version = minor_version
|
||||
ac_version = ac_major_version + '.' + ac_minor_version
|
||||
ac_project_name = 'webrtc-audio-coding-' + ac_major_version
|
||||
|
||||
include_subdir = meson.project_name()
|
||||
include_subdir = apm_project_name
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
cpp = meson.get_compiler('cpp')
|
||||
|
||||
host_system = host_machine.system()
|
||||
|
||||
# Don't rely on the cross file setting the system properly when targeting ios
|
||||
if host_system == 'darwin' and meson.is_cross_build()
|
||||
ios_test_code = '''#include <TargetConditionals.h>
|
||||
#if ! TARGET_OS_IPHONE
|
||||
#error "Not iOS/tvOS/watchOS/iPhoneSimulator"
|
||||
#endif'''
|
||||
if cc.compiles(ios_test_code, name : 'building for iOS')
|
||||
host_system = 'ios'
|
||||
endif
|
||||
endif
|
||||
|
||||
platform_cflags = []
|
||||
os_cflags = []
|
||||
os_deps = []
|
||||
have_posix = false
|
||||
have_win = false
|
||||
|
||||
# Let's use pkg-config if available. This will also fallback to the subproject
|
||||
# if pkg-config is not found, which is really the most reliable way of building
|
||||
# abseil due to strict C++ standard match requirements.
|
||||
absl_dep = [
|
||||
cpp.find_library('absl_base'),
|
||||
cpp.find_library('absl_bad_optional_access'),
|
||||
cpp.find_library('absl_flags_internal'),
|
||||
cpp.find_library('absl_flags_marshalling'),
|
||||
cpp.find_library('absl_flags_parse'),
|
||||
cpp.find_library('absl_flags_registry'),
|
||||
cpp.find_library('absl_flags_usage_internal'),
|
||||
cpp.find_library('absl_raw_logging_internal'),
|
||||
cpp.find_library('absl_strings'),
|
||||
cpp.find_library('absl_synchronization'),
|
||||
cpp.find_library('absl_throw_delegate'),
|
||||
dependency('absl_base', default_options: ['cpp_std=c++17'], version: '>=20240722'),
|
||||
dependency('absl_flags'),
|
||||
dependency('absl_strings'),
|
||||
dependency('absl_synchronization'),
|
||||
dependency('absl_bad_optional_access'),
|
||||
]
|
||||
|
||||
if absl_dep[0].type_name() == 'internal'
|
||||
absl_subproj = subproject('abseil-cpp')
|
||||
headers = [
|
||||
absl_subproj.get_variable('absl_base_headers'),
|
||||
absl_subproj.get_variable('absl_flags_headers'),
|
||||
absl_subproj.get_variable('absl_strings_headers'),
|
||||
absl_subproj.get_variable('absl_synchronization_headers'),
|
||||
absl_subproj.get_variable('absl_types_headers'),
|
||||
]
|
||||
install_headers(headers, preserve_path: true)
|
||||
pc_requires = []
|
||||
else
|
||||
pc_requires = [absl_dep[0]]
|
||||
endif
|
||||
|
||||
if ['darwin', 'ios'].contains(host_system)
|
||||
os_cflags = ['-DWEBRTC_MAC']
|
||||
if host_system == 'ios'
|
||||
@ -66,12 +83,23 @@ elif host_system == 'android'
|
||||
os_deps += [dependency('gnustl', required : get_option('gnustl'))]
|
||||
have_posix = true
|
||||
elif host_system == 'linux'
|
||||
os_cflags += ['-DWEBRTC_LINUX', '-DWEBRTC_THREAD_RR']
|
||||
os_cflags += ['-DWEBRTC_LINUX']
|
||||
os_deps += [cc.find_library('rt', required : false)]
|
||||
os_deps += [dependency('threads')]
|
||||
have_posix = true
|
||||
elif (host_system == 'dragonfly' or host_system == 'freebsd' or
|
||||
host_system == 'netbsd' or host_system == 'openbsd')
|
||||
os_cflags += ['-DWEBRTC_BSD']
|
||||
os_deps += [dependency('threads')]
|
||||
have_posix = true
|
||||
elif host_system == 'windows'
|
||||
platform_cflags += ['-DWEBRTC_WIN', '-D_WIN32', '-U__STRICT_ANSI__']
|
||||
platform_cflags += ['-DWEBRTC_WIN', '-D_WIN32']
|
||||
# this one is for MinGW to get format specifiers from inttypes.h in C++
|
||||
platform_cflags += ['-D__STDC_FORMAT_MACROS=1']
|
||||
# Avoid min/max from windows.h which breaks std::min/max
|
||||
platform_cflags += ['-DNOMINMAX']
|
||||
# Ensure M_PI etc are defined
|
||||
platform_cflags += ['-D_USE_MATH_DEFINES']
|
||||
os_deps += [cc.find_library('winmm')]
|
||||
have_win = true
|
||||
endif
|
||||
@ -83,13 +111,15 @@ endif
|
||||
arch_cflags = []
|
||||
have_arm = false
|
||||
have_armv7 = false
|
||||
have_arm64 = false
|
||||
have_neon = false
|
||||
have_mips = false
|
||||
have_mips64 = false
|
||||
have_x86 = false
|
||||
have_inline_sse = false
|
||||
have_avx2 = false
|
||||
if ['arm', 'armv7'].contains(host_machine.cpu_family())
|
||||
if cc.compiles('''#ifdef __ARM_ARCH_ISA_ARM
|
||||
if host_machine.cpu_family() == 'arm'
|
||||
if cc.compiles('''#ifndef __ARM_ARCH_ISA_ARM
|
||||
#error no arm arch
|
||||
#endif''')
|
||||
have_arm = true
|
||||
@ -101,84 +131,85 @@ if ['arm', 'armv7'].contains(host_machine.cpu_family())
|
||||
have_armv7 = true
|
||||
arch_cflags += ['-DWEBRTC_ARCH_ARM_V7']
|
||||
endif
|
||||
endif
|
||||
if cc.compiles('''#ifndef __aarch64__
|
||||
#error no aarch64 arch
|
||||
#endif''')
|
||||
if cc.compiles('#include <arm_neon.h>', args : '-mfpu=neon')
|
||||
have_neon = true
|
||||
arch_cflags += ['-DWEBRTC_ARCH_ARM64', '-DWEBRTC_HAS_NEON']
|
||||
endif
|
||||
endif
|
||||
if host_machine.cpu_family() == 'aarch64'
|
||||
have_arm64 = true
|
||||
have_neon = true
|
||||
arch_cflags += ['-DWEBRTC_ARCH_ARM64']
|
||||
endif
|
||||
if ['mips', 'mips64'].contains(host_machine.cpu_family())
|
||||
have_mips = true
|
||||
arch_cflags += ['WEBRTC_ARCH_MIPS_FAMILY']
|
||||
endif
|
||||
if host_machine.cpu_family() == 'mips64'
|
||||
have_mips64 = true
|
||||
endif
|
||||
if ['x86', 'x86_64'].contains(host_machine.cpu_family())
|
||||
have_x86 = true
|
||||
# This is unconditionally enabled for now, actual usage is determined by
|
||||
# runtime CPU detection, so we're just assuming the compiler supports avx2
|
||||
# AVX2 support is unconditionally available, since all the code (compiled
|
||||
# with -mavx2) is in separate files from runtime detection (which should not
|
||||
# be compiled with SIMD flags for cases where the CPU does not support it).
|
||||
# Unfortunately, a bunch of SSE code is inline with the runtime detection,
|
||||
# and we can't support that on systems that don't support SSE.
|
||||
have_avx2 = true
|
||||
arch_cflags += ['-DWEBRTC_ENABLE_AVX2']
|
||||
if get_option('inline-sse')
|
||||
have_inline_sse = true
|
||||
else
|
||||
have_inline_sse = false
|
||||
arch_cflags += ['-DWAP_DISABLE_INLINE_SSE']
|
||||
endif
|
||||
endif
|
||||
|
||||
neon_opt = get_option('neon')
|
||||
if neon_opt != 'no'
|
||||
if neon_opt != 'runtime'
|
||||
if cc.compiles('#include <arm_neon.h>', args : '-mfpu=neon')
|
||||
arch_cflags += ['-mfpu=neon', '-DWEBRTC_HAS_NEON']
|
||||
have_neon = true
|
||||
endif
|
||||
else
|
||||
neon_opt += ['-DWEBRTC_DETECT_NEON', '-mfpu=neon']
|
||||
have_neon = true
|
||||
neon_opt = get_option('neon').require(have_neon)
|
||||
|
||||
if neon_opt.enabled()
|
||||
arch_cflags += ['-DWEBRTC_HAS_NEON']
|
||||
if not have_arm64
|
||||
arch_cflags += ['-mfpu=neon']
|
||||
endif
|
||||
endif
|
||||
|
||||
common_cflags = [
|
||||
'-DWEBRTC_LIBRARY_IMPL',
|
||||
'-DWEBRTC_ENABLE_SYMBOL_EXPORT',
|
||||
# avoid windows.h/winsock2.h conflicts
|
||||
'-D_WINSOCKAPI_',
|
||||
'-DNDEBUG'
|
||||
] + platform_cflags + os_cflags + arch_cflags
|
||||
common_cxxflags = common_cflags
|
||||
common_deps = os_deps + [absl_dep]
|
||||
webrtc_inc = include_directories('.')
|
||||
|
||||
# FIXME: use the unstable-simd module instead
|
||||
if cc.get_define('_MSC_VER') != ''
|
||||
avx_flags = ['/arch:AVX2']
|
||||
else
|
||||
avx_flags = ['-mavx2', '-mfma']
|
||||
endif
|
||||
|
||||
subdir('webrtc')
|
||||
|
||||
pkgconfig = import('pkgconfig')
|
||||
|
||||
pkgconfig.generate(
|
||||
name: apm_project_name,
|
||||
libwebrtc_audio_processing,
|
||||
description: 'WebRTC Audio Processing library',
|
||||
version: apm_major_version + '.' + apm_minor_version,
|
||||
filebase: apm_project_name,
|
||||
subdirs: include_subdir,
|
||||
requires: pc_requires,
|
||||
extra_cflags: [
|
||||
'-DWEBRTC_LIBRARY_IMPL',
|
||||
] + platform_cflags,
|
||||
libraries: libwebrtc_audio_processing,
|
||||
)
|
||||
|
||||
audio_processing_dep = declare_dependency(link_with : libwebrtc_audio_processing,
|
||||
include_directories : [webrtc_inc])
|
||||
audio_processing_dep = declare_dependency(
|
||||
link_with: libwebrtc_audio_processing,
|
||||
dependencies: [absl_dep],
|
||||
include_directories: [webrtc_inc]
|
||||
)
|
||||
|
||||
meson.override_dependency(apm_project_name, audio_processing_dep)
|
||||
|
||||
pkgconfig.generate(
|
||||
name: ac_project_name,
|
||||
description: 'WebRTC Audio Coding library',
|
||||
version: ac_major_version + '.' + ac_minor_version,
|
||||
filebase: ac_project_name,
|
||||
subdirs: include_subdir,
|
||||
extra_cflags: [
|
||||
'-DWEBRTC_LIBRARY_IMPL',
|
||||
] + platform_cflags,
|
||||
libraries: libwebrtc_audio_coding,
|
||||
)
|
||||
|
||||
audio_coding_dep = declare_dependency(link_with : libwebrtc_audio_coding,
|
||||
include_directories : [webrtc_inc])
|
||||
|
||||
meson.override_dependency(ac_project_name, audio_coding_dep)
|
||||
subdir('examples')
|
||||
|
@ -1,6 +1,9 @@
|
||||
option('gnustl', type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Use gnustl for a c++ library implementation (only used on Android)')
|
||||
option('neon', type: 'combo',
|
||||
choices: ['no', 'yes', 'auto', 'runtime'],
|
||||
description: '')
|
||||
option('neon', type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Enable NEON optimisations')
|
||||
option('inline-sse', type: 'boolean',
|
||||
value: true,
|
||||
description: 'Enable inline SSE/SSE2 optimisations (i.e. assume CPU supports SSE/SSE2)')
|
||||
|
@ -0,0 +1,68 @@
|
||||
From 297fd4f2efc53b6d49433eaad91a8e09a0f9cbec Mon Sep 17 00:00:00 2001
|
||||
From: Alper Nebi Yasak <alpernebiyasak@gmail.com>
|
||||
Date: Fri, 25 Oct 2024 00:40:59 +0300
|
||||
Subject: [PATCH] AECM: MIPS: Use uintptr_t for pointer arithmetic
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Trying to compile the MIPS-specific AECM audio processing file for
|
||||
mips64el on Debian results in the following errors:
|
||||
|
||||
../webrtc/modules/audio_processing/aecm/aecm_core_mips.cc: In function ‘int webrtc::WebRtcAecm_ProcessBlock(AecmCore*, const int16_t*, const int16_t*, const int16_t*, int16_t*)’:
|
||||
../webrtc/modules/audio_processing/aecm/aecm_core_mips.cc:955:30: error: cast from ‘int16_t*’ {aka ‘short int*’} to ‘uint32_t’ {aka ‘unsigned int’} loses precision [-fpermissive]
|
||||
955 | int16_t* fft = (int16_t*)(((uint32_t)fft_buf + 31) & ~31);
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
../webrtc/modules/audio_processing/aecm/aecm_core_mips.cc:955:18: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
|
||||
955 | int16_t* fft = (int16_t*)(((uint32_t)fft_buf + 31) & ~31);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
../webrtc/modules/audio_processing/aecm/aecm_core_mips.cc:956:36: error: cast from ‘int32_t*’ {aka ‘int*’} to ‘uint32_t’ {aka ‘unsigned int’} loses precision [-fpermissive]
|
||||
956 | int32_t* echoEst32 = (int32_t*)(((uint32_t)echoEst32_buf + 31) & ~31);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~
|
||||
../webrtc/modules/audio_processing/aecm/aecm_core_mips.cc:956:24: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
|
||||
956 | int32_t* echoEst32 = (int32_t*)(((uint32_t)echoEst32_buf + 31) & ~31);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
../webrtc/modules/audio_processing/aecm/aecm_core_mips.cc:957:40: error: cast from ‘int32_t*’ {aka ‘int*’} to ‘uint32_t’ {aka ‘unsigned int’} loses precision [-fpermissive]
|
||||
957 | ComplexInt16* dfw = (ComplexInt16*)(((uint32_t)dfw_buf + 31) & ~31);
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
../webrtc/modules/audio_processing/aecm/aecm_core_mips.cc:957:23: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
|
||||
957 | ComplexInt16* dfw = (ComplexInt16*)(((uint32_t)dfw_buf + 31) & ~31);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
../webrtc/modules/audio_processing/aecm/aecm_core_mips.cc:958:40: error: cast from ‘int32_t*’ {aka ‘int*’} to ‘uint32_t’ {aka ‘unsigned int’} loses precision [-fpermissive]
|
||||
958 | ComplexInt16* efw = (ComplexInt16*)(((uint32_t)efw_buf + 31) & ~31);
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
../webrtc/modules/audio_processing/aecm/aecm_core_mips.cc:958:23: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
|
||||
958 | ComplexInt16* efw = (ComplexInt16*)(((uint32_t)efw_buf + 31) & ~31);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Presumably, this file was written for 32-bit MIPS so the author used
|
||||
uint32_t to do pointer arithmetic over these arrays. Fix the errors by
|
||||
using uintptr_t to work with pointers.
|
||||
|
||||
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
|
||||
---
|
||||
webrtc/modules/audio_processing/aecm/aecm_core_mips.cc | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/webrtc/modules/audio_processing/aecm/aecm_core_mips.cc b/webrtc/modules/audio_processing/aecm/aecm_core_mips.cc
|
||||
index 16b03cf..07c785e 100644
|
||||
--- a/webrtc/modules/audio_processing/aecm/aecm_core_mips.cc
|
||||
+++ b/webrtc/modules/audio_processing/aecm/aecm_core_mips.cc
|
||||
@@ -952,10 +952,10 @@ int WebRtcAecm_ProcessBlock(AecmCore* aecm,
|
||||
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* 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;
|
||||
--
|
||||
2.47.1
|
||||
|
110
patches/0001-Add-support-for-BSD-systems.patch
Normal file
110
patches/0001-Add-support-for-BSD-systems.patch
Normal file
@ -0,0 +1,110 @@
|
||||
From 2a318149f8d5094c82306b8091a7a8b5194bf9c1 Mon Sep 17 00:00:00 2001
|
||||
From: Jan Beich <jbeich@FreeBSD.org>
|
||||
Date: Tue, 7 Jan 2020 18:08:24 +0000
|
||||
Subject: [PATCH] Add support for BSD systems
|
||||
|
||||
webrtc/rtc_base/checks.cc:158:28: error: use of undeclared identifier 'LAST_SYSTEM_ERROR'
|
||||
158 | file, line, LAST_SYSTEM_ERROR, message);
|
||||
| ^
|
||||
webrtc/rtc_base/checks.cc:220:16: error: use of undeclared identifier 'LAST_SYSTEM_ERROR'
|
||||
220 | LAST_SYSTEM_ERROR);
|
||||
| ^
|
||||
In file included from webrtc/rtc_base/platform_thread_types.cc:11:
|
||||
webrtc/rtc_base/platform_thread_types.h:47:1: error: unknown type name 'PlatformThreadId'
|
||||
47 | PlatformThreadId CurrentThreadId();
|
||||
| ^
|
||||
webrtc/rtc_base/platform_thread_types.h:52:1: error: unknown type name 'PlatformThreadRef'
|
||||
52 | PlatformThreadRef CurrentThreadRef();
|
||||
| ^
|
||||
webrtc/rtc_base/platform_thread_types.h:55:29: error: unknown type name 'PlatformThreadRef'
|
||||
55 | bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b);
|
||||
| ^
|
||||
webrtc/rtc_base/platform_thread_types.h:55:57: error: unknown type name 'PlatformThreadRef'
|
||||
55 | bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b);
|
||||
| ^
|
||||
webrtc/rtc_base/platform_thread_types.cc:37:1: error: unknown type name 'PlatformThreadId'
|
||||
37 | PlatformThreadId CurrentThreadId() {
|
||||
| ^
|
||||
webrtc/rtc_base/platform_thread_types.cc:58:1: error: unknown type name 'PlatformThreadRef'
|
||||
58 | PlatformThreadRef CurrentThreadRef() {
|
||||
| ^
|
||||
webrtc/rtc_base/platform_thread_types.cc:68:29: error: unknown type name 'PlatformThreadRef'
|
||||
68 | bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b) {
|
||||
| ^
|
||||
webrtc/rtc_base/platform_thread_types.cc:68:57: error: unknown type name 'PlatformThreadRef'
|
||||
68 | bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b) {
|
||||
| ^
|
||||
In file included from webrtc/rtc_base/event_tracer.cc:30:
|
||||
In file included from webrtc/api/sequence_checker.h:15:
|
||||
In file included from webrtc/rtc_base/synchronization/sequence_checker_internal.h:18:
|
||||
webrtc/rtc_base/synchronization/mutex.h:28:2: error: Unsupported platform.
|
||||
28 | #error Unsupported platform.
|
||||
| ^
|
||||
webrtc/rtc_base/synchronization/mutex.h:52:3: error: unknown type name 'MutexImpl'
|
||||
52 | MutexImpl impl_;
|
||||
| ^
|
||||
---
|
||||
meson.build | 5 +++++
|
||||
webrtc/rtc_base/platform_thread_types.cc | 16 ++++++++++++++++
|
||||
2 files changed, 21 insertions(+)
|
||||
|
||||
diff --git a/meson.build b/meson.build
|
||||
index 8d85d56..05a434a 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -87,6 +87,11 @@ elif host_system == 'linux'
|
||||
os_deps += [cc.find_library('rt', required : false)]
|
||||
os_deps += [dependency('threads')]
|
||||
have_posix = true
|
||||
+elif (host_system == 'dragonfly' or host_system == 'freebsd' or
|
||||
+ host_system == 'netbsd' or host_system == 'openbsd')
|
||||
+ os_cflags += ['-DWEBRTC_BSD', '-DWEBRTC_THREAD_RR']
|
||||
+ os_deps += [dependency('threads')]
|
||||
+ have_posix = true
|
||||
elif host_system == 'windows'
|
||||
platform_cflags += ['-DWEBRTC_WIN', '-D_WIN32']
|
||||
# this one is for MinGW to get format specifiers from inttypes.h in C++
|
||||
diff --git a/webrtc/rtc_base/platform_thread_types.cc b/webrtc/rtc_base/platform_thread_types.cc
|
||||
index d64ea68..e98e8ec 100644
|
||||
--- a/webrtc/rtc_base/platform_thread_types.cc
|
||||
+++ b/webrtc/rtc_base/platform_thread_types.cc
|
||||
@@ -15,6 +15,12 @@
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) // WEBRTC_BSD
|
||||
+#include <pthread_np.h>
|
||||
+#elif defined(__NetBSD__) // WEBRTC_BSD
|
||||
+#include <lwp.h>
|
||||
+#endif
|
||||
+
|
||||
#if defined(WEBRTC_WIN)
|
||||
#include "rtc_base/arraysize.h"
|
||||
|
||||
@@ -46,6 +52,12 @@ PlatformThreadId CurrentThreadId() {
|
||||
return zx_thread_self();
|
||||
#elif defined(WEBRTC_LINUX)
|
||||
return syscall(__NR_gettid);
|
||||
+#elif defined(__DragonFly__) || defined(__FreeBSD__) // WEBRTC_BSD
|
||||
+ return pthread_getthreadid_np();
|
||||
+#elif defined(__NetBSD__) // WEBRTC_BSD
|
||||
+ return _lwp_self();
|
||||
+#elif defined(__OpenBSD__) // WEBRTC_BSD
|
||||
+ return getthrid();
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
return static_cast<PlatformThreadId>(pthread_self());
|
||||
#else
|
||||
@@ -116,6 +128,10 @@ void SetCurrentThreadName(const char* name) {
|
||||
prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name)); // NOLINT
|
||||
#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
|
||||
pthread_setname_np(name);
|
||||
+#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) // WEBRTC_BSD
|
||||
+ pthread_set_name_np(pthread_self(), name);
|
||||
+#elif defined(__NetBSD__) // WEBRTC_BSD
|
||||
+ pthread_setname_np(pthread_self(), "%s", (void*)name);
|
||||
#elif defined(WEBRTC_FUCHSIA)
|
||||
zx_status_t status = zx_object_set_property(zx_thread_self(), ZX_PROP_NAME,
|
||||
name, strlen(name));
|
||||
--
|
||||
2.47.1
|
||||
|
336
patches/0001-Allow-disabling-inline-SSE.patch
Normal file
336
patches/0001-Allow-disabling-inline-SSE.patch
Normal file
@ -0,0 +1,336 @@
|
||||
From fed81a77c9a9bc366556f732324cdc5f9e7b09e9 Mon Sep 17 00:00:00 2001
|
||||
From: Arun Raghavan <arun@asymptotic.io>
|
||||
Date: Thu, 26 Dec 2024 14:24:40 -0500
|
||||
Subject: [PATCH] Allow disabling inline SSE
|
||||
|
||||
Should make building on i686 without SSE feasible.
|
||||
|
||||
Fixes: https://gitlab.freedesktop.org/pulseaudio/webrtc-audio-processing/-/issues/5
|
||||
---
|
||||
meson.build | 14 ++++++++++++--
|
||||
meson_options.txt | 5 ++++-
|
||||
.../audio_processing/aec3/adaptive_fir_filter.cc | 14 ++++++++++----
|
||||
.../aec3/adaptive_fir_filter_erl.cc | 6 ++++--
|
||||
webrtc/modules/audio_processing/aec3/fft_data.h | 4 +++-
|
||||
.../audio_processing/aec3/matched_filter.cc | 6 ++++--
|
||||
webrtc/modules/audio_processing/aec3/vector_math.h | 8 +++++---
|
||||
.../audio_processing/agc2/rnn_vad/vector_math.h | 4 +++-
|
||||
webrtc/third_party/pffft/meson.build | 2 +-
|
||||
9 files changed, 46 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/meson.build b/meson.build
|
||||
index 811d795..ebf053a 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -110,6 +110,7 @@ have_neon = false
|
||||
have_mips = false
|
||||
have_mips64 = false
|
||||
have_x86 = false
|
||||
+have_inline_sse = false
|
||||
have_avx2 = false
|
||||
if host_machine.cpu_family() == 'arm'
|
||||
if cc.compiles('''#ifndef __ARM_ARCH_ISA_ARM
|
||||
@@ -140,10 +141,19 @@ if host_machine.cpu_family() == 'mips64'
|
||||
endif
|
||||
if ['x86', 'x86_64'].contains(host_machine.cpu_family())
|
||||
have_x86 = true
|
||||
- # This is unconditionally enabled for now, actual usage is determined by
|
||||
- # runtime CPU detection, so we're just assuming the compiler supports avx2
|
||||
+ # AVX2 support is unconditionally available, since all the code (compiled
|
||||
+ # with -mavx2) is in separate files from runtime detection (which should not
|
||||
+ # be compiled with SIMD flags for cases where the CPU does not support it).
|
||||
+ # Unfortunately, a bunch of SSE code is inline with the runtime detection,
|
||||
+ # and we can't support that on systems that don't support SSE.
|
||||
have_avx2 = true
|
||||
arch_cflags += ['-DWEBRTC_ENABLE_AVX2']
|
||||
+ if get_option('inline-sse')
|
||||
+ have_inline_sse = true
|
||||
+ else
|
||||
+ have_inline_sse = false
|
||||
+ arch_cflags += ['-DWAP_DISABLE_INLINE_SSE']
|
||||
+ endif
|
||||
endif
|
||||
|
||||
neon_opt = get_option('neon')
|
||||
diff --git a/meson_options.txt b/meson_options.txt
|
||||
index c939fb9..d08f356 100644
|
||||
--- a/meson_options.txt
|
||||
+++ b/meson_options.txt
|
||||
@@ -3,4 +3,7 @@ option('gnustl', type: 'feature',
|
||||
description: 'Use gnustl for a c++ library implementation (only used on Android)')
|
||||
option('neon', type: 'combo',
|
||||
choices: ['no', 'yes', 'auto', 'runtime'],
|
||||
- description: '')
|
||||
+ description: 'Enable NEON optimisations')
|
||||
+option('inline-sse', type: 'boolean',
|
||||
+ value: true,
|
||||
+ description: 'Enable inline SSE/SSE2 optimisations (i.e. assume CPU supports SSE/SSE2)')
|
||||
diff --git a/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc
|
||||
index 917aa95..ded0511 100644
|
||||
--- a/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc
|
||||
+++ b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc
|
||||
@@ -16,7 +16,7 @@
|
||||
#if defined(WEBRTC_HAS_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
@@ -88,7 +88,7 @@ void ComputeFrequencyResponse_Neon(
|
||||
}
|
||||
#endif
|
||||
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
// Computes and stores the frequency response of the filter.
|
||||
void ComputeFrequencyResponse_Sse2(
|
||||
size_t num_partitions,
|
||||
@@ -212,7 +212,7 @@ void AdaptPartitions_Neon(const RenderBuffer& render_buffer,
|
||||
}
|
||||
#endif
|
||||
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
// Adapts the filter partitions. (SSE2 variant)
|
||||
void AdaptPartitions_Sse2(const RenderBuffer& render_buffer,
|
||||
const FftData& G,
|
||||
@@ -377,7 +377,7 @@ void ApplyFilter_Neon(const RenderBuffer& render_buffer,
|
||||
}
|
||||
#endif
|
||||
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
// Produces the filter output (SSE2 variant).
|
||||
void ApplyFilter_Sse2(const RenderBuffer& render_buffer,
|
||||
size_t num_partitions,
|
||||
@@ -557,9 +557,11 @@ void AdaptiveFirFilter::Filter(const RenderBuffer& render_buffer,
|
||||
RTC_DCHECK(S);
|
||||
switch (optimization_) {
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if !defined(WAP_DISABLE_INLINE_SSE)
|
||||
case Aec3Optimization::kSse2:
|
||||
aec3::ApplyFilter_Sse2(render_buffer, current_size_partitions_, H_, S);
|
||||
break;
|
||||
+#endif
|
||||
case Aec3Optimization::kAvx2:
|
||||
aec3::ApplyFilter_Avx2(render_buffer, current_size_partitions_, H_, S);
|
||||
break;
|
||||
@@ -601,9 +603,11 @@ void AdaptiveFirFilter::ComputeFrequencyResponse(
|
||||
|
||||
switch (optimization_) {
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if !defined(WAP_DISABLE_INLINE_SSE)
|
||||
case Aec3Optimization::kSse2:
|
||||
aec3::ComputeFrequencyResponse_Sse2(current_size_partitions_, H_, H2);
|
||||
break;
|
||||
+#endif
|
||||
case Aec3Optimization::kAvx2:
|
||||
aec3::ComputeFrequencyResponse_Avx2(current_size_partitions_, H_, H2);
|
||||
break;
|
||||
@@ -626,10 +630,12 @@ void AdaptiveFirFilter::AdaptAndUpdateSize(const RenderBuffer& render_buffer,
|
||||
// Adapt the filter.
|
||||
switch (optimization_) {
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if !defined(WAP_DISABLE_INLINE_SSE)
|
||||
case Aec3Optimization::kSse2:
|
||||
aec3::AdaptPartitions_Sse2(render_buffer, G, current_size_partitions_,
|
||||
&H_);
|
||||
break;
|
||||
+#endif
|
||||
case Aec3Optimization::kAvx2:
|
||||
aec3::AdaptPartitions_Avx2(render_buffer, G, current_size_partitions_,
|
||||
&H_);
|
||||
diff --git a/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc
|
||||
index 45b8813..920d51c 100644
|
||||
--- a/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc
|
||||
+++ b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc
|
||||
@@ -16,7 +16,7 @@
|
||||
#if defined(WEBRTC_HAS_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
@@ -54,7 +54,7 @@ void ErlComputer_NEON(
|
||||
}
|
||||
#endif
|
||||
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
// Computes and stores the echo return loss estimate of the filter, which is the
|
||||
// sum of the partition frequency responses.
|
||||
void ErlComputer_SSE2(
|
||||
@@ -82,9 +82,11 @@ void ComputeErl(const Aec3Optimization& optimization,
|
||||
// Update the frequency response and echo return loss for the filter.
|
||||
switch (optimization) {
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if !defined(WAP_DISABLE_INLINE_SSE)
|
||||
case Aec3Optimization::kSse2:
|
||||
aec3::ErlComputer_SSE2(H2, erl);
|
||||
break;
|
||||
+#endif
|
||||
case Aec3Optimization::kAvx2:
|
||||
aec3::ErlComputer_AVX2(H2, erl);
|
||||
break;
|
||||
diff --git a/webrtc/modules/audio_processing/aec3/fft_data.h b/webrtc/modules/audio_processing/aec3/fft_data.h
|
||||
index 9c25e78..892407d 100644
|
||||
--- a/webrtc/modules/audio_processing/aec3/fft_data.h
|
||||
+++ b/webrtc/modules/audio_processing/aec3/fft_data.h
|
||||
@@ -14,7 +14,7 @@
|
||||
// Defines WEBRTC_ARCH_X86_FAMILY, used below.
|
||||
#include "rtc_base/system/arch.h"
|
||||
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
@@ -49,6 +49,7 @@ struct FftData {
|
||||
RTC_DCHECK_EQ(kFftLengthBy2Plus1, power_spectrum.size());
|
||||
switch (optimization) {
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if !defined(WAP_DISABLE_INLINE_SSE)
|
||||
case Aec3Optimization::kSse2: {
|
||||
constexpr int kNumFourBinBands = kFftLengthBy2 / 4;
|
||||
constexpr int kLimit = kNumFourBinBands * 4;
|
||||
@@ -63,6 +64,7 @@ struct FftData {
|
||||
power_spectrum[kFftLengthBy2] = re[kFftLengthBy2] * re[kFftLengthBy2] +
|
||||
im[kFftLengthBy2] * im[kFftLengthBy2];
|
||||
} break;
|
||||
+#endif
|
||||
case Aec3Optimization::kAvx2:
|
||||
SpectrumAVX2(power_spectrum);
|
||||
break;
|
||||
diff --git a/webrtc/modules/audio_processing/aec3/matched_filter.cc b/webrtc/modules/audio_processing/aec3/matched_filter.cc
|
||||
index 59a3b46..86f365a 100644
|
||||
--- a/webrtc/modules/audio_processing/aec3/matched_filter.cc
|
||||
+++ b/webrtc/modules/audio_processing/aec3/matched_filter.cc
|
||||
@@ -15,7 +15,7 @@
|
||||
#if defined(WEBRTC_HAS_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
@@ -286,7 +286,7 @@ void MatchedFilterCore_NEON(size_t x_start_index,
|
||||
|
||||
#endif
|
||||
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
|
||||
void MatchedFilterCore_AccumulatedError_SSE2(
|
||||
size_t x_start_index,
|
||||
@@ -695,12 +695,14 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer,
|
||||
|
||||
switch (optimization_) {
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if !defined(WAP_DISABLE_INLINE_SSE)
|
||||
case Aec3Optimization::kSse2:
|
||||
aec3::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;
|
||||
+#endif
|
||||
case Aec3Optimization::kAvx2:
|
||||
aec3::MatchedFilterCore_AVX2(
|
||||
x_start_index, x2_sum_threshold, smoothing, render_buffer.buffer, y,
|
||||
diff --git a/webrtc/modules/audio_processing/aec3/vector_math.h b/webrtc/modules/audio_processing/aec3/vector_math.h
|
||||
index e4d1381..1506a44 100644
|
||||
--- a/webrtc/modules/audio_processing/aec3/vector_math.h
|
||||
+++ b/webrtc/modules/audio_processing/aec3/vector_math.h
|
||||
@@ -17,7 +17,7 @@
|
||||
#if defined(WEBRTC_HAS_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
@@ -43,7 +43,7 @@ class VectorMath {
|
||||
void SqrtAVX2(rtc::ArrayView<float> x);
|
||||
void Sqrt(rtc::ArrayView<float> x) {
|
||||
switch (optimization_) {
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
case Aec3Optimization::kSse2: {
|
||||
const int x_size = static_cast<int>(x.size());
|
||||
const int vector_limit = x_size >> 2;
|
||||
@@ -123,7 +123,7 @@ class VectorMath {
|
||||
RTC_DCHECK_EQ(z.size(), x.size());
|
||||
RTC_DCHECK_EQ(z.size(), y.size());
|
||||
switch (optimization_) {
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
case Aec3Optimization::kSse2: {
|
||||
const int x_size = static_cast<int>(x.size());
|
||||
const int vector_limit = x_size >> 2;
|
||||
@@ -174,6 +174,7 @@ class VectorMath {
|
||||
RTC_DCHECK_EQ(z.size(), x.size());
|
||||
switch (optimization_) {
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if !defined(WAP_DISABLE_INLINE_SSE)
|
||||
case Aec3Optimization::kSse2: {
|
||||
const int x_size = static_cast<int>(x.size());
|
||||
const int vector_limit = x_size >> 2;
|
||||
@@ -190,6 +191,7 @@ class VectorMath {
|
||||
z[j] += x[j];
|
||||
}
|
||||
} break;
|
||||
+#endif
|
||||
case Aec3Optimization::kAvx2:
|
||||
AccumulateAVX2(x, z);
|
||||
break;
|
||||
diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/vector_math.h b/webrtc/modules/audio_processing/agc2/rnn_vad/vector_math.h
|
||||
index 47f6811..f965086 100644
|
||||
--- a/webrtc/modules/audio_processing/agc2/rnn_vad/vector_math.h
|
||||
+++ b/webrtc/modules/audio_processing/agc2/rnn_vad/vector_math.h
|
||||
@@ -17,7 +17,7 @@
|
||||
#if defined(WEBRTC_HAS_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
+#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(WAP_DISABLE_INLINE_SSE)
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
@@ -47,6 +47,7 @@ class VectorMath {
|
||||
if (cpu_features_.avx2) {
|
||||
return DotProductAvx2(x, y);
|
||||
} else if (cpu_features_.sse2) {
|
||||
+#if !defined(WAP_DISABLE_INLINE_SSE)
|
||||
__m128 accumulator = _mm_setzero_ps();
|
||||
constexpr int kBlockSizeLog2 = 2;
|
||||
constexpr int kBlockSize = 1 << kBlockSizeLog2;
|
||||
@@ -72,6 +73,7 @@ class VectorMath {
|
||||
dot_product += x[i] * y[i];
|
||||
}
|
||||
return dot_product;
|
||||
+#endif
|
||||
}
|
||||
#elif defined(WEBRTC_HAS_NEON) && defined(WEBRTC_ARCH_ARM64)
|
||||
if (cpu_features_.neon) {
|
||||
diff --git a/webrtc/third_party/pffft/meson.build b/webrtc/third_party/pffft/meson.build
|
||||
index c1eb5c6..cf4c9c7 100644
|
||||
--- a/webrtc/third_party/pffft/meson.build
|
||||
+++ b/webrtc/third_party/pffft/meson.build
|
||||
@@ -4,7 +4,7 @@ pffft_sources = [
|
||||
|
||||
pffft_cflags = [ '-D_GNU_SOURCE' ]
|
||||
|
||||
-if (have_arm and not have_neon) or (have_mips and host_machine.endian() == 'little') or have_mips64
|
||||
+if not have_inline_sse or (have_arm and not have_neon) or (have_mips and host_machine.endian() == 'little') or have_mips64
|
||||
pffft_cflags += [ '-DPFFFT_SIMD_DISABLE' ]
|
||||
endif
|
||||
|
||||
--
|
||||
2.47.1
|
||||
|
68
patches/0001-Fix-up-XMM-intrinsics-usage-on-MSVC.patch
Normal file
68
patches/0001-Fix-up-XMM-intrinsics-usage-on-MSVC.patch
Normal file
@ -0,0 +1,68 @@
|
||||
From ad563b095cea13730ca95e77d50e352ea9e344a9 Mon Sep 17 00:00:00 2001
|
||||
From: Arun Raghavan <arun@asymptotic.io>
|
||||
Date: Fri, 15 Dec 2023 16:06:05 -0500
|
||||
Subject: [PATCH] Fix up XMM intrinsics usage on MSVC
|
||||
|
||||
Repplying 0a0050746bc20ef970b9f260d485e4367c7ba854 after M131 bump.
|
||||
---
|
||||
.../aec3/matched_filter_avx2.cc | 30 ++++++++++++-------
|
||||
1 file changed, 20 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/webrtc/modules/audio_processing/aec3/matched_filter_avx2.cc b/webrtc/modules/audio_processing/aec3/matched_filter_avx2.cc
|
||||
index 8c2ffcb..65a1b76 100644
|
||||
--- a/webrtc/modules/audio_processing/aec3/matched_filter_avx2.cc
|
||||
+++ b/webrtc/modules/audio_processing/aec3/matched_filter_avx2.cc
|
||||
@@ -13,6 +13,16 @@
|
||||
#include "modules/audio_processing/aec3/matched_filter.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
+#ifdef _MSC_VER
|
||||
+// Visual Studio
|
||||
+#define LOOKUP_M128(v, i) v.m128_f32[i]
|
||||
+#define LOOKUP_M256(v, i) v.m256_f32[i]
|
||||
+#else
|
||||
+// GCC/Clang
|
||||
+#define LOOKUP_M128(v, i) v[i]
|
||||
+#define LOOKUP_M256(v, i) v[i]
|
||||
+#endif
|
||||
+
|
||||
namespace webrtc {
|
||||
namespace aec3 {
|
||||
|
||||
@@ -81,14 +91,14 @@ void MatchedFilterCore_AccumulatedError_AVX2(
|
||||
s_inst_256_8 = _mm256_mul_ps(h_k_8, x_k_8);
|
||||
s_inst_hadd_256 = _mm256_hadd_ps(s_inst_256, s_inst_256_8);
|
||||
s_inst_hadd_256 = _mm256_hadd_ps(s_inst_hadd_256, s_inst_hadd_256);
|
||||
- s_acum += s_inst_hadd_256[0];
|
||||
- e_128[0] = s_acum - y[i];
|
||||
- s_acum += s_inst_hadd_256[4];
|
||||
- e_128[1] = s_acum - y[i];
|
||||
- s_acum += s_inst_hadd_256[1];
|
||||
- e_128[2] = s_acum - y[i];
|
||||
- s_acum += s_inst_hadd_256[5];
|
||||
- e_128[3] = s_acum - y[i];
|
||||
+ s_acum += LOOKUP_M256(s_inst_hadd_256, 0);
|
||||
+ LOOKUP_M128(e_128, 0) = s_acum - y[i];
|
||||
+ s_acum += LOOKUP_M256(s_inst_hadd_256,4);
|
||||
+ LOOKUP_M128(e_128, 1) = s_acum - y[i];
|
||||
+ s_acum += LOOKUP_M256(s_inst_hadd_256,1);
|
||||
+ LOOKUP_M128(e_128, 2) = s_acum - y[i];
|
||||
+ s_acum += LOOKUP_M256(s_inst_hadd_256,5);
|
||||
+ LOOKUP_M128(e_128, 3) = s_acum - y[i];
|
||||
|
||||
__m128 accumulated_error = _mm_load_ps(a_p);
|
||||
accumulated_error = _mm_fmadd_ps(e_128, e_128, accumulated_error);
|
||||
@@ -209,8 +219,8 @@ void MatchedFilterCore_AVX2(size_t x_start_index,
|
||||
x2_sum_256 = _mm256_add_ps(x2_sum_256, x2_sum_256_8);
|
||||
s_256 = _mm256_add_ps(s_256, s_256_8);
|
||||
__m128 sum = hsum_ab(x2_sum_256, s_256);
|
||||
- x2_sum += sum[0];
|
||||
- s += sum[1];
|
||||
+ x2_sum += LOOKUP_M128(sum, 0);
|
||||
+ s += LOOKUP_M128(sum, 1);
|
||||
|
||||
// Compute the matched filter error.
|
||||
float e = y[i] - s;
|
||||
--
|
||||
2.47.1
|
||||
|
@ -0,0 +1,45 @@
|
||||
From 4a17c682e9a173c27feec9e67fb8c4c36090b1a6 Mon Sep 17 00:00:00 2001
|
||||
From: Alper Nebi Yasak <alpernebiyasak@gmail.com>
|
||||
Date: Fri, 25 Oct 2024 01:53:16 +0300
|
||||
Subject: [PATCH] common_audio: Add MIPS_DSP_R1_LE guard for vector scaling ops
|
||||
|
||||
The MIPS-specific source for vector scaling operations fails to build on
|
||||
Debian's mips64el:
|
||||
|
||||
[97/303] Compiling C object webrtc/common_audio/libcommon_audio.a.p/signal_processing_vector_scaling_operations_mips.c.o
|
||||
FAILED: webrtc/common_audio/libcommon_audio.a.p/signal_processing_vector_scaling_operations_mips.c.o
|
||||
cc [...] webrtc/common_audio/libcommon_audio.a.p/signal_processing_vector_scaling_operations_mips.c.o.d -o webrtc/common_audio/libcommon_audio.a.p/signal_processing_vector_scaling_operations_mips.c.o -c ../webrtc/common_audio/signal_processing/vector_scaling_operations_mips.c
|
||||
/tmp/cc7UGPkY.s: Assembler messages:
|
||||
/tmp/cc7UGPkY.s:57: Error: opcode not supported on this processor: mips64r2 (mips64r2) `extrv_r.w $3,$ac0,$8'
|
||||
ninja: build stopped: subcommand failed.
|
||||
|
||||
The EXTRV_R.W instruction it uses is part of DSP extensions for this
|
||||
architecture. In signal_processing_library.h, this function's prototype
|
||||
is guarded with #if defined(MIPS_DSP_R1_LE). Guard the implementation
|
||||
like that as well to fix the error.
|
||||
|
||||
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
|
||||
---
|
||||
.../signal_processing/vector_scaling_operations_mips.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/webrtc/common_audio/signal_processing/vector_scaling_operations_mips.c b/webrtc/common_audio/signal_processing/vector_scaling_operations_mips.c
|
||||
index ba2d26d..08ca293 100644
|
||||
--- a/webrtc/common_audio/signal_processing/vector_scaling_operations_mips.c
|
||||
+++ b/webrtc/common_audio/signal_processing/vector_scaling_operations_mips.c
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "common_audio/signal_processing/include/signal_processing_library.h"
|
||||
|
||||
+#if defined(MIPS_DSP_R1_LE)
|
||||
int WebRtcSpl_ScaleAndAddVectorsWithRound_mips(const int16_t* in_vector1,
|
||||
int16_t in_vector1_scale,
|
||||
const int16_t* in_vector2,
|
||||
@@ -55,3 +56,4 @@ int WebRtcSpl_ScaleAndAddVectorsWithRound_mips(const int16_t* in_vector1,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+#endif
|
||||
--
|
||||
2.47.1
|
||||
|
108
subprojects/abseil-cpp.wrap
Normal file
108
subprojects/abseil-cpp.wrap
Normal file
@ -0,0 +1,108 @@
|
||||
[wrap-file]
|
||||
directory = abseil-cpp-20240722.0
|
||||
source_url = https://github.com/abseil/abseil-cpp/releases/download/20240722.0/abseil-cpp-20240722.0.tar.gz
|
||||
source_filename = abseil-cpp-20240722.0.tar.gz
|
||||
source_hash = f50e5ac311a81382da7fa75b97310e4b9006474f9560ac46f54a9967f07d4ae3
|
||||
patch_filename = abseil-cpp_20240722.0-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/abseil-cpp_20240722.0-1/get_patch
|
||||
patch_hash = 692bbbc39cacaba4dc4b0c8b2fbbe32736c9cde6377acfa0d52088797af14ded
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/abseil-cpp_20240722.0-1/abseil-cpp-20240722.0.tar.gz
|
||||
wrapdb_version = 20240722.0-1
|
||||
|
||||
[provide]
|
||||
absl_base = absl_base_dep
|
||||
absl_container = absl_container_dep
|
||||
absl_debugging = absl_debugging_dep
|
||||
absl_log = absl_log_dep
|
||||
absl_flags = absl_flags_dep
|
||||
absl_hash = absl_hash_dep
|
||||
absl_crc = absl_crc_dep
|
||||
absl_numeric = absl_numeric_dep
|
||||
absl_profiling = absl_profiling_dep
|
||||
absl_random = absl_random_dep
|
||||
absl_status = absl_status_dep
|
||||
absl_strings = absl_strings_dep
|
||||
absl_synchronization = absl_synchronization_dep
|
||||
absl_time = absl_time_dep
|
||||
absl_types = absl_types_dep
|
||||
absl_algorithm_container = absl_base_dep
|
||||
absl_any_invocable = absl_base_dep
|
||||
absl_bad_any_cast_impl = absl_types_dep
|
||||
absl_bad_optional_access = absl_types_dep
|
||||
absl_bad_variant_access = absl_types_dep
|
||||
absl_bind_front = absl_base_dep
|
||||
absl_city = absl_hash_dep
|
||||
absl_civil_time = absl_time_dep
|
||||
absl_cleanup = absl_base_dep
|
||||
absl_cord = absl_strings_dep
|
||||
absl_cord_internal = absl_strings_dep
|
||||
absl_cordz_functions = absl_strings_dep
|
||||
absl_cordz_handle = absl_strings_dep
|
||||
absl_cordz_info = absl_strings_dep
|
||||
absl_cordz_sample_token = absl_strings_dep
|
||||
absl_core_headers = absl_base_dep
|
||||
absl_crc32c = absl_crc_dep
|
||||
absl_debugging_internal = absl_debugging_dep
|
||||
absl_demangle_internal = absl_debugging_dep
|
||||
absl_die_if_null = absl_log_dep
|
||||
absl_examine_stack = absl_debugging_dep
|
||||
absl_exponential_biased = absl_profiling_dep
|
||||
absl_failure_signal_handler = absl_debugging_dep
|
||||
absl_flags_commandlineflag = absl_flags_dep
|
||||
absl_flags_commandlineflag_internal = absl_flags_dep
|
||||
absl_flags_config = absl_flags_dep
|
||||
absl_flags_internal = absl_flags_dep
|
||||
absl_flags_marshalling = absl_flags_dep
|
||||
absl_flags_parse = absl_flags_dep
|
||||
absl_flags_private_handle_accessor = absl_flags_dep
|
||||
absl_flags_program_name = absl_flags_dep
|
||||
absl_flags_reflection = absl_flags_dep
|
||||
absl_flags_usage = absl_flags_dep
|
||||
absl_flags_usage_internal = absl_flags_dep
|
||||
absl_flat_hash_map = absl_container_dep
|
||||
absl_flat_hash_set = absl_container_dep
|
||||
absl_function_ref = absl_base_dep
|
||||
absl_graphcycles_internal = absl_synchronization_dep
|
||||
absl_hashtablez_sampler = absl_container_dep
|
||||
absl_inlined_vector = absl_container_dep
|
||||
absl_int128 = absl_numeric_dep
|
||||
absl_leak_check = absl_debugging_dep
|
||||
absl_log_initialize = absl_log_dep
|
||||
absl_log_internal_check_op = absl_log_dep
|
||||
absl_log_internal_message = absl_log_dep
|
||||
absl_log_severity = absl_base_dep
|
||||
absl_low_level_hash = absl_hash_dep
|
||||
absl_memory = absl_base_dep
|
||||
absl_optional = absl_types_dep
|
||||
absl_periodic_sampler = absl_profiling_dep
|
||||
absl_random_bit_gen_ref = absl_random_dep
|
||||
absl_random_distributions = absl_random_dep
|
||||
absl_random_internal_distribution_test_util = absl_random_dep
|
||||
absl_random_internal_platform = absl_random_dep
|
||||
absl_random_internal_pool_urbg = absl_random_dep
|
||||
absl_random_internal_randen = absl_random_dep
|
||||
absl_random_internal_randen_hwaes = absl_random_dep
|
||||
absl_random_internal_randen_hwaes_impl = absl_random_dep
|
||||
absl_random_internal_randen_slow = absl_random_dep
|
||||
absl_random_internal_seed_material = absl_random_dep
|
||||
absl_random_random = absl_random_dep
|
||||
absl_random_seed_gen_exception = absl_random_dep
|
||||
absl_random_seed_sequences = absl_random_dep
|
||||
absl_raw_hash_set = absl_container_dep
|
||||
absl_raw_logging_internal = absl_base_dep
|
||||
absl_scoped_set_env = absl_base_dep
|
||||
absl_span = absl_types_dep
|
||||
absl_spinlock_wait = absl_base_dep
|
||||
absl_stacktrace = absl_debugging_dep
|
||||
absl_statusor = absl_status_dep
|
||||
absl_str_format = absl_strings_dep
|
||||
absl_str_format_internal = absl_strings_dep
|
||||
absl_strerror = absl_base_dep
|
||||
absl_string_view = absl_strings_dep
|
||||
absl_strings_internal = absl_strings_dep
|
||||
absl_symbolize = absl_debugging_dep
|
||||
absl_throw_delegate = absl_base_dep
|
||||
absl_time_zone = absl_time_dep
|
||||
absl_type_traits = absl_base_dep
|
||||
absl_utility = absl_base_dep
|
||||
absl_variant = absl_types_dep
|
279
webrtc/BUILD.gn
279
webrtc/BUILD.gn
@ -12,6 +12,15 @@
|
||||
# you add a new build file, there must be some path of dependencies from this
|
||||
# file to your new one or GN won't know about it.
|
||||
|
||||
# Use of visibility = clauses:
|
||||
# The default visibility for all rtc_ targets is equivalent to "//*", or
|
||||
# "all targets in webrtc can depend on this, nothing outside can".
|
||||
#
|
||||
# When overriding, the choices are:
|
||||
# - visibility = [ "*" ] - public. Stuff outside webrtc can use this.
|
||||
# - visibility = [ ":*" ] - directory private.
|
||||
# As a general guideline, only targets in api/ should have public visibility.
|
||||
|
||||
import("//build/config/linux/pkg_config.gni")
|
||||
import("//build/config/sanitizers/sanitizers.gni")
|
||||
import("webrtc.gni")
|
||||
@ -21,6 +30,7 @@ if (rtc_enable_protobuf) {
|
||||
if (is_android) {
|
||||
import("//build/config/android/config.gni")
|
||||
import("//build/config/android/rules.gni")
|
||||
import("//third_party/jni_zero/jni_zero.gni")
|
||||
}
|
||||
|
||||
if (!build_with_chromium) {
|
||||
@ -38,7 +48,6 @@ if (!build_with_chromium) {
|
||||
if (rtc_include_tests) {
|
||||
deps += [
|
||||
":rtc_unittests",
|
||||
":slow_tests",
|
||||
":video_engine_tests",
|
||||
":voip_unittests",
|
||||
":webrtc_nonparallel_tests",
|
||||
@ -54,9 +63,14 @@ if (!build_with_chromium) {
|
||||
"modules/remote_bitrate_estimator:rtp_to_text",
|
||||
"modules/rtp_rtcp:test_packet_masks_metrics",
|
||||
"modules/video_capture:video_capture_internal_impl",
|
||||
"modules/video_coding:video_codec_perf_tests",
|
||||
"net/dcsctp:dcsctp_unittests",
|
||||
"pc:peerconnection_unittests",
|
||||
"pc:rtc_pc_unittests",
|
||||
"pc:slow_peer_connection_unittests",
|
||||
"pc:svc_tests",
|
||||
"rtc_tools:rtp_generator",
|
||||
"rtc_tools:video_encoder",
|
||||
"rtc_tools:video_replay",
|
||||
"stats:rtc_stats_unittests",
|
||||
"system_wrappers:system_wrappers_unittests",
|
||||
@ -65,12 +79,22 @@ if (!build_with_chromium) {
|
||||
"video:sv_loopback",
|
||||
"video:video_loopback",
|
||||
]
|
||||
if (use_libfuzzer) {
|
||||
deps += [ "test/fuzzers" ]
|
||||
}
|
||||
if (!is_asan) {
|
||||
# Do not build :webrtc_lib_link_test because lld complains on some OS
|
||||
# (e.g. when target_os = "mac") when is_asan=true. For more details,
|
||||
# see bugs.webrtc.org/11027#c5.
|
||||
deps += [ ":webrtc_lib_link_test" ]
|
||||
}
|
||||
if (is_ios) {
|
||||
deps += [
|
||||
"examples:apprtcmobile_tests",
|
||||
"sdk:sdk_framework_unittests",
|
||||
"sdk:sdk_unittests",
|
||||
]
|
||||
}
|
||||
if (is_android) {
|
||||
deps += [
|
||||
"examples:android_examples_junit_tests",
|
||||
@ -82,11 +106,16 @@ if (!build_with_chromium) {
|
||||
}
|
||||
if (rtc_enable_protobuf) {
|
||||
deps += [
|
||||
"audio:low_bandwidth_audio_test",
|
||||
"logging:rtc_event_log_rtp_dump",
|
||||
"tools_webrtc/perf:webrtc_dashboard_upload",
|
||||
]
|
||||
}
|
||||
if ((is_linux || is_chromeos) && rtc_use_pipewire) {
|
||||
deps += [ "modules/desktop_capture:shared_screencast_stream_test" ]
|
||||
}
|
||||
}
|
||||
if (target_os == "android") {
|
||||
deps += [ "tools_webrtc:binary_version_check" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,12 +142,19 @@ config("common_inherited_config") {
|
||||
cflags = []
|
||||
ldflags = []
|
||||
|
||||
if (rtc_enable_symbol_export || is_component_build) {
|
||||
defines = [ "WEBRTC_ENABLE_SYMBOL_EXPORT" ]
|
||||
if (rtc_objc_prefix != "") {
|
||||
defines += [ "RTC_OBJC_TYPE_PREFIX=${rtc_objc_prefix}" ]
|
||||
}
|
||||
|
||||
if (build_with_mozilla) {
|
||||
defines += [ "WEBRTC_MOZILLA_BUILD" ]
|
||||
if (rtc_dlog_always_on) {
|
||||
defines += [ "DLOG_ALWAYS_ON" ]
|
||||
}
|
||||
|
||||
if (rtc_enable_symbol_export || is_component_build) {
|
||||
defines += [ "WEBRTC_ENABLE_SYMBOL_EXPORT" ]
|
||||
}
|
||||
if (rtc_enable_objc_symbol_export) {
|
||||
defines += [ "WEBRTC_ENABLE_OBJC_SYMBOL_EXPORT" ]
|
||||
}
|
||||
|
||||
if (!rtc_builtin_ssl_root_certificates) {
|
||||
@ -133,6 +169,11 @@ config("common_inherited_config") {
|
||||
defines += [ "WEBRTC_ENABLE_AVX2" ]
|
||||
}
|
||||
|
||||
if (rtc_enable_win_wgc) {
|
||||
defines += [ "RTC_ENABLE_WIN_WGC" ]
|
||||
}
|
||||
|
||||
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
|
||||
# value for the current handler in an static variable, so that subsequent
|
||||
@ -144,6 +185,7 @@ config("common_inherited_config") {
|
||||
} else {
|
||||
defines += [ "WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=0" ]
|
||||
}
|
||||
}
|
||||
if (build_with_chromium) {
|
||||
defines += [ "WEBRTC_CHROMIUM_BUILD" ]
|
||||
include_dirs = [
|
||||
@ -210,14 +252,6 @@ config("common_inherited_config") {
|
||||
}
|
||||
}
|
||||
|
||||
# TODO(bugs.webrtc.org/9693): Remove the possibility to suppress this warning
|
||||
# as soon as WebRTC compiles without it.
|
||||
config("no_exit_time_destructors") {
|
||||
if (is_clang) {
|
||||
cflags = [ "-Wno-exit-time-destructors" ]
|
||||
}
|
||||
}
|
||||
|
||||
# TODO(bugs.webrtc.org/9693): Remove the possibility to suppress this warning
|
||||
# as soon as WebRTC compiles without it.
|
||||
config("no_global_constructors") {
|
||||
@ -236,6 +270,33 @@ 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 = []
|
||||
@ -249,6 +310,18 @@ config("common_config") {
|
||||
defines += [ "WEBRTC_ENABLE_PROTOBUF=0" ]
|
||||
}
|
||||
|
||||
if (rtc_strict_field_trials == "") {
|
||||
defines += [ "WEBRTC_STRICT_FIELD_TRIALS=0" ]
|
||||
} else if (rtc_strict_field_trials == "dcheck") {
|
||||
defines += [ "WEBRTC_STRICT_FIELD_TRIALS=1" ]
|
||||
} else if (rtc_strict_field_trials == "warn") {
|
||||
defines += [ "WEBRTC_STRICT_FIELD_TRIALS=2" ]
|
||||
} else {
|
||||
assert(false,
|
||||
"Unsupported value for rtc_strict_field_trials: " +
|
||||
"$rtc_strict_field_trials")
|
||||
}
|
||||
|
||||
if (rtc_include_internal_audio_device) {
|
||||
defines += [ "WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE" ]
|
||||
}
|
||||
@ -257,8 +330,16 @@ config("common_config") {
|
||||
defines += [ "RTC_ENABLE_VP9" ]
|
||||
}
|
||||
|
||||
if (rtc_use_h265) {
|
||||
defines += [ "RTC_ENABLE_H265" ]
|
||||
}
|
||||
|
||||
if (rtc_include_dav1d_in_internal_decoder_factory) {
|
||||
defines += [ "RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY" ]
|
||||
}
|
||||
|
||||
if (rtc_enable_sctp) {
|
||||
defines += [ "HAVE_SCTP" ]
|
||||
defines += [ "WEBRTC_HAVE_SCTP" ]
|
||||
}
|
||||
|
||||
if (rtc_enable_external_auth) {
|
||||
@ -273,6 +354,10 @@ config("common_config") {
|
||||
defines += [ "WEBRTC_ABSL_MUTEX" ]
|
||||
}
|
||||
|
||||
if (rtc_enable_libevent) {
|
||||
defines += [ "WEBRTC_ENABLE_LIBEVENT" ]
|
||||
}
|
||||
|
||||
if (rtc_disable_logging) {
|
||||
defines += [ "RTC_DISABLE_LOGGING" ]
|
||||
}
|
||||
@ -285,15 +370,20 @@ config("common_config") {
|
||||
defines += [ "RTC_DISABLE_METRICS" ]
|
||||
}
|
||||
|
||||
if (rtc_exclude_transient_suppressor) {
|
||||
defines += [ "WEBRTC_EXCLUDE_TRANSIENT_SUPPRESSOR" ]
|
||||
}
|
||||
|
||||
if (rtc_exclude_audio_processing_module) {
|
||||
defines += [ "WEBRTC_EXCLUDE_AUDIO_PROCESSING_MODULE" ]
|
||||
}
|
||||
|
||||
cflags = []
|
||||
if (is_clang) {
|
||||
cflags += [
|
||||
# TODO(webrtc:13219): Fix -Wshadow instances and enable.
|
||||
"-Wno-shadow",
|
||||
|
||||
# See https://reviews.llvm.org/D56731 for details about this
|
||||
# warning.
|
||||
"-Wctad-maybe-unsupported",
|
||||
]
|
||||
}
|
||||
|
||||
if (build_with_chromium) {
|
||||
defines += [
|
||||
@ -331,20 +421,9 @@ config("common_config") {
|
||||
if (is_clang) {
|
||||
cflags += [
|
||||
"-Wc++11-narrowing",
|
||||
"-Wimplicit-fallthrough",
|
||||
"-Wthread-safety",
|
||||
"-Winconsistent-missing-override",
|
||||
"-Wundef",
|
||||
"-Wunused-lambda-capture",
|
||||
]
|
||||
|
||||
# use_xcode_clang only refers to the iOS toolchain, host binaries use
|
||||
# chromium's clang always.
|
||||
if (!is_nacl &&
|
||||
(!use_xcode_clang || current_toolchain == host_toolchain)) {
|
||||
# Flags NaCl (Clang 3.7) and Xcode 7.3 (Clang clang-703.0.31) do not
|
||||
# recognize.
|
||||
cflags += [ "-Wunused-lambda-capture" ]
|
||||
}
|
||||
}
|
||||
|
||||
if (is_win && !is_clang) {
|
||||
@ -404,7 +483,7 @@ config("common_config") {
|
||||
]
|
||||
}
|
||||
|
||||
if (use_fuzzing_engine && optimize_for_fuzzing) {
|
||||
if (use_fuzzing_engine) {
|
||||
# Used in Chromium's overrides to disable logging
|
||||
defines += [ "WEBRTC_UNSAFE_FUZZER_MODE" ]
|
||||
}
|
||||
@ -415,13 +494,25 @@ config("common_config") {
|
||||
"/U_UNICODE",
|
||||
]
|
||||
}
|
||||
|
||||
if (rtc_use_perfetto) {
|
||||
defines += [ "RTC_USE_PERFETTO" ]
|
||||
}
|
||||
}
|
||||
|
||||
config("common_objc") {
|
||||
frameworks = [ "Foundation.framework" ]
|
||||
}
|
||||
|
||||
if (rtc_use_metal_rendering) {
|
||||
defines = [ "RTC_SUPPORTS_METAL" ]
|
||||
if (!rtc_build_ssl) {
|
||||
config("external_ssl_library") {
|
||||
if (rtc_ssl_root != "") {
|
||||
include_dirs = [ rtc_ssl_root ]
|
||||
}
|
||||
libs = [
|
||||
"crypto",
|
||||
"ssl",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,13 +532,26 @@ if (!build_with_chromium) {
|
||||
|
||||
deps = [
|
||||
"api:create_peerconnection_factory",
|
||||
"api:enable_media",
|
||||
"api:libjingle_peerconnection_api",
|
||||
"api:rtc_error",
|
||||
"api:transport_api",
|
||||
"api/audio_codecs:opus_audio_decoder_factory",
|
||||
"api/crypto",
|
||||
"api/rtc_event_log:rtc_event_log_factory",
|
||||
"api/task_queue",
|
||||
"api/task_queue:default_task_queue_factory",
|
||||
"api/test/metrics",
|
||||
"api/video_codecs:video_decoder_factory_template",
|
||||
"api/video_codecs:video_decoder_factory_template_dav1d_adapter",
|
||||
"api/video_codecs:video_decoder_factory_template_libvpx_vp8_adapter",
|
||||
"api/video_codecs:video_decoder_factory_template_libvpx_vp9_adapter",
|
||||
"api/video_codecs:video_decoder_factory_template_open_h264_adapter",
|
||||
"api/video_codecs:video_encoder_factory_template",
|
||||
"api/video_codecs:video_encoder_factory_template_libaom_av1_adapter",
|
||||
"api/video_codecs:video_encoder_factory_template_libvpx_vp8_adapter",
|
||||
"api/video_codecs:video_encoder_factory_template_libvpx_vp9_adapter",
|
||||
"api/video_codecs:video_encoder_factory_template_open_h264_adapter",
|
||||
"audio",
|
||||
"call",
|
||||
"common_audio",
|
||||
@ -458,10 +562,7 @@ if (!build_with_chromium) {
|
||||
"modules/video_capture:video_capture_internal_impl",
|
||||
"p2p:rtc_p2p",
|
||||
"pc:libjingle_peerconnection",
|
||||
"pc:peerconnection",
|
||||
"pc:rtc_pc",
|
||||
"pc:rtc_pc_base",
|
||||
"rtc_base",
|
||||
"sdk",
|
||||
"video",
|
||||
]
|
||||
@ -473,13 +574,6 @@ if (!build_with_chromium) {
|
||||
]
|
||||
}
|
||||
|
||||
if (rtc_include_builtin_video_codecs) {
|
||||
deps += [
|
||||
"api/video_codecs:builtin_video_decoder_factory",
|
||||
"api/video_codecs:builtin_video_encoder_factory",
|
||||
]
|
||||
}
|
||||
|
||||
if (build_with_mozilla) {
|
||||
deps += [
|
||||
"api/video:video_frame",
|
||||
@ -504,6 +598,10 @@ if (!build_with_chromium) {
|
||||
rtc_executable("webrtc_lib_link_test") {
|
||||
testonly = true
|
||||
|
||||
# This target is used for checking to link, so do not check dependencies
|
||||
# on gn check.
|
||||
check_includes = false # no-presubmit-check TODO(bugs.webrtc.org/12785)
|
||||
|
||||
sources = [ "webrtc_lib_link_test.cc" ]
|
||||
deps = [
|
||||
# NOTE: Don't add deps here. If this test fails to link, it means you
|
||||
@ -523,7 +621,17 @@ if (use_libfuzzer || use_afl) {
|
||||
}
|
||||
}
|
||||
|
||||
if (rtc_include_tests) {
|
||||
if (rtc_include_tests && !build_with_chromium) {
|
||||
rtc_unittests_resources = [ "resources/reference_video_640x360_30fps.y4m" ]
|
||||
|
||||
if (is_ios) {
|
||||
bundle_data("rtc_unittests_bundle_data") {
|
||||
testonly = true
|
||||
sources = rtc_unittests_resources
|
||||
outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
|
||||
}
|
||||
}
|
||||
|
||||
rtc_test("rtc_unittests") {
|
||||
testonly = true
|
||||
|
||||
@ -533,13 +641,20 @@ if (rtc_include_tests) {
|
||||
"api/audio/test:audio_api_unittests",
|
||||
"api/audio_codecs/test:audio_codecs_api_unittests",
|
||||
"api/numerics:numerics_unittests",
|
||||
"api/task_queue:pending_task_safety_flag_unittests",
|
||||
"api/test/metrics:metrics_unittests",
|
||||
"api/transport:stun_unittest",
|
||||
"api/video/test:rtc_api_video_unittests",
|
||||
"api/video_codecs:libaom_av1_encoder_factory_test",
|
||||
"api/video_codecs:simple_encoder_wrapper_unittests",
|
||||
"api/video_codecs/test:video_codecs_api_unittests",
|
||||
"api/voip:compile_all_headers",
|
||||
"call:fake_network_pipe_unittests",
|
||||
"p2p:libstunprober_unittests",
|
||||
"p2p:rtc_p2p_unittests",
|
||||
"rtc_base:robo_caller_unittests",
|
||||
"rtc_base:async_dns_resolver_unittests",
|
||||
"rtc_base:async_packet_socket_unittest",
|
||||
"rtc_base:callback_list_unittests",
|
||||
"rtc_base:rtc_base_approved_unittests",
|
||||
"rtc_base:rtc_base_unittests",
|
||||
"rtc_base:rtc_json_unittests",
|
||||
@ -547,20 +662,30 @@ if (rtc_include_tests) {
|
||||
"rtc_base:rtc_operations_chain_unittests",
|
||||
"rtc_base:rtc_task_queue_unittests",
|
||||
"rtc_base:sigslot_unittest",
|
||||
"rtc_base:task_queue_stdlib_unittest",
|
||||
"rtc_base:untyped_function_unittest",
|
||||
"rtc_base:weak_ptr_unittests",
|
||||
"rtc_base/experiments:experiments_unittests",
|
||||
"rtc_base/synchronization:sequence_checker_unittests",
|
||||
"rtc_base/task_utils:pending_task_safety_flag_unittests",
|
||||
"rtc_base/task_utils:to_queued_task_unittests",
|
||||
"rtc_base/system:file_wrapper_unittests",
|
||||
"rtc_base/task_utils:repeating_task_unittests",
|
||||
"rtc_base/units:units_unittests",
|
||||
"sdk:sdk_tests",
|
||||
"test:rtp_test_utils",
|
||||
"test:test_main",
|
||||
"test/network:network_emulation_unittests",
|
||||
]
|
||||
|
||||
data = rtc_unittests_resources
|
||||
|
||||
if (rtc_enable_protobuf) {
|
||||
deps += [ "logging:rtc_event_log_tests" ]
|
||||
deps += [
|
||||
"api/test/network_emulation:network_config_schedule_proto",
|
||||
"logging:rtc_event_log_tests",
|
||||
]
|
||||
}
|
||||
|
||||
if (is_ios) {
|
||||
deps += [ ":rtc_unittests_bundle_data" ]
|
||||
}
|
||||
|
||||
if (is_android) {
|
||||
@ -574,12 +699,9 @@ if (rtc_include_tests) {
|
||||
]
|
||||
shard_timeout = 900
|
||||
}
|
||||
|
||||
if (is_ios || is_mac) {
|
||||
deps += [ "sdk:rtc_unittests_objc" ]
|
||||
}
|
||||
}
|
||||
|
||||
if (rtc_enable_google_benchmarks) {
|
||||
rtc_test("benchmarks") {
|
||||
testonly = true
|
||||
deps = [
|
||||
@ -587,16 +709,6 @@ if (rtc_include_tests) {
|
||||
"test:benchmark_main",
|
||||
]
|
||||
}
|
||||
|
||||
# This runs tests that must run in real time and therefore can take some
|
||||
# time to execute. They are in a separate executable to avoid making the
|
||||
# regular unittest suite too slow to run frequently.
|
||||
rtc_test("slow_tests") {
|
||||
testonly = true
|
||||
deps = [
|
||||
"rtc_base/task_utils:repeating_task_unittests",
|
||||
"test:test_main",
|
||||
]
|
||||
}
|
||||
|
||||
# TODO(pbos): Rename test suite, this is no longer "just" for video targets.
|
||||
@ -630,7 +742,12 @@ if (rtc_include_tests) {
|
||||
]
|
||||
data = video_engine_tests_resources
|
||||
if (is_android) {
|
||||
deps += [ "//testing/android/native_test:native_test_native_code" ]
|
||||
use_default_launcher = false
|
||||
deps += [
|
||||
"//build/android/gtest_apk:native_test_instrumentation_test_runner_java",
|
||||
"//testing/android/native_test:native_test_java",
|
||||
"//testing/android/native_test:native_test_support",
|
||||
]
|
||||
shard_timeout = 900
|
||||
}
|
||||
if (is_ios) {
|
||||
@ -663,7 +780,6 @@ if (rtc_include_tests) {
|
||||
rtc_test("webrtc_perf_tests") {
|
||||
testonly = true
|
||||
deps = [
|
||||
"audio:audio_perf_tests",
|
||||
"call:call_perf_tests",
|
||||
"modules/audio_coding:audio_coding_perf_tests",
|
||||
"modules/audio_processing:audio_processing_perf_tests",
|
||||
@ -675,7 +791,12 @@ if (rtc_include_tests) {
|
||||
|
||||
data = webrtc_perf_tests_resources
|
||||
if (is_android) {
|
||||
deps += [ "//testing/android/native_test:native_test_native_code" ]
|
||||
use_default_launcher = false
|
||||
deps += [
|
||||
"//build/android/gtest_apk:native_test_instrumentation_test_runner_java",
|
||||
"//testing/android/native_test:native_test_java",
|
||||
"//testing/android/native_test:native_test_support",
|
||||
]
|
||||
shard_timeout = 4500
|
||||
}
|
||||
if (is_ios) {
|
||||
@ -695,6 +816,7 @@ if (rtc_include_tests) {
|
||||
rtc_test("voip_unittests") {
|
||||
testonly = true
|
||||
deps = [
|
||||
"api/voip:compile_all_headers",
|
||||
"api/voip:voip_engine_factory_unittests",
|
||||
"audio/voip/test:audio_channel_unittests",
|
||||
"audio/voip/test:audio_egress_unittests",
|
||||
@ -705,6 +827,23 @@ if (rtc_include_tests) {
|
||||
}
|
||||
}
|
||||
|
||||
# Build target for standalone dcsctp
|
||||
rtc_static_library("dcsctp") {
|
||||
# Only the root target should depend on this.
|
||||
visibility = [ "//:default" ]
|
||||
sources = []
|
||||
complete_static_lib = true
|
||||
suppressed_configs += [ "//build/config/compiler:thin_archive" ]
|
||||
defines = []
|
||||
deps = [
|
||||
"net/dcsctp/public:factory",
|
||||
"net/dcsctp/public:socket",
|
||||
"net/dcsctp/public:types",
|
||||
"net/dcsctp/socket:dcsctp_socket",
|
||||
"net/dcsctp/timer:task_queue_timeout",
|
||||
]
|
||||
}
|
||||
|
||||
# ---- Poisons ----
|
||||
#
|
||||
# Here is one empty dummy target for each poison type (needed because
|
||||
@ -717,10 +856,10 @@ if (rtc_include_tests) {
|
||||
group("poison_audio_codecs") {
|
||||
}
|
||||
|
||||
group("poison_default_task_queue") {
|
||||
group("poison_default_echo_detector") {
|
||||
}
|
||||
|
||||
group("poison_rtc_json") {
|
||||
group("poison_environment_construction") {
|
||||
}
|
||||
|
||||
group("poison_software_video_codecs") {
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
@ -83,7 +85,7 @@ namespace rtc {
|
||||
// a pointer if fix-sized) and trivially copyable, so it's probably cheaper to
|
||||
// pass it by value than by const reference.
|
||||
|
||||
namespace impl {
|
||||
namespace array_view_internal {
|
||||
|
||||
// Magic constant for indicating that the size of an ArrayView is variable
|
||||
// instead of fixed.
|
||||
@ -124,7 +126,7 @@ class ArrayViewBase<T, 0> {
|
||||
|
||||
// Specialized base class for ArrayViews of variable size.
|
||||
template <typename T>
|
||||
class ArrayViewBase<T, impl::kArrayViewVarSize> {
|
||||
class ArrayViewBase<T, array_view_internal::kArrayViewVarSize> {
|
||||
public:
|
||||
ArrayViewBase(T* data, size_t size)
|
||||
: data_(size == 0 ? nullptr : data), size_(size) {}
|
||||
@ -141,18 +143,23 @@ class ArrayViewBase<T, impl::kArrayViewVarSize> {
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
} // namespace array_view_internal
|
||||
|
||||
template <typename T, std::ptrdiff_t Size = impl::kArrayViewVarSize>
|
||||
class ArrayView final : public impl::ArrayViewBase<T, Size> {
|
||||
template <typename T,
|
||||
std::ptrdiff_t Size = array_view_internal::kArrayViewVarSize>
|
||||
class ArrayView final : public array_view_internal::ArrayViewBase<T, Size> {
|
||||
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 <typename U>
|
||||
ArrayView(U* data, size_t size)
|
||||
: impl::ArrayViewBase<T, Size>::ArrayViewBase(data, size) {
|
||||
: array_view_internal::ArrayViewBase<T, Size>::ArrayViewBase(data, size) {
|
||||
RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data());
|
||||
RTC_DCHECK_EQ(size, this->size());
|
||||
RTC_DCHECK_EQ(!this->data(),
|
||||
@ -166,7 +173,8 @@ class ArrayView final : public impl::ArrayViewBase<T, Size> {
|
||||
: ArrayView() {}
|
||||
ArrayView(std::nullptr_t, size_t size)
|
||||
: ArrayView(static_cast<T*>(nullptr), size) {
|
||||
static_assert(Size == 0 || Size == impl::kArrayViewVarSize, "");
|
||||
static_assert(Size == 0 || Size == array_view_internal::kArrayViewVarSize,
|
||||
"");
|
||||
RTC_DCHECK_EQ(0, size);
|
||||
}
|
||||
|
||||
@ -174,7 +182,7 @@ class ArrayView final : public impl::ArrayViewBase<T, Size> {
|
||||
template <typename U, size_t N>
|
||||
ArrayView(U (&array)[N]) // NOLINT
|
||||
: ArrayView(array, N) {
|
||||
static_assert(Size == N || Size == impl::kArrayViewVarSize,
|
||||
static_assert(Size == N || Size == array_view_internal::kArrayViewVarSize,
|
||||
"Array size must match ArrayView size");
|
||||
}
|
||||
|
||||
@ -207,7 +215,7 @@ class ArrayView final : public impl::ArrayViewBase<T, Size> {
|
||||
// N> when M != N.
|
||||
template <
|
||||
typename U,
|
||||
typename std::enable_if<Size != impl::kArrayViewVarSize &&
|
||||
typename std::enable_if<Size != array_view_internal::kArrayViewVarSize &&
|
||||
HasDataAndSize<U, T>::value>::type* = nullptr>
|
||||
ArrayView(U& u) // NOLINT
|
||||
: ArrayView(u.data(), u.size()) {
|
||||
@ -215,7 +223,7 @@ class ArrayView final : public impl::ArrayViewBase<T, Size> {
|
||||
}
|
||||
template <
|
||||
typename U,
|
||||
typename std::enable_if<Size != impl::kArrayViewVarSize &&
|
||||
typename std::enable_if<Size != array_view_internal::kArrayViewVarSize &&
|
||||
HasDataAndSize<U, T>::value>::type* = nullptr>
|
||||
ArrayView(const U& u) // NOLINT(runtime/explicit)
|
||||
: ArrayView(u.data(), u.size()) {
|
||||
@ -235,13 +243,13 @@ class ArrayView final : public impl::ArrayViewBase<T, Size> {
|
||||
// const rtc::Buffer to ArrayView<const uint8_t>.
|
||||
template <
|
||||
typename U,
|
||||
typename std::enable_if<Size == impl::kArrayViewVarSize &&
|
||||
typename std::enable_if<Size == array_view_internal::kArrayViewVarSize &&
|
||||
HasDataAndSize<U, T>::value>::type* = nullptr>
|
||||
ArrayView(U& u) // NOLINT
|
||||
: ArrayView(u.data(), u.size()) {}
|
||||
template <
|
||||
typename U,
|
||||
typename std::enable_if<Size == impl::kArrayViewVarSize &&
|
||||
typename std::enable_if<Size == array_view_internal::kArrayViewVarSize &&
|
||||
HasDataAndSize<U, T>::value>::type* = nullptr>
|
||||
ArrayView(const U& u) // NOLINT(runtime/explicit)
|
||||
: ArrayView(u.data(), u.size()) {}
|
||||
@ -258,6 +266,18 @@ class ArrayView final : public impl::ArrayViewBase<T, Size> {
|
||||
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<T*> rbegin() const {
|
||||
return std::make_reverse_iterator(end());
|
||||
}
|
||||
std::reverse_iterator<T*> rend() const {
|
||||
return std::make_reverse_iterator(begin());
|
||||
}
|
||||
std::reverse_iterator<const T*> crbegin() const {
|
||||
return std::make_reverse_iterator(cend());
|
||||
}
|
||||
std::reverse_iterator<const T*> crend() const {
|
||||
return std::make_reverse_iterator(cbegin());
|
||||
}
|
||||
|
||||
ArrayView<T> subview(size_t offset, size_t size) const {
|
||||
return offset < this->size()
|
||||
|
@ -11,9 +11,14 @@
|
||||
#include "api/audio/audio_frame.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/audio/audio_view.h"
|
||||
#include "api/audio/channel_layout.h"
|
||||
#include "api/rtp_packet_infos.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
@ -24,26 +29,18 @@ AudioFrame::AudioFrame() {
|
||||
static_assert(sizeof(data_) == kMaxDataSizeBytes, "kMaxDataSizeBytes");
|
||||
}
|
||||
|
||||
void swap(AudioFrame& a, AudioFrame& b) {
|
||||
using std::swap;
|
||||
swap(a.timestamp_, b.timestamp_);
|
||||
swap(a.elapsed_time_ms_, b.elapsed_time_ms_);
|
||||
swap(a.ntp_time_ms_, b.ntp_time_ms_);
|
||||
swap(a.samples_per_channel_, b.samples_per_channel_);
|
||||
swap(a.sample_rate_hz_, b.sample_rate_hz_);
|
||||
swap(a.num_channels_, b.num_channels_);
|
||||
swap(a.channel_layout_, b.channel_layout_);
|
||||
swap(a.speech_type_, b.speech_type_);
|
||||
swap(a.vad_activity_, b.vad_activity_);
|
||||
swap(a.profile_timestamp_ms_, b.profile_timestamp_ms_);
|
||||
swap(a.packet_infos_, b.packet_infos_);
|
||||
const size_t length_a = a.samples_per_channel_ * a.num_channels_;
|
||||
const size_t length_b = b.samples_per_channel_ * b.num_channels_;
|
||||
RTC_DCHECK_LE(length_a, AudioFrame::kMaxDataSizeSamples);
|
||||
RTC_DCHECK_LE(length_b, AudioFrame::kMaxDataSizeSamples);
|
||||
std::swap_ranges(a.data_, a.data_ + std::max(length_a, length_b), b.data_);
|
||||
swap(a.muted_, b.muted_);
|
||||
swap(a.absolute_capture_timestamp_ms_, b.absolute_capture_timestamp_ms_);
|
||||
AudioFrame::AudioFrame(int sample_rate_hz,
|
||||
size_t num_channels,
|
||||
ChannelLayout layout /*= CHANNEL_LAYOUT_UNSUPPORTED*/)
|
||||
: samples_per_channel_(SampleRateToDefaultChannelSize(sample_rate_hz)),
|
||||
sample_rate_hz_(sample_rate_hz),
|
||||
num_channels_(num_channels),
|
||||
channel_layout_(layout == CHANNEL_LAYOUT_UNSUPPORTED
|
||||
? GuessChannelLayout(num_channels)
|
||||
: layout) {
|
||||
RTC_DCHECK_LE(num_channels_, kMaxConcurrentChannels);
|
||||
RTC_DCHECK_GT(sample_rate_hz_, 0);
|
||||
RTC_DCHECK_GT(samples_per_channel_, 0u);
|
||||
}
|
||||
|
||||
void AudioFrame::Reset() {
|
||||
@ -52,7 +49,7 @@ void AudioFrame::Reset() {
|
||||
}
|
||||
|
||||
void AudioFrame::ResetWithoutMuting() {
|
||||
// TODO(wu): Zero is a valid value for |timestamp_|. We should initialize
|
||||
// TODO(wu): Zero is a valid value for `timestamp_`. We should initialize
|
||||
// to an invalid value, or add a new member to indicate invalidity.
|
||||
timestamp_ = 0;
|
||||
elapsed_time_ms_ = -1;
|
||||
@ -65,7 +62,7 @@ void AudioFrame::ResetWithoutMuting() {
|
||||
vad_activity_ = kVadUnknown;
|
||||
profile_timestamp_ms_ = 0;
|
||||
packet_infos_ = RtpPacketInfos();
|
||||
absolute_capture_timestamp_ms_ = absl::nullopt;
|
||||
absolute_capture_timestamp_ms_ = std::nullopt;
|
||||
}
|
||||
|
||||
void AudioFrame::UpdateFrame(uint32_t timestamp,
|
||||
@ -75,6 +72,7 @@ void AudioFrame::UpdateFrame(uint32_t timestamp,
|
||||
SpeechType speech_type,
|
||||
VADActivity vad_activity,
|
||||
size_t num_channels) {
|
||||
RTC_CHECK_LE(num_channels, kMaxConcurrentChannels);
|
||||
timestamp_ = timestamp;
|
||||
samples_per_channel_ = samples_per_channel;
|
||||
sample_rate_hz_ = sample_rate_hz;
|
||||
@ -87,9 +85,9 @@ void AudioFrame::UpdateFrame(uint32_t timestamp,
|
||||
}
|
||||
|
||||
const size_t length = samples_per_channel * num_channels;
|
||||
RTC_CHECK_LE(length, kMaxDataSizeSamples);
|
||||
RTC_CHECK_LE(length, data_.size());
|
||||
if (data != nullptr) {
|
||||
memcpy(data_, data, sizeof(int16_t) * length);
|
||||
memcpy(data_.data(), data, sizeof(int16_t) * length);
|
||||
muted_ = false;
|
||||
} else {
|
||||
muted_ = true;
|
||||
@ -100,6 +98,16 @@ void AudioFrame::CopyFrom(const AudioFrame& src) {
|
||||
if (this == &src)
|
||||
return;
|
||||
|
||||
if (muted_ && !src.muted()) {
|
||||
// TODO: bugs.webrtc.org/5647 - Since the default value for `muted_` is
|
||||
// false and `data_` may still be uninitialized (because we don't initialize
|
||||
// data_ as part of construction), we clear the full buffer here before
|
||||
// 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_);
|
||||
}
|
||||
|
||||
timestamp_ = src.timestamp_;
|
||||
elapsed_time_ms_ = src.elapsed_time_ms_;
|
||||
ntp_time_ms_ = src.ntp_time_ms_;
|
||||
@ -113,11 +121,10 @@ void AudioFrame::CopyFrom(const AudioFrame& src) {
|
||||
channel_layout_ = src.channel_layout_;
|
||||
absolute_capture_timestamp_ms_ = src.absolute_capture_timestamp_ms();
|
||||
|
||||
const size_t length = samples_per_channel_ * num_channels_;
|
||||
RTC_CHECK_LE(length, kMaxDataSizeSamples);
|
||||
if (!src.muted()) {
|
||||
memcpy(data_, src.data(), sizeof(int16_t) * length);
|
||||
muted_ = false;
|
||||
auto data = src.data_view();
|
||||
RTC_CHECK_LE(data.size(), data_.size());
|
||||
if (!muted_ && !data.empty()) {
|
||||
memcpy(&data_[0], &data[0], sizeof(int16_t) * data.size());
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,17 +141,56 @@ int64_t AudioFrame::ElapsedProfileTimeMs() const {
|
||||
}
|
||||
|
||||
const int16_t* AudioFrame::data() const {
|
||||
return muted_ ? empty_data() : data_;
|
||||
return muted_ ? zeroed_data().begin() : data_.data();
|
||||
}
|
||||
|
||||
InterleavedView<const int16_t> 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 rtc::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.
|
||||
return InterleavedView<const int16_t>(muted_ ? &zeroed_data()[0] : &data_[0],
|
||||
samples_per_channel_, num_channels_);
|
||||
}
|
||||
|
||||
// TODO(henrik.lundin) Can we skip zeroing the buffer?
|
||||
// See https://bugs.chromium.org/p/webrtc/issues/detail?id=5647.
|
||||
int16_t* AudioFrame::mutable_data() {
|
||||
// TODO: bugs.webrtc.org/5647 - Can we skip zeroing the buffer?
|
||||
// Consider instead if we should rather zero the buffer when `muted_` is set
|
||||
// to `true`.
|
||||
if (muted_) {
|
||||
memset(data_, 0, kMaxDataSizeBytes);
|
||||
ClearSamples(data_);
|
||||
muted_ = false;
|
||||
}
|
||||
return data_;
|
||||
return &data_[0];
|
||||
}
|
||||
|
||||
InterleavedView<int16_t> AudioFrame::mutable_data(size_t samples_per_channel,
|
||||
size_t num_channels) {
|
||||
const size_t total_samples = samples_per_channel * num_channels;
|
||||
RTC_CHECK_LE(total_samples, data_.size());
|
||||
RTC_CHECK_LE(num_channels, kMaxConcurrentChannels);
|
||||
// Sanity check for valid argument values during development.
|
||||
// If `samples_per_channel` is < `num_channels` but larger than 0,
|
||||
// then chances are the order of arguments is incorrect.
|
||||
RTC_DCHECK((samples_per_channel == 0 && num_channels == 0) ||
|
||||
num_channels <= samples_per_channel)
|
||||
<< "samples_per_channel=" << samples_per_channel
|
||||
<< "num_channels=" << num_channels;
|
||||
|
||||
// TODO: bugs.webrtc.org/5647 - Can we skip zeroing the buffer?
|
||||
// Consider instead if we should rather zero the whole buffer when `muted_` is
|
||||
// set to `true`.
|
||||
if (muted_) {
|
||||
ClearSamples(data_, total_samples);
|
||||
muted_ = false;
|
||||
}
|
||||
samples_per_channel_ = samples_per_channel;
|
||||
num_channels_ = num_channels;
|
||||
return InterleavedView<int16_t>(&data_[0], samples_per_channel, num_channels);
|
||||
}
|
||||
|
||||
void AudioFrame::Mute() {
|
||||
@ -155,10 +201,35 @@ bool AudioFrame::muted() const {
|
||||
return muted_;
|
||||
}
|
||||
|
||||
void AudioFrame::SetLayoutAndNumChannels(ChannelLayout layout,
|
||||
size_t num_channels) {
|
||||
channel_layout_ = layout;
|
||||
num_channels_ = num_channels;
|
||||
#if RTC_DCHECK_IS_ON
|
||||
// Do a sanity check that the layout and num_channels match.
|
||||
// If this lookup yield 0u, then the layout is likely CHANNEL_LAYOUT_DISCRETE.
|
||||
auto expected_num_channels = ChannelLayoutToChannelCount(layout);
|
||||
if (expected_num_channels) { // If expected_num_channels is 0
|
||||
RTC_DCHECK_EQ(expected_num_channels, num_channels_);
|
||||
}
|
||||
#endif
|
||||
RTC_CHECK_LE(samples_per_channel_ * num_channels_, data_.size());
|
||||
}
|
||||
|
||||
void AudioFrame::SetSampleRateAndChannelSize(int sample_rate) {
|
||||
sample_rate_hz_ = sample_rate;
|
||||
// We could call `AudioProcessing::GetFrameSize()` here, but that requires
|
||||
// adding a dependency on the ":audio_processing" build target, which can
|
||||
// complicate the dependency tree. Some refactoring is probably in order to
|
||||
// get some consistency around this since there are many places across the
|
||||
// code that assume this default buffer size.
|
||||
samples_per_channel_ = SampleRateToDefaultChannelSize(sample_rate_hz_);
|
||||
}
|
||||
|
||||
// static
|
||||
const int16_t* AudioFrame::empty_data() {
|
||||
rtc::ArrayView<const int16_t> AudioFrame::zeroed_data() {
|
||||
static int16_t* null_data = new int16_t[kMaxDataSizeSamples]();
|
||||
return &null_data[0];
|
||||
return rtc::ArrayView<const int16_t>(null_data, kMaxDataSizeSamples);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -14,14 +14,34 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <utility>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/audio/audio_view.h"
|
||||
#include "api/audio/channel_layout.h"
|
||||
#include "api/rtp_packet_infos.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Default webrtc buffer size in milliseconds.
|
||||
constexpr size_t kDefaultAudioBufferLengthMs = 10u;
|
||||
|
||||
// Default total number of audio buffers per second based on the default length.
|
||||
constexpr size_t kDefaultAudioBuffersPerSec =
|
||||
1000u / kDefaultAudioBufferLengthMs;
|
||||
|
||||
// Returns the number of samples a buffer needs to hold for ~10ms of a single
|
||||
// audio channel at a given sample rate.
|
||||
// See also `AudioProcessing::GetFrameSize()`.
|
||||
inline size_t SampleRateToDefaultChannelSize(size_t sample_rate) {
|
||||
// Basic sanity check. 192kHz is the highest supported input sample rate.
|
||||
RTC_DCHECK_LE(sample_rate, 192000);
|
||||
return sample_rate / kDefaultAudioBuffersPerSec;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* This class holds up to 120 ms of super-wideband (32 kHz) stereo audio. It
|
||||
* allows for adding and subtracting frames while keeping track of the resulting
|
||||
* states.
|
||||
@ -60,7 +80,17 @@ class AudioFrame {
|
||||
|
||||
AudioFrame();
|
||||
|
||||
friend void swap(AudioFrame& a, AudioFrame& b);
|
||||
// Construct an audio frame with frame length properties and channel
|
||||
// information. `samples_per_channel()` will be initialized to a 10ms buffer
|
||||
// size and if `layout` is not specified (default value of
|
||||
// CHANNEL_LAYOUT_UNSUPPORTED is set), then the channel layout is derived
|
||||
// (guessed) from `num_channels`.
|
||||
AudioFrame(int sample_rate_hz,
|
||||
size_t num_channels,
|
||||
ChannelLayout layout = CHANNEL_LAYOUT_UNSUPPORTED);
|
||||
|
||||
AudioFrame(const AudioFrame&) = delete;
|
||||
AudioFrame& operator=(const AudioFrame&) = delete;
|
||||
|
||||
// Resets all members to their default state.
|
||||
void Reset();
|
||||
@ -70,6 +100,7 @@ class AudioFrame {
|
||||
// ResetWithoutMuting() to skip this wasteful zeroing.
|
||||
void ResetWithoutMuting();
|
||||
|
||||
// TODO: b/335805780 - Accept InterleavedView.
|
||||
void UpdateFrame(uint32_t timestamp,
|
||||
const int16_t* data,
|
||||
size_t samples_per_channel,
|
||||
@ -92,20 +123,40 @@ class AudioFrame {
|
||||
int64_t ElapsedProfileTimeMs() const;
|
||||
|
||||
// data() returns a zeroed static buffer if the frame is muted.
|
||||
// mutable_frame() always returns a non-static buffer; the first call to
|
||||
// mutable_frame() zeros the non-static buffer and marks the frame unmuted.
|
||||
// TODO: b/335805780 - Return InterleavedView.
|
||||
const int16_t* data() const;
|
||||
|
||||
// Returns a read-only view of all the valid samples held by the AudioFrame.
|
||||
// For a muted AudioFrame, the samples will all be 0.
|
||||
InterleavedView<const int16_t> data_view() const;
|
||||
|
||||
// mutable_frame() always returns a non-static buffer; the first call to
|
||||
// mutable_frame() zeros the buffer and marks the frame as unmuted.
|
||||
// TODO: b/335805780 - Return an InterleavedView.
|
||||
int16_t* mutable_data();
|
||||
|
||||
// Grants write access to the audio buffer. The size of the returned writable
|
||||
// view is determined by the `samples_per_channel` and `num_channels`
|
||||
// dimensions which the function checks for correctness and stores in the
|
||||
// internal member variables; `samples_per_channel()` and `num_channels()`
|
||||
// respectively.
|
||||
// If the state is currently muted, the returned view will be zeroed out.
|
||||
InterleavedView<int16_t> mutable_data(size_t samples_per_channel,
|
||||
size_t num_channels);
|
||||
|
||||
// Prefer to mute frames using AudioFrameOperations::Mute.
|
||||
void Mute();
|
||||
// Frame is muted by default.
|
||||
bool muted() const;
|
||||
|
||||
size_t max_16bit_samples() const { return kMaxDataSizeSamples; }
|
||||
size_t max_16bit_samples() const { return data_.size(); }
|
||||
size_t samples_per_channel() const { return samples_per_channel_; }
|
||||
size_t num_channels() const { return num_channels_; }
|
||||
|
||||
ChannelLayout channel_layout() const { return channel_layout_; }
|
||||
// Sets the `channel_layout` property as well as `num_channels`.
|
||||
void SetLayoutAndNumChannels(ChannelLayout layout, size_t num_channels);
|
||||
|
||||
int sample_rate_hz() const { return sample_rate_hz_; }
|
||||
|
||||
void set_absolute_capture_timestamp_ms(
|
||||
@ -113,10 +164,14 @@ class AudioFrame {
|
||||
absolute_capture_timestamp_ms_ = absolute_capture_time_stamp_ms;
|
||||
}
|
||||
|
||||
absl::optional<int64_t> absolute_capture_timestamp_ms() const {
|
||||
std::optional<int64_t> absolute_capture_timestamp_ms() const {
|
||||
return absolute_capture_timestamp_ms_;
|
||||
}
|
||||
|
||||
// Sets the sample_rate_hz and samples_per_channel properties based on a
|
||||
// given sample rate and calculates a default 10ms samples_per_channel value.
|
||||
void SetSampleRateAndChannelSize(int sample_rate);
|
||||
|
||||
// RTP timestamp of the first sample in the AudioFrame.
|
||||
uint32_t timestamp_ = 0;
|
||||
// Time since the first frame in milliseconds.
|
||||
@ -128,18 +183,17 @@ class AudioFrame {
|
||||
size_t samples_per_channel_ = 0;
|
||||
int sample_rate_hz_ = 0;
|
||||
size_t num_channels_ = 0;
|
||||
ChannelLayout channel_layout_ = CHANNEL_LAYOUT_NONE;
|
||||
SpeechType speech_type_ = kUndefined;
|
||||
VADActivity vad_activity_ = kVadUnknown;
|
||||
// Monotonically increasing timestamp intended for profiling of audio frames.
|
||||
// Typically used for measuring elapsed time between two different points in
|
||||
// the audio path. No lock is used to save resources and we are thread safe
|
||||
// by design.
|
||||
// TODO(nisse@webrtc.org): consider using absl::optional.
|
||||
// TODO(nisse@webrtc.org): consider using std::optional.
|
||||
int64_t profile_timestamp_ms_ = 0;
|
||||
|
||||
// Information about packets used to assemble this audio frame. This is needed
|
||||
// by |SourceTracker| when the frame is delivered to the RTCRtpReceiver's
|
||||
// by `SourceTracker` when the frame is delivered to the RTCRtpReceiver's
|
||||
// MediaStreamTrack, in order to implement getContributingSources(). See:
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcrtpreceiver-getcontributingsources
|
||||
//
|
||||
@ -149,27 +203,26 @@ class AudioFrame {
|
||||
// sync buffer is the small sample-holding buffer located after the audio
|
||||
// decoder and before where samples are assembled into output frames.
|
||||
//
|
||||
// |RtpPacketInfos| may also be empty if the audio samples did not come from
|
||||
// `RtpPacketInfos` may also be empty if the audio samples did not come from
|
||||
// RTP packets. E.g. if the audio were locally generated by packet loss
|
||||
// concealment, comfort noise generation, etc.
|
||||
RtpPacketInfos packet_infos_;
|
||||
|
||||
private:
|
||||
// A permanently zeroed out buffer to represent muted frames. This is a
|
||||
// header-only class, so the only way to avoid creating a separate empty
|
||||
// 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 const int16_t* empty_data();
|
||||
static rtc::ArrayView<const int16_t> zeroed_data();
|
||||
|
||||
int16_t data_[kMaxDataSizeSamples];
|
||||
std::array<int16_t, kMaxDataSizeSamples> data_;
|
||||
bool muted_ = true;
|
||||
ChannelLayout channel_layout_ = CHANNEL_LAYOUT_NONE;
|
||||
|
||||
// Absolute capture timestamp when this audio frame was originally captured.
|
||||
// This is only valid for audio frames captured on this machine. The absolute
|
||||
// capture timestamp of a received frame is found in |packet_infos_|.
|
||||
// capture timestamp of a received frame is found in `packet_infos_`.
|
||||
// This timestamp MUST be based on the same clock as rtc::TimeMillis().
|
||||
absl::optional<int64_t> absolute_capture_timestamp_ms_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioFrame);
|
||||
std::optional<int64_t> absolute_capture_timestamp_ms_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
211
webrtc/api/audio/audio_processing.cc
Normal file
211
webrtc/api/audio/audio_processing.cc
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "api/audio/audio_processing.h"
|
||||
#include <string>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
using Agc1Config = AudioProcessing::Config::GainController1;
|
||||
using Agc2Config = AudioProcessing::Config::GainController2;
|
||||
|
||||
std::string NoiseSuppressionLevelToString(
|
||||
const AudioProcessing::Config::NoiseSuppression::Level& level) {
|
||||
switch (level) {
|
||||
case AudioProcessing::Config::NoiseSuppression::Level::kLow:
|
||||
return "Low";
|
||||
case AudioProcessing::Config::NoiseSuppression::Level::kModerate:
|
||||
return "Moderate";
|
||||
case AudioProcessing::Config::NoiseSuppression::Level::kHigh:
|
||||
return "High";
|
||||
case AudioProcessing::Config::NoiseSuppression::Level::kVeryHigh:
|
||||
return "VeryHigh";
|
||||
}
|
||||
RTC_CHECK_NOTREACHED();
|
||||
}
|
||||
|
||||
std::string GainController1ModeToString(const Agc1Config::Mode& mode) {
|
||||
switch (mode) {
|
||||
case Agc1Config::Mode::kAdaptiveAnalog:
|
||||
return "AdaptiveAnalog";
|
||||
case Agc1Config::Mode::kAdaptiveDigital:
|
||||
return "AdaptiveDigital";
|
||||
case Agc1Config::Mode::kFixedDigital:
|
||||
return "FixedDigital";
|
||||
}
|
||||
RTC_CHECK_NOTREACHED();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
constexpr int AudioProcessing::kNativeSampleRatesHz[];
|
||||
|
||||
void CustomProcessing::SetRuntimeSetting(
|
||||
AudioProcessing::RuntimeSetting setting) {}
|
||||
|
||||
bool Agc1Config::operator==(const Agc1Config& rhs) const {
|
||||
const auto& analog_lhs = analog_gain_controller;
|
||||
const auto& analog_rhs = rhs.analog_gain_controller;
|
||||
return enabled == rhs.enabled && mode == rhs.mode &&
|
||||
target_level_dbfs == rhs.target_level_dbfs &&
|
||||
compression_gain_db == rhs.compression_gain_db &&
|
||||
enable_limiter == rhs.enable_limiter &&
|
||||
analog_lhs.enabled == analog_rhs.enabled &&
|
||||
analog_lhs.startup_min_volume == analog_rhs.startup_min_volume &&
|
||||
analog_lhs.clipped_level_min == analog_rhs.clipped_level_min &&
|
||||
analog_lhs.enable_digital_adaptive ==
|
||||
analog_rhs.enable_digital_adaptive &&
|
||||
analog_lhs.clipped_level_step == analog_rhs.clipped_level_step &&
|
||||
analog_lhs.clipped_ratio_threshold ==
|
||||
analog_rhs.clipped_ratio_threshold &&
|
||||
analog_lhs.clipped_wait_frames == analog_rhs.clipped_wait_frames &&
|
||||
analog_lhs.clipping_predictor.mode ==
|
||||
analog_rhs.clipping_predictor.mode &&
|
||||
analog_lhs.clipping_predictor.window_length ==
|
||||
analog_rhs.clipping_predictor.window_length &&
|
||||
analog_lhs.clipping_predictor.reference_window_length ==
|
||||
analog_rhs.clipping_predictor.reference_window_length &&
|
||||
analog_lhs.clipping_predictor.reference_window_delay ==
|
||||
analog_rhs.clipping_predictor.reference_window_delay &&
|
||||
analog_lhs.clipping_predictor.clipping_threshold ==
|
||||
analog_rhs.clipping_predictor.clipping_threshold &&
|
||||
analog_lhs.clipping_predictor.crest_factor_margin ==
|
||||
analog_rhs.clipping_predictor.crest_factor_margin &&
|
||||
analog_lhs.clipping_predictor.use_predicted_step ==
|
||||
analog_rhs.clipping_predictor.use_predicted_step;
|
||||
}
|
||||
|
||||
bool Agc2Config::AdaptiveDigital::operator==(
|
||||
const Agc2Config::AdaptiveDigital& rhs) const {
|
||||
return enabled == rhs.enabled && headroom_db == rhs.headroom_db &&
|
||||
max_gain_db == rhs.max_gain_db &&
|
||||
initial_gain_db == rhs.initial_gain_db &&
|
||||
max_gain_change_db_per_second == rhs.max_gain_change_db_per_second &&
|
||||
max_output_noise_level_dbfs == rhs.max_output_noise_level_dbfs;
|
||||
}
|
||||
|
||||
bool Agc2Config::InputVolumeController::operator==(
|
||||
const Agc2Config::InputVolumeController& rhs) const {
|
||||
return enabled == rhs.enabled;
|
||||
}
|
||||
|
||||
bool Agc2Config::operator==(const Agc2Config& rhs) const {
|
||||
return enabled == rhs.enabled &&
|
||||
fixed_digital.gain_db == rhs.fixed_digital.gain_db &&
|
||||
adaptive_digital == rhs.adaptive_digital &&
|
||||
input_volume_controller == rhs.input_volume_controller;
|
||||
}
|
||||
|
||||
bool AudioProcessing::Config::CaptureLevelAdjustment::operator==(
|
||||
const AudioProcessing::Config::CaptureLevelAdjustment& rhs) const {
|
||||
return enabled == rhs.enabled && pre_gain_factor == rhs.pre_gain_factor &&
|
||||
post_gain_factor == rhs.post_gain_factor &&
|
||||
analog_mic_gain_emulation == rhs.analog_mic_gain_emulation;
|
||||
}
|
||||
|
||||
bool AudioProcessing::Config::CaptureLevelAdjustment::AnalogMicGainEmulation::
|
||||
operator==(const AudioProcessing::Config::CaptureLevelAdjustment::
|
||||
AnalogMicGainEmulation& rhs) const {
|
||||
return enabled == rhs.enabled && initial_level == rhs.initial_level;
|
||||
}
|
||||
|
||||
std::string AudioProcessing::Config::ToString() const {
|
||||
char buf[2048];
|
||||
rtc::SimpleStringBuilder builder(buf);
|
||||
builder << "AudioProcessing::Config{ "
|
||||
"pipeline: { "
|
||||
"maximum_internal_processing_rate: "
|
||||
<< pipeline.maximum_internal_processing_rate
|
||||
<< ", multi_channel_render: " << pipeline.multi_channel_render
|
||||
<< ", multi_channel_capture: " << pipeline.multi_channel_capture
|
||||
<< " }, pre_amplifier: { enabled: " << pre_amplifier.enabled
|
||||
<< ", fixed_gain_factor: " << pre_amplifier.fixed_gain_factor
|
||||
<< " },capture_level_adjustment: { enabled: "
|
||||
<< capture_level_adjustment.enabled
|
||||
<< ", pre_gain_factor: " << capture_level_adjustment.pre_gain_factor
|
||||
<< ", post_gain_factor: " << capture_level_adjustment.post_gain_factor
|
||||
<< ", analog_mic_gain_emulation: { enabled: "
|
||||
<< capture_level_adjustment.analog_mic_gain_emulation.enabled
|
||||
<< ", initial_level: "
|
||||
<< capture_level_adjustment.analog_mic_gain_emulation.initial_level
|
||||
<< " }}, high_pass_filter: { enabled: " << high_pass_filter.enabled
|
||||
<< " }, echo_canceller: { enabled: " << echo_canceller.enabled
|
||||
<< ", mobile_mode: " << echo_canceller.mobile_mode
|
||||
<< ", enforce_high_pass_filtering: "
|
||||
<< echo_canceller.enforce_high_pass_filtering
|
||||
<< " }, noise_suppression: { enabled: " << noise_suppression.enabled
|
||||
<< ", level: "
|
||||
<< NoiseSuppressionLevelToString(noise_suppression.level)
|
||||
<< " }, transient_suppression: { enabled: "
|
||||
<< transient_suppression.enabled
|
||||
<< " }, gain_controller1: { enabled: " << gain_controller1.enabled
|
||||
<< ", mode: " << GainController1ModeToString(gain_controller1.mode)
|
||||
<< ", target_level_dbfs: " << gain_controller1.target_level_dbfs
|
||||
<< ", compression_gain_db: " << gain_controller1.compression_gain_db
|
||||
<< ", enable_limiter: " << gain_controller1.enable_limiter
|
||||
<< ", analog_gain_controller { enabled: "
|
||||
<< gain_controller1.analog_gain_controller.enabled
|
||||
<< ", startup_min_volume: "
|
||||
<< gain_controller1.analog_gain_controller.startup_min_volume
|
||||
<< ", clipped_level_min: "
|
||||
<< gain_controller1.analog_gain_controller.clipped_level_min
|
||||
<< ", enable_digital_adaptive: "
|
||||
<< gain_controller1.analog_gain_controller.enable_digital_adaptive
|
||||
<< ", clipped_level_step: "
|
||||
<< gain_controller1.analog_gain_controller.clipped_level_step
|
||||
<< ", clipped_ratio_threshold: "
|
||||
<< gain_controller1.analog_gain_controller.clipped_ratio_threshold
|
||||
<< ", clipped_wait_frames: "
|
||||
<< gain_controller1.analog_gain_controller.clipped_wait_frames
|
||||
<< ", clipping_predictor: { enabled: "
|
||||
<< gain_controller1.analog_gain_controller.clipping_predictor.enabled
|
||||
<< ", mode: "
|
||||
<< gain_controller1.analog_gain_controller.clipping_predictor.mode
|
||||
<< ", window_length: "
|
||||
<< gain_controller1.analog_gain_controller.clipping_predictor
|
||||
.window_length
|
||||
<< ", reference_window_length: "
|
||||
<< gain_controller1.analog_gain_controller.clipping_predictor
|
||||
.reference_window_length
|
||||
<< ", reference_window_delay: "
|
||||
<< gain_controller1.analog_gain_controller.clipping_predictor
|
||||
.reference_window_delay
|
||||
<< ", clipping_threshold: "
|
||||
<< gain_controller1.analog_gain_controller.clipping_predictor
|
||||
.clipping_threshold
|
||||
<< ", crest_factor_margin: "
|
||||
<< gain_controller1.analog_gain_controller.clipping_predictor
|
||||
.crest_factor_margin
|
||||
<< ", use_predicted_step: "
|
||||
<< gain_controller1.analog_gain_controller.clipping_predictor
|
||||
.use_predicted_step
|
||||
<< " }}}, gain_controller2: { enabled: " << gain_controller2.enabled
|
||||
<< ", fixed_digital: { gain_db: "
|
||||
<< gain_controller2.fixed_digital.gain_db
|
||||
<< " }, adaptive_digital: { enabled: "
|
||||
<< gain_controller2.adaptive_digital.enabled
|
||||
<< ", headroom_db: " << gain_controller2.adaptive_digital.headroom_db
|
||||
<< ", max_gain_db: " << gain_controller2.adaptive_digital.max_gain_db
|
||||
<< ", initial_gain_db: "
|
||||
<< gain_controller2.adaptive_digital.initial_gain_db
|
||||
<< ", max_gain_change_db_per_second: "
|
||||
<< gain_controller2.adaptive_digital.max_gain_change_db_per_second
|
||||
<< ", max_output_noise_level_dbfs: "
|
||||
<< gain_controller2.adaptive_digital.max_output_noise_level_dbfs
|
||||
<< " }, input_volume_control : { enabled "
|
||||
<< gain_controller2.input_volume_controller.enabled << "}}";
|
||||
return builder.str();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
944
webrtc/api/audio/audio_processing.h
Normal file
944
webrtc/api/audio/audio_processing.h
Normal file
@ -0,0 +1,944 @@
|
||||
/*
|
||||
* 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 API_AUDIO_AUDIO_PROCESSING_H_
|
||||
#define API_AUDIO_AUDIO_PROCESSING_H_
|
||||
|
||||
// MSVC++ requires this to be set before any other includes to get M_PI.
|
||||
#ifndef _USE_MATH_DEFINES
|
||||
#define _USE_MATH_DEFINES
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
#include <stddef.h> // size_t
|
||||
#include <stdio.h> // FILE
|
||||
#include <string.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#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/ref_count.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "rtc_base/arraysize.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class AecDump;
|
||||
class AudioBuffer;
|
||||
|
||||
class StreamConfig;
|
||||
class ProcessingConfig;
|
||||
|
||||
class EchoDetector;
|
||||
|
||||
// The Audio Processing Module (APM) provides a collection of voice processing
|
||||
// components designed for real-time communications software.
|
||||
//
|
||||
// APM operates on two audio streams on a frame-by-frame basis. Frames of the
|
||||
// primary stream, on which all processing is applied, are passed to
|
||||
// `ProcessStream()`. Frames of the reverse direction stream are passed to
|
||||
// `ProcessReverseStream()`. On the client-side, this will typically be the
|
||||
// near-end (capture) and far-end (render) streams, respectively. APM should be
|
||||
// placed in the signal chain as close to the audio hardware abstraction layer
|
||||
// (HAL) as possible.
|
||||
//
|
||||
// On the server-side, the reverse stream will normally not be used, with
|
||||
// processing occurring on each incoming stream.
|
||||
//
|
||||
// Component interfaces follow a similar pattern and are accessed through
|
||||
// corresponding getters in APM. All components are disabled at create-time,
|
||||
// with default settings that are recommended for most situations. New settings
|
||||
// can be applied without enabling a component. Enabling a component triggers
|
||||
// memory allocation and initialization to allow it to start processing the
|
||||
// streams.
|
||||
//
|
||||
// Thread safety is provided with the following assumptions to reduce locking
|
||||
// overhead:
|
||||
// 1. The stream getters and setters are called from the same thread as
|
||||
// ProcessStream(). More precisely, stream functions are never called
|
||||
// concurrently with ProcessStream().
|
||||
// 2. Parameter getters are never called concurrently with the corresponding
|
||||
// setter.
|
||||
//
|
||||
// APM accepts only linear PCM audio data in chunks of ~10 ms (see
|
||||
// AudioProcessing::GetFrameSize() for details) and sample rates ranging from
|
||||
// 8000 Hz to 384000 Hz. The int16 interfaces use interleaved data, while the
|
||||
// float interfaces use deinterleaved data.
|
||||
//
|
||||
// Usage example, omitting error checking:
|
||||
// rtc::scoped_refptr<AudioProcessing> apm = AudioProcessingBuilder().Create();
|
||||
//
|
||||
// AudioProcessing::Config config;
|
||||
// config.echo_canceller.enabled = true;
|
||||
// config.echo_canceller.mobile_mode = false;
|
||||
//
|
||||
// config.gain_controller1.enabled = true;
|
||||
// config.gain_controller1.mode =
|
||||
// AudioProcessing::Config::GainController1::kAdaptiveAnalog;
|
||||
// config.gain_controller1.analog_level_minimum = 0;
|
||||
// config.gain_controller1.analog_level_maximum = 255;
|
||||
//
|
||||
// config.gain_controller2.enabled = true;
|
||||
//
|
||||
// config.high_pass_filter.enabled = true;
|
||||
//
|
||||
// apm->ApplyConfig(config)
|
||||
//
|
||||
// // Start a voice call...
|
||||
//
|
||||
// // ... Render frame arrives bound for the audio HAL ...
|
||||
// apm->ProcessReverseStream(render_frame);
|
||||
//
|
||||
// // ... Capture frame arrives from the audio HAL ...
|
||||
// // Call required set_stream_ functions.
|
||||
// apm->set_stream_delay_ms(delay_ms);
|
||||
// apm->set_stream_analog_level(analog_level);
|
||||
//
|
||||
// apm->ProcessStream(capture_frame);
|
||||
//
|
||||
// // Call required stream_ functions.
|
||||
// analog_level = apm->recommended_stream_analog_level();
|
||||
// has_voice = apm->stream_has_voice();
|
||||
//
|
||||
// // Repeat render and capture processing for the duration of the call...
|
||||
// // Start a new call...
|
||||
// apm->Initialize();
|
||||
//
|
||||
// // Close the application...
|
||||
// apm.reset();
|
||||
//
|
||||
class RTC_EXPORT AudioProcessing : public RefCountInterface {
|
||||
public:
|
||||
// The struct below constitutes the new parameter scheme for the audio
|
||||
// processing. It is being introduced gradually and until it is fully
|
||||
// introduced, it is prone to change.
|
||||
// TODO(peah): Remove this comment once the new config scheme is fully rolled
|
||||
// out.
|
||||
//
|
||||
// The parameters and behavior of the audio processing module are controlled
|
||||
// by changing the default values in the AudioProcessing::Config struct.
|
||||
// The config is applied by passing the struct to the ApplyConfig method.
|
||||
//
|
||||
// This config is intended to be used during setup, and to enable/disable
|
||||
// top-level processing effects. Use during processing may cause undesired
|
||||
// submodule resets, affecting the audio quality. Use the RuntimeSetting
|
||||
// construct for runtime configuration.
|
||||
struct RTC_EXPORT Config {
|
||||
// Sets the properties of the audio processing pipeline.
|
||||
struct RTC_EXPORT Pipeline {
|
||||
// Ways to downmix a multi-channel track to mono.
|
||||
enum class DownmixMethod {
|
||||
kAverageChannels, // Average across channels.
|
||||
kUseFirstChannel // Use the first channel.
|
||||
};
|
||||
|
||||
// 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 = 48000;
|
||||
// Allow multi-channel processing of render audio.
|
||||
bool multi_channel_render = false;
|
||||
// Allow multi-channel processing of capture audio when AEC3 is active
|
||||
// or a custom AEC is injected..
|
||||
bool multi_channel_capture = false;
|
||||
// Indicates how to downmix multi-channel capture audio to mono (when
|
||||
// needed).
|
||||
DownmixMethod capture_downmix_method = DownmixMethod::kAverageChannels;
|
||||
} pipeline;
|
||||
|
||||
// Enabled the pre-amplifier. It amplifies the capture signal
|
||||
// before any other processing is done.
|
||||
// TODO(webrtc:5298): Deprecate and use the pre-gain functionality in
|
||||
// capture_level_adjustment instead.
|
||||
struct PreAmplifier {
|
||||
bool enabled = false;
|
||||
float fixed_gain_factor = 1.0f;
|
||||
} pre_amplifier;
|
||||
|
||||
// Functionality for general level adjustment in the capture pipeline. This
|
||||
// should not be used together with the legacy PreAmplifier functionality.
|
||||
struct CaptureLevelAdjustment {
|
||||
bool operator==(const CaptureLevelAdjustment& rhs) const;
|
||||
bool operator!=(const CaptureLevelAdjustment& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
bool enabled = false;
|
||||
// The `pre_gain_factor` scales the signal before any processing is done.
|
||||
float pre_gain_factor = 1.0f;
|
||||
// The `post_gain_factor` scales the signal after all processing is done.
|
||||
float post_gain_factor = 1.0f;
|
||||
struct AnalogMicGainEmulation {
|
||||
bool operator==(const AnalogMicGainEmulation& rhs) const;
|
||||
bool operator!=(const AnalogMicGainEmulation& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
bool enabled = false;
|
||||
// Initial analog gain level to use for the emulated analog gain. Must
|
||||
// be in the range [0...255].
|
||||
int initial_level = 255;
|
||||
} analog_mic_gain_emulation;
|
||||
} capture_level_adjustment;
|
||||
|
||||
struct HighPassFilter {
|
||||
bool enabled = false;
|
||||
bool apply_in_full_band = true;
|
||||
} high_pass_filter;
|
||||
|
||||
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).
|
||||
bool enforce_high_pass_filtering = true;
|
||||
} echo_canceller;
|
||||
|
||||
// Enables background noise suppression.
|
||||
struct NoiseSuppression {
|
||||
bool enabled = false;
|
||||
enum Level { kLow, kModerate, kHigh, kVeryHigh };
|
||||
Level level = kModerate;
|
||||
bool analyze_linear_aec_output_when_available = false;
|
||||
} noise_suppression;
|
||||
|
||||
// TODO(bugs.webrtc.org/357281131): Deprecated. Stop using and remove.
|
||||
// Enables transient suppression.
|
||||
struct TransientSuppression {
|
||||
bool enabled = false;
|
||||
} transient_suppression;
|
||||
|
||||
// Enables automatic gain control (AGC) functionality.
|
||||
// The automatic gain control (AGC) component brings the signal to an
|
||||
// appropriate range. This is done by applying a digital gain directly and,
|
||||
// in the analog mode, prescribing an analog gain to be applied at the audio
|
||||
// HAL.
|
||||
// Recommended to be enabled on the client-side.
|
||||
struct RTC_EXPORT GainController1 {
|
||||
bool operator==(const GainController1& rhs) const;
|
||||
bool operator!=(const GainController1& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool enabled = false;
|
||||
enum Mode {
|
||||
// Adaptive mode intended for use if an analog volume control is
|
||||
// available on the capture device. It will require the user to provide
|
||||
// coupling between the OS mixer controls and AGC through the
|
||||
// stream_analog_level() functions.
|
||||
// It consists of an analog gain prescription for the audio device and a
|
||||
// digital compression stage.
|
||||
kAdaptiveAnalog,
|
||||
// Adaptive mode intended for situations in which an analog volume
|
||||
// control is unavailable. It operates in a similar fashion to the
|
||||
// adaptive analog mode, but with scaling instead applied in the digital
|
||||
// domain. As with the analog mode, it additionally uses a digital
|
||||
// compression stage.
|
||||
kAdaptiveDigital,
|
||||
// Fixed mode which enables only the digital compression stage also used
|
||||
// by the two adaptive modes.
|
||||
// It is distinguished from the adaptive modes by considering only a
|
||||
// short time-window of the input signal. It applies a fixed gain
|
||||
// through most of the input level range, and compresses (gradually
|
||||
// reduces gain with increasing level) the input signal at higher
|
||||
// levels. This mode is preferred on embedded devices where the capture
|
||||
// signal level is predictable, so that a known gain can be applied.
|
||||
kFixedDigital
|
||||
};
|
||||
Mode mode = kAdaptiveAnalog;
|
||||
// Sets the target peak level (or envelope) of the AGC in dBFs (decibels
|
||||
// from digital full-scale). The convention is to use positive values. For
|
||||
// instance, passing in a value of 3 corresponds to -3 dBFs, or a target
|
||||
// level 3 dB below full-scale. Limited to [0, 31].
|
||||
int target_level_dbfs = 3;
|
||||
// Sets the maximum gain the digital compression stage may apply, in dB. A
|
||||
// higher number corresponds to greater compression, while a value of 0
|
||||
// will leave the signal uncompressed. Limited to [0, 90].
|
||||
// For updates after APM setup, use a RuntimeSetting instead.
|
||||
int compression_gain_db = 9;
|
||||
// When enabled, the compression stage will hard limit the signal to the
|
||||
// target level. Otherwise, the signal will be compressed but not limited
|
||||
// above the target level.
|
||||
bool enable_limiter = true;
|
||||
|
||||
// Enables the analog gain controller functionality.
|
||||
struct AnalogGainController {
|
||||
bool enabled = true;
|
||||
// TODO(bugs.webrtc.org/7494): Deprecated. Stop using and remove.
|
||||
int startup_min_volume = 0;
|
||||
// Lowest analog microphone level that will be applied in response to
|
||||
// clipping.
|
||||
int clipped_level_min = 70;
|
||||
// If true, an adaptive digital gain is applied.
|
||||
bool enable_digital_adaptive = true;
|
||||
// Amount the microphone level is lowered with every clipping event.
|
||||
// Limited to (0, 255].
|
||||
int clipped_level_step = 15;
|
||||
// Proportion of clipped samples required to declare a clipping event.
|
||||
// Limited to (0.f, 1.f).
|
||||
float clipped_ratio_threshold = 0.1f;
|
||||
// Time in frames to wait after a clipping event before checking again.
|
||||
// Limited to values higher than 0.
|
||||
int clipped_wait_frames = 300;
|
||||
|
||||
// Enables clipping prediction functionality.
|
||||
struct ClippingPredictor {
|
||||
bool enabled = false;
|
||||
enum Mode {
|
||||
// Clipping event prediction mode with fixed step estimation.
|
||||
kClippingEventPrediction,
|
||||
// Clipped peak estimation mode with adaptive step estimation.
|
||||
kAdaptiveStepClippingPeakPrediction,
|
||||
// Clipped peak estimation mode with fixed step estimation.
|
||||
kFixedStepClippingPeakPrediction,
|
||||
};
|
||||
Mode mode = kClippingEventPrediction;
|
||||
// Number of frames in the sliding analysis window.
|
||||
int window_length = 5;
|
||||
// Number of frames in the sliding reference window.
|
||||
int reference_window_length = 5;
|
||||
// Reference window delay (unit: number of frames).
|
||||
int reference_window_delay = 5;
|
||||
// Clipping prediction threshold (dBFS).
|
||||
float clipping_threshold = -1.0f;
|
||||
// Crest factor drop threshold (dB).
|
||||
float crest_factor_margin = 3.0f;
|
||||
// If true, the recommended clipped level step is used to modify the
|
||||
// analog gain. Otherwise, the predictor runs without affecting the
|
||||
// analog gain.
|
||||
bool use_predicted_step = true;
|
||||
} clipping_predictor;
|
||||
} analog_gain_controller;
|
||||
} gain_controller1;
|
||||
|
||||
// Parameters for AGC2, an Automatic Gain Control (AGC) sub-module which
|
||||
// replaces the AGC sub-module parametrized by `gain_controller1`.
|
||||
// AGC2 brings the captured audio signal to the desired level by combining
|
||||
// three different controllers (namely, input volume controller, adapative
|
||||
// digital controller and fixed digital controller) and a limiter.
|
||||
// TODO(bugs.webrtc.org:7494): Name `GainController` when AGC1 removed.
|
||||
struct RTC_EXPORT GainController2 {
|
||||
bool operator==(const GainController2& rhs) const;
|
||||
bool operator!=(const GainController2& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
// AGC2 must be created if and only if `enabled` is true.
|
||||
bool enabled = false;
|
||||
|
||||
// Parameters for the input volume controller, which adjusts the input
|
||||
// volume applied when the audio is captured (e.g., microphone volume on
|
||||
// a soundcard, input volume on HAL).
|
||||
struct InputVolumeController {
|
||||
bool operator==(const InputVolumeController& rhs) const;
|
||||
bool operator!=(const InputVolumeController& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
bool enabled = false;
|
||||
} input_volume_controller;
|
||||
|
||||
// Parameters for the adaptive digital controller, which adjusts and
|
||||
// applies a digital gain after echo cancellation and after noise
|
||||
// suppression.
|
||||
struct RTC_EXPORT AdaptiveDigital {
|
||||
bool operator==(const AdaptiveDigital& rhs) const;
|
||||
bool operator!=(const AdaptiveDigital& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
bool enabled = false;
|
||||
float headroom_db = 5.0f;
|
||||
float max_gain_db = 50.0f;
|
||||
float initial_gain_db = 15.0f;
|
||||
float max_gain_change_db_per_second = 6.0f;
|
||||
float max_output_noise_level_dbfs = -50.0f;
|
||||
} adaptive_digital;
|
||||
|
||||
// Parameters for the fixed digital controller, which applies a fixed
|
||||
// digital gain after the adaptive digital controller and before the
|
||||
// limiter.
|
||||
struct FixedDigital {
|
||||
// By setting `gain_db` to a value greater than zero, the limiter can be
|
||||
// turned into a compressor that first applies a fixed gain.
|
||||
float gain_db = 0.0f;
|
||||
} fixed_digital;
|
||||
} gain_controller2;
|
||||
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
// Specifies the properties of a setting to be passed to AudioProcessing at
|
||||
// runtime.
|
||||
class RuntimeSetting {
|
||||
public:
|
||||
enum class Type {
|
||||
kNotSpecified,
|
||||
kCapturePreGain,
|
||||
kCaptureCompressionGain,
|
||||
kCaptureFixedPostGain,
|
||||
kPlayoutVolumeChange,
|
||||
kCustomRenderProcessingRuntimeSetting,
|
||||
kPlayoutAudioDeviceChange,
|
||||
kCapturePostGain,
|
||||
kCaptureOutputUsed
|
||||
};
|
||||
|
||||
// Play-out audio device properties.
|
||||
struct PlayoutAudioDeviceInfo {
|
||||
int id; // Identifies the audio device.
|
||||
int max_volume; // Maximum play-out volume.
|
||||
};
|
||||
|
||||
RuntimeSetting() : type_(Type::kNotSpecified), value_(0.0f) {}
|
||||
~RuntimeSetting() = default;
|
||||
|
||||
static RuntimeSetting CreateCapturePreGain(float gain) {
|
||||
return {Type::kCapturePreGain, gain};
|
||||
}
|
||||
|
||||
static RuntimeSetting CreateCapturePostGain(float gain) {
|
||||
return {Type::kCapturePostGain, gain};
|
||||
}
|
||||
|
||||
// Corresponds to Config::GainController1::compression_gain_db, but for
|
||||
// runtime configuration.
|
||||
static RuntimeSetting CreateCompressionGainDb(int gain_db) {
|
||||
RTC_DCHECK_GE(gain_db, 0);
|
||||
RTC_DCHECK_LE(gain_db, 90);
|
||||
return {Type::kCaptureCompressionGain, static_cast<float>(gain_db)};
|
||||
}
|
||||
|
||||
// Corresponds to Config::GainController2::fixed_digital::gain_db, but for
|
||||
// runtime configuration.
|
||||
static RuntimeSetting CreateCaptureFixedPostGain(float gain_db) {
|
||||
RTC_DCHECK_GE(gain_db, 0.0f);
|
||||
RTC_DCHECK_LE(gain_db, 90.0f);
|
||||
return {Type::kCaptureFixedPostGain, gain_db};
|
||||
}
|
||||
|
||||
// Creates a runtime setting to notify play-out (aka render) audio device
|
||||
// changes.
|
||||
static RuntimeSetting CreatePlayoutAudioDeviceChange(
|
||||
PlayoutAudioDeviceInfo audio_device) {
|
||||
return {Type::kPlayoutAudioDeviceChange, audio_device};
|
||||
}
|
||||
|
||||
// Creates a runtime setting to notify play-out (aka render) volume changes.
|
||||
// `volume` is the unnormalized volume, the maximum of which
|
||||
static RuntimeSetting CreatePlayoutVolumeChange(int volume) {
|
||||
return {Type::kPlayoutVolumeChange, volume};
|
||||
}
|
||||
|
||||
static RuntimeSetting CreateCustomRenderSetting(float payload) {
|
||||
return {Type::kCustomRenderProcessingRuntimeSetting, payload};
|
||||
}
|
||||
|
||||
static RuntimeSetting CreateCaptureOutputUsedSetting(
|
||||
bool capture_output_used) {
|
||||
return {Type::kCaptureOutputUsed, capture_output_used};
|
||||
}
|
||||
|
||||
Type type() const { return type_; }
|
||||
// Getters do not return a value but instead modify the argument to protect
|
||||
// from implicit casting.
|
||||
void GetFloat(float* value) const {
|
||||
RTC_DCHECK(value);
|
||||
*value = value_.float_value;
|
||||
}
|
||||
void GetInt(int* value) const {
|
||||
RTC_DCHECK(value);
|
||||
*value = value_.int_value;
|
||||
}
|
||||
void GetBool(bool* value) const {
|
||||
RTC_DCHECK(value);
|
||||
*value = value_.bool_value;
|
||||
}
|
||||
void GetPlayoutAudioDeviceInfo(PlayoutAudioDeviceInfo* value) const {
|
||||
RTC_DCHECK(value);
|
||||
*value = value_.playout_audio_device_info;
|
||||
}
|
||||
|
||||
private:
|
||||
RuntimeSetting(Type id, float value) : type_(id), value_(value) {}
|
||||
RuntimeSetting(Type id, int value) : type_(id), value_(value) {}
|
||||
RuntimeSetting(Type id, PlayoutAudioDeviceInfo value)
|
||||
: type_(id), value_(value) {}
|
||||
Type type_;
|
||||
union U {
|
||||
U() {}
|
||||
U(int value) : int_value(value) {}
|
||||
U(float value) : float_value(value) {}
|
||||
U(PlayoutAudioDeviceInfo value) : playout_audio_device_info(value) {}
|
||||
float float_value;
|
||||
int int_value;
|
||||
bool bool_value;
|
||||
PlayoutAudioDeviceInfo playout_audio_device_info;
|
||||
} value_;
|
||||
};
|
||||
|
||||
~AudioProcessing() override {}
|
||||
|
||||
// Initializes internal states, while retaining all user settings. This
|
||||
// should be called before beginning to process a new audio stream. However,
|
||||
// it is not necessary to call before processing the first stream after
|
||||
// creation.
|
||||
//
|
||||
// It is also not necessary to call if the audio parameters (sample
|
||||
// rate and number of channels) have changed. Passing updated parameters
|
||||
// directly to `ProcessStream()` and `ProcessReverseStream()` is permissible.
|
||||
// If the parameters are known at init-time though, they may be provided.
|
||||
// TODO(webrtc:5298): Change to return void.
|
||||
virtual int Initialize() = 0;
|
||||
|
||||
// The int16 interfaces require:
|
||||
// - only `NativeRate`s be used
|
||||
// - that the input, output and reverse rates must match
|
||||
// - that `processing_config.output_stream()` matches
|
||||
// `processing_config.input_stream()`.
|
||||
//
|
||||
// The float interfaces accept arbitrary rates and support differing input and
|
||||
// output layouts, but the output must have either one channel or the same
|
||||
// number of channels as the input.
|
||||
virtual int Initialize(const ProcessingConfig& processing_config) = 0;
|
||||
|
||||
// TODO(peah): This method is a temporary solution used to take control
|
||||
// over the parameters in the audio processing module and is likely to change.
|
||||
virtual void ApplyConfig(const Config& config) = 0;
|
||||
|
||||
// TODO(ajm): Only intended for internal use. Make private and friend the
|
||||
// necessary classes?
|
||||
virtual int proc_sample_rate_hz() const = 0;
|
||||
virtual int proc_split_sample_rate_hz() const = 0;
|
||||
virtual size_t num_input_channels() const = 0;
|
||||
virtual size_t num_proc_channels() const = 0;
|
||||
virtual size_t num_output_channels() const = 0;
|
||||
virtual size_t num_reverse_channels() const = 0;
|
||||
|
||||
// Set to true when the output of AudioProcessing will be muted or in some
|
||||
// other way not used. Ideally, the captured audio would still be processed,
|
||||
// but some components may change behavior based on this information.
|
||||
// Default false. This method takes a lock. To achieve this in a lock-less
|
||||
// manner the PostRuntimeSetting can instead be used.
|
||||
virtual void set_output_will_be_muted(bool muted) = 0;
|
||||
|
||||
// Enqueues a runtime setting.
|
||||
virtual void SetRuntimeSetting(RuntimeSetting setting) = 0;
|
||||
|
||||
// Enqueues a runtime setting. Returns a bool indicating whether the
|
||||
// enqueueing was successfull.
|
||||
virtual bool PostRuntimeSetting(RuntimeSetting setting) = 0;
|
||||
|
||||
// Accepts and produces a ~10 ms frame of interleaved 16 bit integer audio as
|
||||
// specified in `input_config` and `output_config`. `src` and `dest` may use
|
||||
// the same memory, if desired.
|
||||
virtual int ProcessStream(const int16_t* const src,
|
||||
const StreamConfig& input_config,
|
||||
const StreamConfig& output_config,
|
||||
int16_t* const dest) = 0;
|
||||
|
||||
// Accepts deinterleaved float audio with the range [-1, 1]. Each element of
|
||||
// `src` points to a channel buffer, arranged according to `input_stream`. At
|
||||
// output, the channels will be arranged according to `output_stream` in
|
||||
// `dest`.
|
||||
//
|
||||
// The output must have one channel or as many channels as the input. `src`
|
||||
// and `dest` may use the same memory, if desired.
|
||||
virtual int ProcessStream(const float* const* src,
|
||||
const StreamConfig& input_config,
|
||||
const StreamConfig& output_config,
|
||||
float* const* dest) = 0;
|
||||
|
||||
// Accepts and produces a ~10 ms frame of interleaved 16 bit integer audio for
|
||||
// the reverse direction audio stream as specified in `input_config` and
|
||||
// `output_config`. `src` and `dest` may use the same memory, if desired.
|
||||
virtual int ProcessReverseStream(const int16_t* const src,
|
||||
const StreamConfig& input_config,
|
||||
const StreamConfig& output_config,
|
||||
int16_t* const dest) = 0;
|
||||
|
||||
// Accepts deinterleaved float audio with the range [-1, 1]. Each element of
|
||||
// `data` points to a channel buffer, arranged according to `reverse_config`.
|
||||
virtual int ProcessReverseStream(const float* const* src,
|
||||
const StreamConfig& input_config,
|
||||
const StreamConfig& output_config,
|
||||
float* const* dest) = 0;
|
||||
|
||||
// Accepts deinterleaved float audio with the range [-1, 1]. Each element
|
||||
// of `data` points to a channel buffer, arranged according to
|
||||
// `reverse_config`.
|
||||
virtual int AnalyzeReverseStream(const float* const* data,
|
||||
const StreamConfig& reverse_config) = 0;
|
||||
|
||||
// Returns the most recently produced ~10 ms of the linear AEC output at a
|
||||
// rate of 16 kHz. If there is more than one capture channel, a mono
|
||||
// representation of the input is returned. Returns true/false to indicate
|
||||
// whether an output returned.
|
||||
virtual bool GetLinearAecOutput(
|
||||
rtc::ArrayView<std::array<float, 160>> 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
|
||||
// HAL. Must be within the range [0, 255].
|
||||
virtual void set_stream_analog_level(int level) = 0;
|
||||
|
||||
// When an analog mode is set, this should be called after
|
||||
// `set_stream_analog_level()` and `ProcessStream()` to obtain the recommended
|
||||
// new analog level for the audio HAL. It is the user's responsibility to
|
||||
// apply this level.
|
||||
virtual int recommended_stream_analog_level() const = 0;
|
||||
|
||||
// This must be called if and only if echo processing is enabled.
|
||||
//
|
||||
// Sets the `delay` in ms between ProcessReverseStream() receiving a far-end
|
||||
// frame and ProcessStream() receiving a near-end frame containing the
|
||||
// corresponding echo. On the client-side this can be expressed as
|
||||
// delay = (t_render - t_analyze) + (t_process - t_capture)
|
||||
// where,
|
||||
// - t_analyze is the time a frame is passed to ProcessReverseStream() and
|
||||
// t_render is the time the first sample of the same frame is rendered by
|
||||
// the audio hardware.
|
||||
// - t_capture is the time the first sample of a frame is captured by the
|
||||
// audio hardware and t_process is the time the same frame is passed to
|
||||
// ProcessStream().
|
||||
virtual int set_stream_delay_ms(int delay) = 0;
|
||||
virtual int stream_delay_ms() const = 0;
|
||||
|
||||
// Call to signal that a key press occurred (true) or did not occur (false)
|
||||
// with this chunk of audio.
|
||||
virtual void set_stream_key_pressed(bool key_pressed) = 0;
|
||||
|
||||
// Creates and attaches an webrtc::AecDump for recording debugging
|
||||
// information.
|
||||
// The `worker_queue` may not be null and must outlive the created
|
||||
// AecDump instance. |max_log_size_bytes == -1| means the log size
|
||||
// will be unlimited. `handle` may not be null. The AecDump takes
|
||||
// responsibility for `handle` and closes it in the destructor. A
|
||||
// return value of true indicates that the file has been
|
||||
// sucessfully opened, while a value of false indicates that
|
||||
// opening the file failed.
|
||||
virtual bool CreateAndAttachAecDump(
|
||||
absl::string_view file_name,
|
||||
int64_t max_log_size_bytes,
|
||||
absl::Nonnull<TaskQueueBase*> worker_queue) = 0;
|
||||
virtual bool CreateAndAttachAecDump(
|
||||
absl::Nonnull<FILE*> handle,
|
||||
int64_t max_log_size_bytes,
|
||||
absl::Nonnull<TaskQueueBase*> worker_queue) = 0;
|
||||
|
||||
// TODO(webrtc:5298) Deprecated variant.
|
||||
// Attaches provided webrtc::AecDump for recording debugging
|
||||
// information. Log file and maximum file size logic is supposed to
|
||||
// be handled by implementing instance of AecDump. Calling this
|
||||
// method when another AecDump is attached resets the active AecDump
|
||||
// with a new one. This causes the d-tor of the earlier AecDump to
|
||||
// be called. The d-tor call may block until all pending logging
|
||||
// tasks are completed.
|
||||
virtual void AttachAecDump(std::unique_ptr<AecDump> aec_dump) = 0;
|
||||
|
||||
// If no AecDump is attached, this has no effect. If an AecDump is
|
||||
// attached, it's destructor is called. The d-tor may block until
|
||||
// all pending logging tasks are completed.
|
||||
virtual void DetachAecDump() = 0;
|
||||
|
||||
// Get audio processing statistics.
|
||||
virtual AudioProcessingStats GetStatistics() = 0;
|
||||
// TODO(webrtc:5298) Deprecated variant. The `has_remote_tracks` argument
|
||||
// should be set if there are active remote tracks (this would usually be true
|
||||
// during a call). If there are no remote tracks some of the stats will not be
|
||||
// set by AudioProcessing, because they only make sense if there is at least
|
||||
// one remote track.
|
||||
virtual AudioProcessingStats GetStatistics(bool has_remote_tracks) = 0;
|
||||
|
||||
// Returns the last applied configuration.
|
||||
virtual AudioProcessing::Config GetConfig() const = 0;
|
||||
|
||||
enum Error {
|
||||
// Fatal errors.
|
||||
kNoError = 0,
|
||||
kUnspecifiedError = -1,
|
||||
kCreationFailedError = -2,
|
||||
kUnsupportedComponentError = -3,
|
||||
kUnsupportedFunctionError = -4,
|
||||
kNullPointerError = -5,
|
||||
kBadParameterError = -6,
|
||||
kBadSampleRateError = -7,
|
||||
kBadDataLengthError = -8,
|
||||
kBadNumberChannelsError = -9,
|
||||
kFileError = -10,
|
||||
kStreamParameterNotSetError = -11,
|
||||
kNotEnabledError = -12,
|
||||
|
||||
// Warnings are non-fatal.
|
||||
// This results when a set_stream_ parameter is out of range. Processing
|
||||
// will continue, but the parameter may have been truncated.
|
||||
kBadStreamParameterWarning = -13
|
||||
};
|
||||
|
||||
// Native rates supported by the integer interfaces.
|
||||
enum NativeRate {
|
||||
kSampleRate8kHz = 8000,
|
||||
kSampleRate16kHz = 16000,
|
||||
kSampleRate32kHz = 32000,
|
||||
kSampleRate48kHz = 48000
|
||||
};
|
||||
|
||||
// TODO(kwiberg): We currently need to support a compiler (Visual C++) that
|
||||
// complains if we don't explicitly state the size of the array here. Remove
|
||||
// the size when that's no longer the case.
|
||||
static constexpr int kNativeSampleRatesHz[4] = {
|
||||
kSampleRate8kHz, kSampleRate16kHz, kSampleRate32kHz, kSampleRate48kHz};
|
||||
static constexpr size_t kNumNativeSampleRates =
|
||||
arraysize(kNativeSampleRatesHz);
|
||||
static constexpr int kMaxNativeSampleRateHz =
|
||||
kNativeSampleRatesHz[kNumNativeSampleRates - 1];
|
||||
|
||||
// APM processes audio in chunks of about 10 ms. See GetFrameSize() for
|
||||
// details.
|
||||
static constexpr int kChunkSizeMs = 10;
|
||||
|
||||
// Returns floor(sample_rate_hz/100): the number of samples per channel used
|
||||
// as input and output to the audio processing module in calls to
|
||||
// ProcessStream, ProcessReverseStream, AnalyzeReverseStream, and
|
||||
// GetLinearAecOutput.
|
||||
//
|
||||
// This is exactly 10 ms for sample rates divisible by 100. For example:
|
||||
// - 48000 Hz (480 samples per channel),
|
||||
// - 44100 Hz (441 samples per channel),
|
||||
// - 16000 Hz (160 samples per channel).
|
||||
//
|
||||
// Sample rates not divisible by 100 are received/produced in frames of
|
||||
// approximately 10 ms. For example:
|
||||
// - 22050 Hz (220 samples per channel, or ~9.98 ms per frame),
|
||||
// - 11025 Hz (110 samples per channel, or ~9.98 ms per frame).
|
||||
// These nondivisible sample rates yield lower audio quality compared to
|
||||
// multiples of 100. Internal resampling to 10 ms frames causes a simulated
|
||||
// clock drift effect which impacts the performance of (for example) echo
|
||||
// cancellation.
|
||||
static int GetFrameSize(int sample_rate_hz) { return sample_rate_hz / 100; }
|
||||
};
|
||||
|
||||
// Experimental interface for a custom analysis submodule.
|
||||
class CustomAudioAnalyzer {
|
||||
public:
|
||||
// (Re-) Initializes the submodule.
|
||||
virtual void Initialize(int sample_rate_hz, int num_channels) = 0;
|
||||
// Analyzes the given capture or render signal.
|
||||
virtual void Analyze(const AudioBuffer* audio) = 0;
|
||||
// Returns a string representation of the module state.
|
||||
virtual std::string ToString() const = 0;
|
||||
|
||||
virtual ~CustomAudioAnalyzer() {}
|
||||
};
|
||||
|
||||
// Interface for a custom processing submodule.
|
||||
class CustomProcessing {
|
||||
public:
|
||||
// (Re-)Initializes the submodule.
|
||||
virtual void Initialize(int sample_rate_hz, int num_channels) = 0;
|
||||
// Processes the given capture or render signal.
|
||||
virtual void Process(AudioBuffer* audio) = 0;
|
||||
// Returns a string representation of the module state.
|
||||
virtual std::string ToString() const = 0;
|
||||
// Handles RuntimeSettings. TODO(webrtc:9262): make pure virtual
|
||||
// after updating dependencies.
|
||||
virtual void SetRuntimeSetting(AudioProcessing::RuntimeSetting setting);
|
||||
|
||||
virtual ~CustomProcessing() {}
|
||||
};
|
||||
|
||||
class RTC_EXPORT AudioProcessingBuilder {
|
||||
public:
|
||||
AudioProcessingBuilder();
|
||||
AudioProcessingBuilder(const AudioProcessingBuilder&) = delete;
|
||||
AudioProcessingBuilder& operator=(const AudioProcessingBuilder&) = delete;
|
||||
~AudioProcessingBuilder();
|
||||
|
||||
// Sets the APM configuration.
|
||||
AudioProcessingBuilder& SetConfig(const AudioProcessing::Config& config) {
|
||||
config_ = config;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Sets the echo controller factory to inject when APM is created.
|
||||
AudioProcessingBuilder& SetEchoControlFactory(
|
||||
std::unique_ptr<EchoControlFactory> echo_control_factory) {
|
||||
echo_control_factory_ = std::move(echo_control_factory);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Sets the capture post-processing sub-module to inject when APM is created.
|
||||
AudioProcessingBuilder& SetCapturePostProcessing(
|
||||
std::unique_ptr<CustomProcessing> capture_post_processing) {
|
||||
capture_post_processing_ = std::move(capture_post_processing);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Sets the render pre-processing sub-module to inject when APM is created.
|
||||
AudioProcessingBuilder& SetRenderPreProcessing(
|
||||
std::unique_ptr<CustomProcessing> render_pre_processing) {
|
||||
render_pre_processing_ = std::move(render_pre_processing);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Sets the echo detector to inject when APM is created.
|
||||
AudioProcessingBuilder& SetEchoDetector(
|
||||
rtc::scoped_refptr<EchoDetector> echo_detector) {
|
||||
echo_detector_ = std::move(echo_detector);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Sets the capture analyzer sub-module to inject when APM is created.
|
||||
AudioProcessingBuilder& SetCaptureAnalyzer(
|
||||
std::unique_ptr<CustomAudioAnalyzer> capture_analyzer) {
|
||||
capture_analyzer_ = std::move(capture_analyzer);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Creates an APM instance with the specified config or the default one if
|
||||
// unspecified. Injects the specified components transferring the ownership
|
||||
// to the newly created APM instance - i.e., except for the config, the
|
||||
// builder is reset to its initial state.
|
||||
rtc::scoped_refptr<AudioProcessing> Create();
|
||||
|
||||
private:
|
||||
AudioProcessing::Config config_;
|
||||
std::unique_ptr<EchoControlFactory> echo_control_factory_;
|
||||
std::unique_ptr<CustomProcessing> capture_post_processing_;
|
||||
std::unique_ptr<CustomProcessing> render_pre_processing_;
|
||||
rtc::scoped_refptr<EchoDetector> echo_detector_;
|
||||
std::unique_ptr<CustomAudioAnalyzer> capture_analyzer_;
|
||||
};
|
||||
|
||||
class StreamConfig {
|
||||
public:
|
||||
// sample_rate_hz: The sampling rate of the stream.
|
||||
// num_channels: The number of audio channels in the stream.
|
||||
StreamConfig(int sample_rate_hz = 0,
|
||||
size_t num_channels = 0) // NOLINT(runtime/explicit)
|
||||
: sample_rate_hz_(sample_rate_hz),
|
||||
num_channels_(num_channels),
|
||||
num_frames_(calculate_frames(sample_rate_hz)) {}
|
||||
|
||||
void set_sample_rate_hz(int value) {
|
||||
sample_rate_hz_ = value;
|
||||
num_frames_ = calculate_frames(value);
|
||||
}
|
||||
void set_num_channels(size_t value) { num_channels_ = value; }
|
||||
|
||||
int sample_rate_hz() const { return sample_rate_hz_; }
|
||||
|
||||
// The number of channels in the stream.
|
||||
size_t num_channels() const { return num_channels_; }
|
||||
|
||||
size_t num_frames() const { return num_frames_; }
|
||||
size_t num_samples() const { return num_channels_ * num_frames_; }
|
||||
|
||||
bool operator==(const StreamConfig& other) const {
|
||||
return sample_rate_hz_ == other.sample_rate_hz_ &&
|
||||
num_channels_ == other.num_channels_;
|
||||
}
|
||||
|
||||
bool operator!=(const StreamConfig& other) const { return !(*this == other); }
|
||||
|
||||
private:
|
||||
static size_t calculate_frames(int sample_rate_hz) {
|
||||
return static_cast<size_t>(AudioProcessing::GetFrameSize(sample_rate_hz));
|
||||
}
|
||||
|
||||
int sample_rate_hz_;
|
||||
size_t num_channels_;
|
||||
size_t num_frames_;
|
||||
};
|
||||
|
||||
class ProcessingConfig {
|
||||
public:
|
||||
enum StreamName {
|
||||
kInputStream,
|
||||
kOutputStream,
|
||||
kReverseInputStream,
|
||||
kReverseOutputStream,
|
||||
kNumStreamNames,
|
||||
};
|
||||
|
||||
const StreamConfig& input_stream() const {
|
||||
return streams[StreamName::kInputStream];
|
||||
}
|
||||
const StreamConfig& output_stream() const {
|
||||
return streams[StreamName::kOutputStream];
|
||||
}
|
||||
const StreamConfig& reverse_input_stream() const {
|
||||
return streams[StreamName::kReverseInputStream];
|
||||
}
|
||||
const StreamConfig& reverse_output_stream() const {
|
||||
return streams[StreamName::kReverseOutputStream];
|
||||
}
|
||||
|
||||
StreamConfig& input_stream() { return streams[StreamName::kInputStream]; }
|
||||
StreamConfig& output_stream() { return streams[StreamName::kOutputStream]; }
|
||||
StreamConfig& reverse_input_stream() {
|
||||
return streams[StreamName::kReverseInputStream];
|
||||
}
|
||||
StreamConfig& reverse_output_stream() {
|
||||
return streams[StreamName::kReverseOutputStream];
|
||||
}
|
||||
|
||||
bool operator==(const ProcessingConfig& other) const {
|
||||
for (int i = 0; i < StreamName::kNumStreamNames; ++i) {
|
||||
if (this->streams[i] != other.streams[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const ProcessingConfig& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
StreamConfig streams[StreamName::kNumStreamNames];
|
||||
};
|
||||
|
||||
// Interface for an echo detector submodule.
|
||||
class EchoDetector : public RefCountInterface {
|
||||
public:
|
||||
// (Re-)Initializes the submodule.
|
||||
virtual void Initialize(int capture_sample_rate_hz,
|
||||
int num_capture_channels,
|
||||
int render_sample_rate_hz,
|
||||
int num_render_channels) = 0;
|
||||
|
||||
// Analysis (not changing) of the first channel of the render signal.
|
||||
virtual void AnalyzeRenderAudio(rtc::ArrayView<const float> render_audio) = 0;
|
||||
|
||||
// Analysis (not changing) of the capture signal.
|
||||
virtual void AnalyzeCaptureAudio(
|
||||
rtc::ArrayView<const float> capture_audio) = 0;
|
||||
|
||||
struct Metrics {
|
||||
std::optional<double> echo_likelihood;
|
||||
std::optional<double> echo_likelihood_recent_max;
|
||||
};
|
||||
|
||||
// Collect current metrics from the echo detector.
|
||||
virtual Metrics GetMetrics() const = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_AUDIO_AUDIO_PROCESSING_H_
|
@ -8,7 +8,7 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/audio_processing/include/audio_processing_statistics.h"
|
||||
#include "api/audio/audio_processing_statistics.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
68
webrtc/api/audio/audio_processing_statistics.h
Normal file
68
webrtc/api/audio/audio_processing_statistics.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef API_AUDIO_AUDIO_PROCESSING_STATISTICS_H_
|
||||
#define API_AUDIO_AUDIO_PROCESSING_STATISTICS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
// This version of the stats uses Optionals, it will replace the regular
|
||||
// AudioProcessingStatistics struct.
|
||||
struct RTC_EXPORT AudioProcessingStats {
|
||||
AudioProcessingStats();
|
||||
AudioProcessingStats(const AudioProcessingStats& other);
|
||||
~AudioProcessingStats();
|
||||
|
||||
// Deprecated.
|
||||
// TODO(bugs.webrtc.org/11226): Remove.
|
||||
// True if voice is detected in the last capture frame, after processing.
|
||||
// It is conservative in flagging audio as speech, with low likelihood of
|
||||
// incorrectly flagging a frame as voice.
|
||||
// Only reported if voice detection is enabled in AudioProcessing::Config.
|
||||
std::optional<bool> voice_detected;
|
||||
|
||||
// AEC Statistics.
|
||||
// ERL = 10log_10(P_far / P_echo)
|
||||
std::optional<double> echo_return_loss;
|
||||
// ERLE = 10log_10(P_echo / P_out)
|
||||
std::optional<double> echo_return_loss_enhancement;
|
||||
// Fraction of time that the AEC linear filter is divergent, in a 1-second
|
||||
// non-overlapped aggregation window.
|
||||
std::optional<double> divergent_filter_fraction;
|
||||
|
||||
// The delay metrics consists of the delay median and standard deviation. It
|
||||
// also consists of the fraction of delay estimates that can make the echo
|
||||
// cancellation perform poorly. The values are aggregated until the first
|
||||
// call to `GetStatistics()` and afterwards aggregated and updated every
|
||||
// second. Note that if there are several clients pulling metrics from
|
||||
// `GetStatistics()` during a session the first call from any of them will
|
||||
// change to one second aggregation window for all.
|
||||
std::optional<int32_t> delay_median_ms;
|
||||
std::optional<int32_t> delay_standard_deviation_ms;
|
||||
|
||||
// Residual echo detector likelihood.
|
||||
std::optional<double> residual_echo_likelihood;
|
||||
// Maximum residual echo likelihood from the last time period.
|
||||
std::optional<double> residual_echo_likelihood_recent_max;
|
||||
|
||||
// The instantaneous delay estimate produced in the AEC. The unit is in
|
||||
// milliseconds and the value is the instantaneous value at the time of the
|
||||
// call to `GetStatistics()`.
|
||||
std::optional<int32_t> delay_ms;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_AUDIO_AUDIO_PROCESSING_STATISTICS_H_
|
269
webrtc/api/audio/audio_view.h
Normal file
269
webrtc/api/audio/audio_view.h
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef API_AUDIO_AUDIO_VIEW_H_
|
||||
#define API_AUDIO_AUDIO_VIEW_H_
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/audio/channel_layout.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// This file contains 3 types of view classes:
|
||||
//
|
||||
// * MonoView<>: A single channel contiguous buffer of samples.
|
||||
//
|
||||
// * InterleavedView<>: Channel samples are interleaved (side-by-side) in
|
||||
// the buffer. A single channel InterleavedView<> is the same thing as a
|
||||
// MonoView<>
|
||||
//
|
||||
// * DeinterleavedView<>: Each channel's samples are contiguous within the
|
||||
// buffer. Channels can be enumerated and accessing the individual channel
|
||||
// data is done via MonoView<>.
|
||||
//
|
||||
// The views are comparable to and built on rtc::ArrayView<> but add
|
||||
// audio specific properties for the dimensions of the buffer and the above
|
||||
// specialized [de]interleaved support.
|
||||
//
|
||||
// There are also a few generic utility functions that can simplify
|
||||
// generic code for supporting more than one type of view.
|
||||
|
||||
// MonoView<> represents a view over a single contiguous, audio buffer. This
|
||||
// can be either an single channel (mono) interleaved buffer (e.g. AudioFrame),
|
||||
// or a de-interleaved channel (e.g. from AudioBuffer).
|
||||
template <typename T>
|
||||
using MonoView = rtc::ArrayView<T>;
|
||||
|
||||
// InterleavedView<> is a view over an interleaved audio buffer (e.g. from
|
||||
// AudioFrame).
|
||||
template <typename T>
|
||||
class InterleavedView {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
InterleavedView() = default;
|
||||
|
||||
template <typename U>
|
||||
InterleavedView(U* data, size_t samples_per_channel, size_t num_channels)
|
||||
: num_channels_(num_channels),
|
||||
samples_per_channel_(samples_per_channel),
|
||||
data_(data, num_channels * samples_per_channel) {
|
||||
RTC_DCHECK_LE(num_channels_, kMaxConcurrentChannels);
|
||||
RTC_DCHECK(num_channels_ == 0u || samples_per_channel_ != 0u);
|
||||
}
|
||||
|
||||
// Construct an InterleavedView from a C-style array. Samples per channels
|
||||
// is calculated based on the array size / num_channels.
|
||||
template <typename U, size_t N>
|
||||
InterleavedView(U (&array)[N], // NOLINT
|
||||
size_t num_channels)
|
||||
: InterleavedView(array, N / num_channels, num_channels) {
|
||||
RTC_DCHECK_EQ(N % num_channels, 0u);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
InterleavedView(const InterleavedView<U>& other)
|
||||
: num_channels_(other.num_channels()),
|
||||
samples_per_channel_(other.samples_per_channel()),
|
||||
data_(other.data()) {}
|
||||
|
||||
size_t num_channels() const { return num_channels_; }
|
||||
size_t samples_per_channel() const { return samples_per_channel_; }
|
||||
rtc::ArrayView<T> data() const { return data_; }
|
||||
bool empty() const { return data_.empty(); }
|
||||
size_t size() const { return data_.size(); }
|
||||
|
||||
MonoView<T> AsMono() const {
|
||||
RTC_DCHECK_EQ(num_channels(), 1u);
|
||||
RTC_DCHECK_EQ(data_.size(), samples_per_channel_);
|
||||
return data_;
|
||||
}
|
||||
|
||||
// A simple wrapper around memcpy that includes checks for properties.
|
||||
// TODO(tommi): Consider if this can be utility function for both interleaved
|
||||
// and deinterleaved views.
|
||||
template <typename U>
|
||||
void CopyFrom(const InterleavedView<U>& source) {
|
||||
static_assert(sizeof(T) == sizeof(U), "");
|
||||
RTC_DCHECK_EQ(num_channels(), source.num_channels());
|
||||
RTC_DCHECK_EQ(samples_per_channel(), source.samples_per_channel());
|
||||
RTC_DCHECK_GE(data_.size(), source.data().size());
|
||||
const auto data = source.data();
|
||||
memcpy(&data_[0], &data[0], data.size() * sizeof(U));
|
||||
}
|
||||
|
||||
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<T*> rbegin() const { return data_.rbegin(); }
|
||||
std::reverse_iterator<T*> rend() const { return data_.rend(); }
|
||||
std::reverse_iterator<const T*> crbegin() const { return data_.crbegin(); }
|
||||
std::reverse_iterator<const T*> crend() const { return data_.crend(); }
|
||||
|
||||
private:
|
||||
// TODO(tommi): Consider having these both be stored as uint16_t to
|
||||
// save a few bytes per view. Use `dchecked_cast` to support size_t during
|
||||
// construction.
|
||||
size_t num_channels_ = 0u;
|
||||
size_t samples_per_channel_ = 0u;
|
||||
rtc::ArrayView<T> data_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class DeinterleavedView {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
DeinterleavedView() = default;
|
||||
|
||||
template <typename U>
|
||||
DeinterleavedView(U* data, size_t samples_per_channel, size_t num_channels)
|
||||
: num_channels_(num_channels),
|
||||
samples_per_channel_(samples_per_channel),
|
||||
data_(data, num_channels * samples_per_channel_) {}
|
||||
|
||||
template <typename U>
|
||||
DeinterleavedView(const DeinterleavedView<U>& other)
|
||||
: num_channels_(other.num_channels()),
|
||||
samples_per_channel_(other.samples_per_channel()),
|
||||
data_(other.data()) {}
|
||||
|
||||
// Returns a deinterleaved channel where `idx` is the zero based index,
|
||||
// in the range [0 .. num_channels()-1].
|
||||
MonoView<T> operator[](size_t idx) const {
|
||||
RTC_DCHECK_LT(idx, num_channels_);
|
||||
return MonoView<T>(&data_[idx * samples_per_channel_],
|
||||
samples_per_channel_);
|
||||
}
|
||||
|
||||
size_t num_channels() const { return num_channels_; }
|
||||
size_t samples_per_channel() const { return samples_per_channel_; }
|
||||
rtc::ArrayView<T> data() const { return data_; }
|
||||
bool empty() const { return data_.empty(); }
|
||||
size_t size() const { return data_.size(); }
|
||||
|
||||
// Returns the first (and possibly only) channel.
|
||||
MonoView<T> AsMono() const {
|
||||
RTC_DCHECK_GE(num_channels(), 1u);
|
||||
return (*this)[0];
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO(tommi): Consider having these be stored as uint16_t to save a few
|
||||
// bytes per view. Use `dchecked_cast` to support size_t during construction.
|
||||
size_t num_channels_ = 0u;
|
||||
size_t samples_per_channel_ = 0u;
|
||||
rtc::ArrayView<T> data_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr size_t NumChannels(const MonoView<T>& view) {
|
||||
return 1u;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t NumChannels(const InterleavedView<T>& view) {
|
||||
return view.num_channels();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t NumChannels(const DeinterleavedView<T>& view) {
|
||||
return view.num_channels();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool IsMono(const MonoView<T>& view) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool IsInterleavedView(const MonoView<T>& view) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool IsInterleavedView(const InterleavedView<T>& view) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool IsInterleavedView(const DeinterleavedView<const T>& view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool IsMono(const InterleavedView<T>& view) {
|
||||
return NumChannels(view) == 1u;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool IsMono(const DeinterleavedView<T>& view) {
|
||||
return NumChannels(view) == 1u;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t SamplesPerChannel(const MonoView<T>& view) {
|
||||
return view.size();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t SamplesPerChannel(const InterleavedView<T>& view) {
|
||||
return view.samples_per_channel();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t SamplesPerChannel(const DeinterleavedView<T>& view) {
|
||||
return view.samples_per_channel();
|
||||
}
|
||||
// A simple wrapper around memcpy that includes checks for properties.
|
||||
// The parameter order is the same as for memcpy(), first destination then
|
||||
// source.
|
||||
template <typename D, typename S>
|
||||
void CopySamples(D& destination, const S& source) {
|
||||
static_assert(
|
||||
sizeof(typename D::value_type) == sizeof(typename S::value_type), "");
|
||||
// Here we'd really like to do
|
||||
// static_assert(IsInterleavedView(destination) == IsInterleavedView(source),
|
||||
// "");
|
||||
// but the compiler doesn't like it inside this template function for
|
||||
// some reason. The following check is an approximation but unfortunately
|
||||
// means that copying between a MonoView and single channel interleaved or
|
||||
// deinterleaved views wouldn't work.
|
||||
// static_assert(sizeof(destination) == sizeof(source),
|
||||
// "Incompatible view types");
|
||||
RTC_DCHECK_EQ(NumChannels(destination), NumChannels(source));
|
||||
RTC_DCHECK_EQ(SamplesPerChannel(destination), SamplesPerChannel(source));
|
||||
RTC_DCHECK_GE(destination.size(), source.size());
|
||||
memcpy(&destination[0], &source[0],
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
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_
|
@ -275,7 +275,7 @@ const char* ChannelLayoutToString(ChannelLayout layout) {
|
||||
case CHANNEL_LAYOUT_BITSTREAM:
|
||||
return "BITSTREAM";
|
||||
}
|
||||
RTC_NOTREACHED() << "Invalid channel layout provided: " << layout;
|
||||
RTC_DCHECK_NOTREACHED() << "Invalid channel layout provided: " << layout;
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -153,6 +153,7 @@ bool EchoCanceller3Config::Validate(EchoCanceller3Config* config) {
|
||||
|
||||
res = res & Limit(&c->filter.config_change_duration_blocks, 0, 100000);
|
||||
res = res & Limit(&c->filter.initial_state_seconds, 0.f, 100.f);
|
||||
res = res & Limit(&c->filter.coarse_reset_hangover_blocks, 0, 250000);
|
||||
|
||||
res = res & Limit(&c->erle.min, 1.f, 100000.f);
|
||||
res = res & Limit(&c->erle.max_l, 1.f, 100000.f);
|
||||
@ -165,6 +166,7 @@ bool EchoCanceller3Config::Validate(EchoCanceller3Config* config) {
|
||||
|
||||
res = res & Limit(&c->ep_strength.default_gain, 0.f, 1000000.f);
|
||||
res = res & Limit(&c->ep_strength.default_len, -1.f, 1.f);
|
||||
res = res & Limit(&c->ep_strength.nearend_len, -1.0f, 1.0f);
|
||||
|
||||
res =
|
||||
res & Limit(&c->echo_audibility.low_render_limit, 0.f, 32768.f * 32768.f);
|
||||
@ -228,6 +230,12 @@ bool EchoCanceller3Config::Validate(EchoCanceller3Config* config) {
|
||||
res =
|
||||
res & Limit(&c->suppressor.nearend_tuning.max_dec_factor_lf, 0.f, 100.f);
|
||||
|
||||
res = res & Limit(&c->suppressor.last_permanent_lf_smoothing_band, 0, 64);
|
||||
res = res & Limit(&c->suppressor.last_lf_smoothing_band, 0, 64);
|
||||
res = res & Limit(&c->suppressor.last_lf_band, 0, 63);
|
||||
res = res &
|
||||
Limit(&c->suppressor.first_hf_band, c->suppressor.last_lf_band + 1, 64);
|
||||
|
||||
res = res & Limit(&c->suppressor.dominant_nearend_detection.enr_threshold,
|
||||
0.f, 1000000.f);
|
||||
res = res & Limit(&c->suppressor.dominant_nearend_detection.snr_threshold,
|
||||
|
@ -43,6 +43,7 @@ struct RTC_EXPORT EchoCanceller3Config {
|
||||
size_t hysteresis_limit_blocks = 1;
|
||||
size_t fixed_capture_delay_samples = 0;
|
||||
float delay_estimate_smoothing = 0.7f;
|
||||
float delay_estimate_smoothing_delay_found = 0.7f;
|
||||
float delay_candidate_detection_threshold = 0.2f;
|
||||
struct DelaySelectionThresholds {
|
||||
int initial;
|
||||
@ -58,6 +59,7 @@ struct RTC_EXPORT EchoCanceller3Config {
|
||||
};
|
||||
AlignmentMixing render_alignment_mixing = {false, true, 10000.f, true};
|
||||
AlignmentMixing capture_alignment_mixing = {false, true, 10000.f, false};
|
||||
bool detect_pre_echo = true;
|
||||
} delay;
|
||||
|
||||
struct Filter {
|
||||
@ -86,9 +88,11 @@ struct RTC_EXPORT EchoCanceller3Config {
|
||||
|
||||
size_t config_change_duration_blocks = 250;
|
||||
float initial_state_seconds = 2.5f;
|
||||
int coarse_reset_hangover_blocks = 25;
|
||||
bool conservative_initial_phase = false;
|
||||
bool enable_coarse_filter_output_usage = true;
|
||||
bool use_linear_filter = true;
|
||||
bool high_pass_filter_echo_reference = false;
|
||||
bool export_linear_aec_output = false;
|
||||
} filter;
|
||||
|
||||
@ -105,8 +109,11 @@ struct RTC_EXPORT EchoCanceller3Config {
|
||||
struct EpStrength {
|
||||
float default_gain = 1.f;
|
||||
float default_len = 0.83f;
|
||||
float nearend_len = 0.83f;
|
||||
bool echo_can_saturate = true;
|
||||
bool bounded_erl = false;
|
||||
bool erle_onset_compensation_in_dominant_nearend = false;
|
||||
bool use_conservative_tail_frequency_response = true;
|
||||
} ep_strength;
|
||||
|
||||
struct EchoAudibility {
|
||||
@ -190,6 +197,12 @@ struct RTC_EXPORT EchoCanceller3Config {
|
||||
2.0f,
|
||||
0.25f);
|
||||
|
||||
bool lf_smoothing_during_initial_phase = true;
|
||||
int last_permanent_lf_smoothing_band = 0;
|
||||
int last_lf_smoothing_band = 5;
|
||||
int last_lf_band = 5;
|
||||
int first_hf_band = 8;
|
||||
|
||||
struct DominantNearendDetection {
|
||||
float enr_threshold = .25f;
|
||||
float enr_exit_threshold = 10.f;
|
||||
@ -197,6 +210,7 @@ struct RTC_EXPORT EchoCanceller3Config {
|
||||
int hold_duration = 50;
|
||||
int trigger_threshold = 12;
|
||||
bool use_during_initial_phase = true;
|
||||
bool use_unbounded_echo_spectrum = true;
|
||||
} dominant_nearend_detection;
|
||||
|
||||
struct SubbandNearendDetection {
|
||||
@ -221,7 +235,15 @@ struct RTC_EXPORT EchoCanceller3Config {
|
||||
} high_bands_suppression;
|
||||
|
||||
float floor_first_increase = 0.00001f;
|
||||
bool conservative_hf_suppression = false;
|
||||
} suppressor;
|
||||
|
||||
struct MultiChannel {
|
||||
bool detect_stereo_content = true;
|
||||
float stereo_detection_threshold = 0.0f;
|
||||
int stereo_detection_timeout_threshold_seconds = 300;
|
||||
float stereo_detection_hysteresis_seconds = 2.0f;
|
||||
} multi_channel;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -48,6 +48,13 @@ class EchoControl {
|
||||
// Provides an optional external estimate of the audio buffer delay.
|
||||
virtual void SetAudioBufferDelay(int delay_ms) = 0;
|
||||
|
||||
// Specifies whether the capture output will be used. The purpose of this is
|
||||
// to allow the echo controller to deactivate some of the processing when the
|
||||
// resulting output is anyway not used, for instance when the endpoint is
|
||||
// muted.
|
||||
// TODO(b/177830919): Make pure virtual.
|
||||
virtual void SetCaptureOutputUsage(bool capture_output_used) {}
|
||||
|
||||
// Returns wheter the signal is altered.
|
||||
virtual bool ActiveProcessing() const = 0;
|
||||
|
||||
|
@ -1,170 +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 "api/audio_codecs/audio_decoder.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/sanitizer.h"
|
||||
#include "rtc_base/trace_event.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
class OldStyleEncodedFrame final : public AudioDecoder::EncodedAudioFrame {
|
||||
public:
|
||||
OldStyleEncodedFrame(AudioDecoder* decoder, rtc::Buffer&& payload)
|
||||
: decoder_(decoder), payload_(std::move(payload)) {}
|
||||
|
||||
size_t Duration() const override {
|
||||
const int ret = decoder_->PacketDuration(payload_.data(), payload_.size());
|
||||
return ret < 0 ? 0 : static_cast<size_t>(ret);
|
||||
}
|
||||
|
||||
absl::optional<DecodeResult> Decode(
|
||||
rtc::ArrayView<int16_t> decoded) const override {
|
||||
auto speech_type = AudioDecoder::kSpeech;
|
||||
const int ret = decoder_->Decode(
|
||||
payload_.data(), payload_.size(), decoder_->SampleRateHz(),
|
||||
decoded.size() * sizeof(int16_t), decoded.data(), &speech_type);
|
||||
return ret < 0 ? absl::nullopt
|
||||
: absl::optional<DecodeResult>(
|
||||
{static_cast<size_t>(ret), speech_type});
|
||||
}
|
||||
|
||||
private:
|
||||
AudioDecoder* const decoder_;
|
||||
const rtc::Buffer payload_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool AudioDecoder::EncodedAudioFrame::IsDtxPacket() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioDecoder::ParseResult::ParseResult() = default;
|
||||
AudioDecoder::ParseResult::ParseResult(ParseResult&& b) = default;
|
||||
AudioDecoder::ParseResult::ParseResult(uint32_t timestamp,
|
||||
int priority,
|
||||
std::unique_ptr<EncodedAudioFrame> frame)
|
||||
: timestamp(timestamp), priority(priority), frame(std::move(frame)) {
|
||||
RTC_DCHECK_GE(priority, 0);
|
||||
}
|
||||
|
||||
AudioDecoder::ParseResult::~ParseResult() = default;
|
||||
|
||||
AudioDecoder::ParseResult& AudioDecoder::ParseResult::operator=(
|
||||
ParseResult&& b) = default;
|
||||
|
||||
std::vector<AudioDecoder::ParseResult> AudioDecoder::ParsePayload(
|
||||
rtc::Buffer&& payload,
|
||||
uint32_t timestamp) {
|
||||
std::vector<ParseResult> results;
|
||||
std::unique_ptr<EncodedAudioFrame> frame(
|
||||
new OldStyleEncodedFrame(this, std::move(payload)));
|
||||
results.emplace_back(timestamp, 0, std::move(frame));
|
||||
return results;
|
||||
}
|
||||
|
||||
int AudioDecoder::Decode(const uint8_t* encoded,
|
||||
size_t encoded_len,
|
||||
int sample_rate_hz,
|
||||
size_t max_decoded_bytes,
|
||||
int16_t* decoded,
|
||||
SpeechType* speech_type) {
|
||||
TRACE_EVENT0("webrtc", "AudioDecoder::Decode");
|
||||
rtc::MsanCheckInitialized(rtc::MakeArrayView(encoded, encoded_len));
|
||||
int duration = PacketDuration(encoded, encoded_len);
|
||||
if (duration >= 0 &&
|
||||
duration * Channels() * sizeof(int16_t) > max_decoded_bytes) {
|
||||
return -1;
|
||||
}
|
||||
return DecodeInternal(encoded, encoded_len, sample_rate_hz, decoded,
|
||||
speech_type);
|
||||
}
|
||||
|
||||
int AudioDecoder::DecodeRedundant(const uint8_t* encoded,
|
||||
size_t encoded_len,
|
||||
int sample_rate_hz,
|
||||
size_t max_decoded_bytes,
|
||||
int16_t* decoded,
|
||||
SpeechType* speech_type) {
|
||||
TRACE_EVENT0("webrtc", "AudioDecoder::DecodeRedundant");
|
||||
rtc::MsanCheckInitialized(rtc::MakeArrayView(encoded, encoded_len));
|
||||
int duration = PacketDurationRedundant(encoded, encoded_len);
|
||||
if (duration >= 0 &&
|
||||
duration * Channels() * sizeof(int16_t) > max_decoded_bytes) {
|
||||
return -1;
|
||||
}
|
||||
return DecodeRedundantInternal(encoded, encoded_len, sample_rate_hz, decoded,
|
||||
speech_type);
|
||||
}
|
||||
|
||||
int AudioDecoder::DecodeRedundantInternal(const uint8_t* encoded,
|
||||
size_t encoded_len,
|
||||
int sample_rate_hz,
|
||||
int16_t* decoded,
|
||||
SpeechType* speech_type) {
|
||||
return DecodeInternal(encoded, encoded_len, sample_rate_hz, decoded,
|
||||
speech_type);
|
||||
}
|
||||
|
||||
bool AudioDecoder::HasDecodePlc() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t AudioDecoder::DecodePlc(size_t num_frames, int16_t* decoded) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO(bugs.webrtc.org/9676): Remove default implementation.
|
||||
void AudioDecoder::GeneratePlc(size_t /*requested_samples_per_channel*/,
|
||||
rtc::BufferT<int16_t>* /*concealment_audio*/) {}
|
||||
|
||||
int AudioDecoder::ErrorCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AudioDecoder::PacketDuration(const uint8_t* encoded,
|
||||
size_t encoded_len) const {
|
||||
return kNotImplemented;
|
||||
}
|
||||
|
||||
int AudioDecoder::PacketDurationRedundant(const uint8_t* encoded,
|
||||
size_t encoded_len) const {
|
||||
return kNotImplemented;
|
||||
}
|
||||
|
||||
bool AudioDecoder::PacketHasFec(const uint8_t* encoded,
|
||||
size_t encoded_len) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioDecoder::SpeechType AudioDecoder::ConvertSpeechType(int16_t type) {
|
||||
switch (type) {
|
||||
case 0: // TODO(hlundin): Both iSAC and Opus return 0 for speech.
|
||||
case 1:
|
||||
return kSpeech;
|
||||
case 2:
|
||||
return kComfortNoise;
|
||||
default:
|
||||
assert(false);
|
||||
return kSpeech;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -1,193 +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 API_AUDIO_CODECS_AUDIO_DECODER_H_
|
||||
#define API_AUDIO_CODECS_AUDIO_DECODER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class AudioDecoder {
|
||||
public:
|
||||
enum SpeechType {
|
||||
kSpeech = 1,
|
||||
kComfortNoise = 2,
|
||||
};
|
||||
|
||||
// Used by PacketDuration below. Save the value -1 for errors.
|
||||
enum { kNotImplemented = -2 };
|
||||
|
||||
AudioDecoder() = default;
|
||||
virtual ~AudioDecoder() = default;
|
||||
|
||||
class EncodedAudioFrame {
|
||||
public:
|
||||
struct DecodeResult {
|
||||
size_t num_decoded_samples;
|
||||
SpeechType speech_type;
|
||||
};
|
||||
|
||||
virtual ~EncodedAudioFrame() = default;
|
||||
|
||||
// Returns the duration in samples-per-channel of this audio frame.
|
||||
// If no duration can be ascertained, returns zero.
|
||||
virtual size_t Duration() const = 0;
|
||||
|
||||
// Returns true if this packet contains DTX.
|
||||
virtual bool IsDtxPacket() const;
|
||||
|
||||
// Decodes this frame of audio and writes the result in |decoded|.
|
||||
// |decoded| must be large enough to store as many samples as indicated by a
|
||||
// call to Duration() . On success, returns an absl::optional containing the
|
||||
// total number of samples across all channels, as well as whether the
|
||||
// decoder produced comfort noise or speech. On failure, returns an empty
|
||||
// absl::optional. Decode may be called at most once per frame object.
|
||||
virtual absl::optional<DecodeResult> Decode(
|
||||
rtc::ArrayView<int16_t> decoded) const = 0;
|
||||
};
|
||||
|
||||
struct ParseResult {
|
||||
ParseResult();
|
||||
ParseResult(uint32_t timestamp,
|
||||
int priority,
|
||||
std::unique_ptr<EncodedAudioFrame> frame);
|
||||
ParseResult(ParseResult&& b);
|
||||
~ParseResult();
|
||||
|
||||
ParseResult& operator=(ParseResult&& b);
|
||||
|
||||
// The timestamp of the frame is in samples per channel.
|
||||
uint32_t timestamp;
|
||||
// The relative priority of the frame compared to other frames of the same
|
||||
// payload and the same timeframe. A higher value means a lower priority.
|
||||
// The highest priority is zero - negative values are not allowed.
|
||||
int priority;
|
||||
std::unique_ptr<EncodedAudioFrame> frame;
|
||||
};
|
||||
|
||||
// Let the decoder parse this payload and prepare zero or more decodable
|
||||
// frames. Each frame must be between 10 ms and 120 ms long. The caller must
|
||||
// ensure that the AudioDecoder object outlives any frame objects returned by
|
||||
// this call. The decoder is free to swap or move the data from the |payload|
|
||||
// buffer. |timestamp| is the input timestamp, in samples, corresponding to
|
||||
// the start of the payload.
|
||||
virtual std::vector<ParseResult> ParsePayload(rtc::Buffer&& payload,
|
||||
uint32_t timestamp);
|
||||
|
||||
// TODO(bugs.webrtc.org/10098): The Decode and DecodeRedundant methods are
|
||||
// obsolete; callers should call ParsePayload instead. For now, subclasses
|
||||
// must still implement DecodeInternal.
|
||||
|
||||
// Decodes |encode_len| bytes from |encoded| and writes the result in
|
||||
// |decoded|. The maximum bytes allowed to be written into |decoded| is
|
||||
// |max_decoded_bytes|. Returns the total number of samples across all
|
||||
// channels. If the decoder produced comfort noise, |speech_type|
|
||||
// is set to kComfortNoise, otherwise it is kSpeech. The desired output
|
||||
// sample rate is provided in |sample_rate_hz|, which must be valid for the
|
||||
// codec at hand.
|
||||
int Decode(const uint8_t* encoded,
|
||||
size_t encoded_len,
|
||||
int sample_rate_hz,
|
||||
size_t max_decoded_bytes,
|
||||
int16_t* decoded,
|
||||
SpeechType* speech_type);
|
||||
|
||||
// Same as Decode(), but interfaces to the decoders redundant decode function.
|
||||
// The default implementation simply calls the regular Decode() method.
|
||||
int DecodeRedundant(const uint8_t* encoded,
|
||||
size_t encoded_len,
|
||||
int sample_rate_hz,
|
||||
size_t max_decoded_bytes,
|
||||
int16_t* decoded,
|
||||
SpeechType* speech_type);
|
||||
|
||||
// Indicates if the decoder implements the DecodePlc method.
|
||||
virtual bool HasDecodePlc() const;
|
||||
|
||||
// Calls the packet-loss concealment of the decoder to update the state after
|
||||
// one or several lost packets. The caller has to make sure that the
|
||||
// memory allocated in |decoded| should accommodate |num_frames| frames.
|
||||
virtual size_t DecodePlc(size_t num_frames, int16_t* decoded);
|
||||
|
||||
// Asks the decoder to generate packet-loss concealment and append it to the
|
||||
// end of |concealment_audio|. The concealment audio should be in
|
||||
// channel-interleaved format, with as many channels as the last decoded
|
||||
// packet produced. The implementation must produce at least
|
||||
// requested_samples_per_channel, or nothing at all. This is a signal to the
|
||||
// caller to conceal the loss with other means. If the implementation provides
|
||||
// concealment samples, it is also responsible for "stitching" it together
|
||||
// with the decoded audio on either side of the concealment.
|
||||
// Note: The default implementation of GeneratePlc will be deleted soon. All
|
||||
// implementations must provide their own, which can be a simple as a no-op.
|
||||
// TODO(bugs.webrtc.org/9676): Remove default impementation.
|
||||
virtual void GeneratePlc(size_t requested_samples_per_channel,
|
||||
rtc::BufferT<int16_t>* concealment_audio);
|
||||
|
||||
// Resets the decoder state (empty buffers etc.).
|
||||
virtual void Reset() = 0;
|
||||
|
||||
// Returns the last error code from the decoder.
|
||||
virtual int ErrorCode();
|
||||
|
||||
// Returns the duration in samples-per-channel of the payload in |encoded|
|
||||
// which is |encoded_len| bytes long. Returns kNotImplemented if no duration
|
||||
// estimate is available, or -1 in case of an error.
|
||||
virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
|
||||
|
||||
// Returns the duration in samples-per-channel of the redandant payload in
|
||||
// |encoded| which is |encoded_len| bytes long. Returns kNotImplemented if no
|
||||
// duration estimate is available, or -1 in case of an error.
|
||||
virtual int PacketDurationRedundant(const uint8_t* encoded,
|
||||
size_t encoded_len) const;
|
||||
|
||||
// Detects whether a packet has forward error correction. The packet is
|
||||
// comprised of the samples in |encoded| which is |encoded_len| bytes long.
|
||||
// Returns true if the packet has FEC and false otherwise.
|
||||
virtual bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const;
|
||||
|
||||
// Returns the actual sample rate of the decoder's output. This value may not
|
||||
// change during the lifetime of the decoder.
|
||||
virtual int SampleRateHz() const = 0;
|
||||
|
||||
// The number of channels in the decoder's output. This value may not change
|
||||
// during the lifetime of the decoder.
|
||||
virtual size_t Channels() const = 0;
|
||||
|
||||
protected:
|
||||
static SpeechType ConvertSpeechType(int16_t type);
|
||||
|
||||
virtual int DecodeInternal(const uint8_t* encoded,
|
||||
size_t encoded_len,
|
||||
int sample_rate_hz,
|
||||
int16_t* decoded,
|
||||
SpeechType* speech_type) = 0;
|
||||
|
||||
virtual int DecodeRedundantInternal(const uint8_t* encoded,
|
||||
size_t encoded_len,
|
||||
int sample_rate_hz,
|
||||
int16_t* decoded,
|
||||
SpeechType* speech_type);
|
||||
|
||||
private:
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoder);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // API_AUDIO_CODECS_AUDIO_DECODER_H_
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "api/audio_codecs/audio_encoder.h"
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/trace_event.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
ANAStats::ANAStats() = default;
|
||||
ANAStats::~ANAStats() = default;
|
||||
ANAStats::ANAStats(const ANAStats&) = default;
|
||||
|
||||
AudioEncoder::EncodedInfo::EncodedInfo() = default;
|
||||
AudioEncoder::EncodedInfo::EncodedInfo(const EncodedInfo&) = default;
|
||||
AudioEncoder::EncodedInfo::EncodedInfo(EncodedInfo&&) = default;
|
||||
AudioEncoder::EncodedInfo::~EncodedInfo() = default;
|
||||
AudioEncoder::EncodedInfo& AudioEncoder::EncodedInfo::operator=(
|
||||
const EncodedInfo&) = default;
|
||||
AudioEncoder::EncodedInfo& AudioEncoder::EncodedInfo::operator=(EncodedInfo&&) =
|
||||
default;
|
||||
|
||||
int AudioEncoder::RtpTimestampRateHz() const {
|
||||
return SampleRateHz();
|
||||
}
|
||||
|
||||
AudioEncoder::EncodedInfo AudioEncoder::Encode(
|
||||
uint32_t rtp_timestamp,
|
||||
rtc::ArrayView<const int16_t> audio,
|
||||
rtc::Buffer* encoded) {
|
||||
TRACE_EVENT0("webrtc", "AudioEncoder::Encode");
|
||||
RTC_CHECK_EQ(audio.size(),
|
||||
static_cast<size_t>(NumChannels() * SampleRateHz() / 100));
|
||||
|
||||
const size_t old_size = encoded->size();
|
||||
EncodedInfo info = EncodeImpl(rtp_timestamp, audio, encoded);
|
||||
RTC_CHECK_EQ(encoded->size() - old_size, info.encoded_bytes);
|
||||
return info;
|
||||
}
|
||||
|
||||
bool AudioEncoder::SetFec(bool enable) {
|
||||
return !enable;
|
||||
}
|
||||
|
||||
bool AudioEncoder::SetDtx(bool enable) {
|
||||
return !enable;
|
||||
}
|
||||
|
||||
bool AudioEncoder::GetDtx() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AudioEncoder::SetApplication(Application application) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioEncoder::SetMaxPlaybackRate(int frequency_hz) {}
|
||||
|
||||
void AudioEncoder::SetTargetBitrate(int target_bps) {}
|
||||
|
||||
rtc::ArrayView<std::unique_ptr<AudioEncoder>>
|
||||
AudioEncoder::ReclaimContainedEncoders() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AudioEncoder::EnableAudioNetworkAdaptor(const std::string& config_string,
|
||||
RtcEventLog* event_log) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioEncoder::DisableAudioNetworkAdaptor() {}
|
||||
|
||||
void AudioEncoder::OnReceivedUplinkPacketLossFraction(
|
||||
float uplink_packet_loss_fraction) {}
|
||||
|
||||
void AudioEncoder::OnReceivedUplinkRecoverablePacketLossFraction(
|
||||
float uplink_recoverable_packet_loss_fraction) {
|
||||
RTC_NOTREACHED();
|
||||
}
|
||||
|
||||
void AudioEncoder::OnReceivedTargetAudioBitrate(int target_audio_bitrate_bps) {
|
||||
OnReceivedUplinkBandwidth(target_audio_bitrate_bps, absl::nullopt);
|
||||
}
|
||||
|
||||
void AudioEncoder::OnReceivedUplinkBandwidth(
|
||||
int target_audio_bitrate_bps,
|
||||
absl::optional<int64_t> bwe_period_ms) {}
|
||||
|
||||
void AudioEncoder::OnReceivedUplinkAllocation(BitrateAllocationUpdate update) {
|
||||
OnReceivedUplinkBandwidth(update.target_bitrate.bps(),
|
||||
update.bwe_period.ms());
|
||||
}
|
||||
|
||||
void AudioEncoder::OnReceivedRtt(int rtt_ms) {}
|
||||
|
||||
void AudioEncoder::OnReceivedOverhead(size_t overhead_bytes_per_packet) {}
|
||||
|
||||
void AudioEncoder::SetReceiverFrameLengthRange(int min_frame_length_ms,
|
||||
int max_frame_length_ms) {}
|
||||
|
||||
ANAStats AudioEncoder::GetANAStats() const {
|
||||
return ANAStats();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -1,257 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef API_AUDIO_CODECS_AUDIO_ENCODER_H_
|
||||
#define API_AUDIO_CODECS_AUDIO_ENCODER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/call/bitrate_allocation.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/deprecation.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtcEventLog;
|
||||
|
||||
// Statistics related to Audio Network Adaptation.
|
||||
struct ANAStats {
|
||||
ANAStats();
|
||||
ANAStats(const ANAStats&);
|
||||
~ANAStats();
|
||||
// Number of actions taken by the ANA bitrate controller since the start of
|
||||
// the call. If this value is not set, it indicates that the bitrate
|
||||
// controller is disabled.
|
||||
absl::optional<uint32_t> bitrate_action_counter;
|
||||
// Number of actions taken by the ANA channel controller since the start of
|
||||
// the call. If this value is not set, it indicates that the channel
|
||||
// controller is disabled.
|
||||
absl::optional<uint32_t> channel_action_counter;
|
||||
// Number of actions taken by the ANA DTX controller since the start of the
|
||||
// call. If this value is not set, it indicates that the DTX controller is
|
||||
// disabled.
|
||||
absl::optional<uint32_t> dtx_action_counter;
|
||||
// Number of actions taken by the ANA FEC controller since the start of the
|
||||
// call. If this value is not set, it indicates that the FEC controller is
|
||||
// disabled.
|
||||
absl::optional<uint32_t> fec_action_counter;
|
||||
// Number of times the ANA frame length controller decided to increase the
|
||||
// frame length since the start of the call. If this value is not set, it
|
||||
// indicates that the frame length controller is disabled.
|
||||
absl::optional<uint32_t> frame_length_increase_counter;
|
||||
// Number of times the ANA frame length controller decided to decrease the
|
||||
// frame length since the start of the call. If this value is not set, it
|
||||
// indicates that the frame length controller is disabled.
|
||||
absl::optional<uint32_t> frame_length_decrease_counter;
|
||||
// The uplink packet loss fractions as set by the ANA FEC controller. If this
|
||||
// value is not set, it indicates that the ANA FEC controller is not active.
|
||||
absl::optional<float> uplink_packet_loss_fraction;
|
||||
};
|
||||
|
||||
// This is the interface class for encoders in AudioCoding module. Each codec
|
||||
// type must have an implementation of this class.
|
||||
class AudioEncoder {
|
||||
public:
|
||||
// Used for UMA logging of codec usage. The same codecs, with the
|
||||
// same values, must be listed in
|
||||
// src/tools/metrics/histograms/histograms.xml in chromium to log
|
||||
// correct values.
|
||||
enum class CodecType {
|
||||
kOther = 0, // Codec not specified, and/or not listed in this enum
|
||||
kOpus = 1,
|
||||
kIsac = 2,
|
||||
kPcmA = 3,
|
||||
kPcmU = 4,
|
||||
kG722 = 5,
|
||||
kIlbc = 6,
|
||||
|
||||
// Number of histogram bins in the UMA logging of codec types. The
|
||||
// total number of different codecs that are logged cannot exceed this
|
||||
// number.
|
||||
kMaxLoggedAudioCodecTypes
|
||||
};
|
||||
|
||||
struct EncodedInfoLeaf {
|
||||
size_t encoded_bytes = 0;
|
||||
uint32_t encoded_timestamp = 0;
|
||||
int payload_type = 0;
|
||||
bool send_even_if_empty = false;
|
||||
bool speech = true;
|
||||
CodecType encoder_type = CodecType::kOther;
|
||||
};
|
||||
|
||||
// This is the main struct for auxiliary encoding information. Each encoded
|
||||
// packet should be accompanied by one EncodedInfo struct, containing the
|
||||
// total number of |encoded_bytes|, the |encoded_timestamp| and the
|
||||
// |payload_type|. If the packet contains redundant encodings, the |redundant|
|
||||
// vector will be populated with EncodedInfoLeaf structs. Each struct in the
|
||||
// vector represents one encoding; the order of structs in the vector is the
|
||||
// same as the order in which the actual payloads are written to the byte
|
||||
// stream. When EncoderInfoLeaf structs are present in the vector, the main
|
||||
// struct's |encoded_bytes| will be the sum of all the |encoded_bytes| in the
|
||||
// vector.
|
||||
struct EncodedInfo : public EncodedInfoLeaf {
|
||||
EncodedInfo();
|
||||
EncodedInfo(const EncodedInfo&);
|
||||
EncodedInfo(EncodedInfo&&);
|
||||
~EncodedInfo();
|
||||
EncodedInfo& operator=(const EncodedInfo&);
|
||||
EncodedInfo& operator=(EncodedInfo&&);
|
||||
|
||||
std::vector<EncodedInfoLeaf> redundant;
|
||||
};
|
||||
|
||||
virtual ~AudioEncoder() = default;
|
||||
|
||||
// Returns the input sample rate in Hz and the number of input channels.
|
||||
// These are constants set at instantiation time.
|
||||
virtual int SampleRateHz() const = 0;
|
||||
virtual size_t NumChannels() const = 0;
|
||||
|
||||
// Returns the rate at which the RTP timestamps are updated. The default
|
||||
// implementation returns SampleRateHz().
|
||||
virtual int RtpTimestampRateHz() const;
|
||||
|
||||
// Returns the number of 10 ms frames the encoder will put in the next
|
||||
// packet. This value may only change when Encode() outputs a packet; i.e.,
|
||||
// the encoder may vary the number of 10 ms frames from packet to packet, but
|
||||
// it must decide the length of the next packet no later than when outputting
|
||||
// the preceding packet.
|
||||
virtual size_t Num10MsFramesInNextPacket() const = 0;
|
||||
|
||||
// Returns the maximum value that can be returned by
|
||||
// Num10MsFramesInNextPacket().
|
||||
virtual size_t Max10MsFramesInAPacket() const = 0;
|
||||
|
||||
// Returns the current target bitrate in bits/s. The value -1 means that the
|
||||
// codec adapts the target automatically, and a current target cannot be
|
||||
// provided.
|
||||
virtual int GetTargetBitrate() const = 0;
|
||||
|
||||
// Accepts one 10 ms block of input audio (i.e., SampleRateHz() / 100 *
|
||||
// NumChannels() samples). Multi-channel audio must be sample-interleaved.
|
||||
// The encoder appends zero or more bytes of output to |encoded| and returns
|
||||
// additional encoding information. Encode() checks some preconditions, calls
|
||||
// EncodeImpl() which does the actual work, and then checks some
|
||||
// postconditions.
|
||||
EncodedInfo Encode(uint32_t rtp_timestamp,
|
||||
rtc::ArrayView<const int16_t> audio,
|
||||
rtc::Buffer* encoded);
|
||||
|
||||
// Resets the encoder to its starting state, discarding any input that has
|
||||
// been fed to the encoder but not yet emitted in a packet.
|
||||
virtual void Reset() = 0;
|
||||
|
||||
// Enables or disables codec-internal FEC (forward error correction). Returns
|
||||
// true if the codec was able to comply. The default implementation returns
|
||||
// true when asked to disable FEC and false when asked to enable it (meaning
|
||||
// that FEC isn't supported).
|
||||
virtual bool SetFec(bool enable);
|
||||
|
||||
// Enables or disables codec-internal VAD/DTX. Returns true if the codec was
|
||||
// able to comply. The default implementation returns true when asked to
|
||||
// disable DTX and false when asked to enable it (meaning that DTX isn't
|
||||
// supported).
|
||||
virtual bool SetDtx(bool enable);
|
||||
|
||||
// Returns the status of codec-internal DTX. The default implementation always
|
||||
// returns false.
|
||||
virtual bool GetDtx() const;
|
||||
|
||||
// Sets the application mode. Returns true if the codec was able to comply.
|
||||
// The default implementation just returns false.
|
||||
enum class Application { kSpeech, kAudio };
|
||||
virtual bool SetApplication(Application application);
|
||||
|
||||
// Tells the encoder about the highest sample rate the decoder is expected to
|
||||
// use when decoding the bitstream. The encoder would typically use this
|
||||
// information to adjust the quality of the encoding. The default
|
||||
// implementation does nothing.
|
||||
virtual void SetMaxPlaybackRate(int frequency_hz);
|
||||
|
||||
// This is to be deprecated. Please use |OnReceivedTargetAudioBitrate|
|
||||
// instead.
|
||||
// Tells the encoder what average bitrate we'd like it to produce. The
|
||||
// encoder is free to adjust or disregard the given bitrate (the default
|
||||
// implementation does the latter).
|
||||
RTC_DEPRECATED virtual void SetTargetBitrate(int target_bps);
|
||||
|
||||
// Causes this encoder to let go of any other encoders it contains, and
|
||||
// returns a pointer to an array where they are stored (which is required to
|
||||
// live as long as this encoder). Unless the returned array is empty, you may
|
||||
// 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 rtc::ArrayView<std::unique_ptr<AudioEncoder>>
|
||||
ReclaimContainedEncoders();
|
||||
|
||||
// Enables audio network adaptor. Returns true if successful.
|
||||
virtual bool EnableAudioNetworkAdaptor(const std::string& config_string,
|
||||
RtcEventLog* event_log);
|
||||
|
||||
// Disables audio network adaptor.
|
||||
virtual void DisableAudioNetworkAdaptor();
|
||||
|
||||
// Provides uplink packet loss fraction to this encoder to allow it to adapt.
|
||||
// |uplink_packet_loss_fraction| is in the range [0.0, 1.0].
|
||||
virtual void OnReceivedUplinkPacketLossFraction(
|
||||
float uplink_packet_loss_fraction);
|
||||
|
||||
RTC_DEPRECATED virtual void OnReceivedUplinkRecoverablePacketLossFraction(
|
||||
float uplink_recoverable_packet_loss_fraction);
|
||||
|
||||
// Provides target audio bitrate to this encoder to allow it to adapt.
|
||||
virtual void OnReceivedTargetAudioBitrate(int target_bps);
|
||||
|
||||
// Provides target audio bitrate and corresponding probing interval of
|
||||
// the bandwidth estimator to this encoder to allow it to adapt.
|
||||
virtual void OnReceivedUplinkBandwidth(int target_audio_bitrate_bps,
|
||||
absl::optional<int64_t> bwe_period_ms);
|
||||
|
||||
// Provides target audio bitrate and corresponding probing interval of
|
||||
// the bandwidth estimator to this encoder to allow it to adapt.
|
||||
virtual void OnReceivedUplinkAllocation(BitrateAllocationUpdate update);
|
||||
|
||||
// Provides RTT to this encoder to allow it to adapt.
|
||||
virtual void OnReceivedRtt(int rtt_ms);
|
||||
|
||||
// Provides overhead to this encoder to adapt. The overhead is the number of
|
||||
// bytes that will be added to each packet the encoder generates.
|
||||
virtual void OnReceivedOverhead(size_t overhead_bytes_per_packet);
|
||||
|
||||
// To allow encoder to adapt its frame length, it must be provided the frame
|
||||
// length range that receivers can accept.
|
||||
virtual void SetReceiverFrameLengthRange(int min_frame_length_ms,
|
||||
int max_frame_length_ms);
|
||||
|
||||
// Get statistics related to audio network adaptation.
|
||||
virtual ANAStats GetANAStats() const;
|
||||
|
||||
// The range of frame lengths that are supported or nullopt if there's no sch
|
||||
// information. This is used to calculated the full bitrate range, including
|
||||
// overhead.
|
||||
virtual absl::optional<std::pair<TimeDelta, TimeDelta>> GetFrameLengthRange()
|
||||
const = 0;
|
||||
|
||||
protected:
|
||||
// Subclasses implement this to perform the actual encoding. Called by
|
||||
// Encode().
|
||||
virtual EncodedInfo EncodeImpl(uint32_t rtp_timestamp,
|
||||
rtc::ArrayView<const int16_t> audio,
|
||||
rtc::Buffer* encoded) = 0;
|
||||
};
|
||||
} // namespace webrtc
|
||||
#endif // API_AUDIO_CODECS_AUDIO_ENCODER_H_
|
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef API_CALL_BITRATE_ALLOCATION_H_
|
||||
#define API_CALL_BITRATE_ALLOCATION_H_
|
||||
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// BitrateAllocationUpdate provides information to allocated streams about their
|
||||
// bitrate allocation. It originates from the BitrateAllocater class and is
|
||||
// propagated from there.
|
||||
struct BitrateAllocationUpdate {
|
||||
// The allocated target bitrate. Media streams should produce this amount of
|
||||
// data. (Note that this may include packet overhead depending on
|
||||
// configuration.)
|
||||
DataRate target_bitrate = DataRate::Zero();
|
||||
// The allocated part of the estimated link capacity. This is more stable than
|
||||
// the target as it is based on the underlying link capacity estimate. This
|
||||
// should be used to change encoder configuration when the cost of change is
|
||||
// high.
|
||||
DataRate stable_target_bitrate = DataRate::Zero();
|
||||
// Predicted packet loss ratio.
|
||||
double packet_loss_ratio = 0;
|
||||
// Predicted round trip time.
|
||||
TimeDelta round_trip_time = TimeDelta::PlusInfinity();
|
||||
// |bwe_period| is deprecated, use |stable_target_bitrate| allocation instead.
|
||||
TimeDelta bwe_period = TimeDelta::PlusInfinity();
|
||||
// Congestion window pushback bitrate reduction fraction. Used in
|
||||
// VideoStreamEncoder to reduce the bitrate by the given fraction
|
||||
// by dropping frames.
|
||||
double cwnd_reduce_ratio = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_CALL_BITRATE_ALLOCATION_H_
|
@ -11,6 +11,7 @@
|
||||
#ifndef API_FUNCTION_VIEW_H_
|
||||
#define API_FUNCTION_VIEW_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
|
31
webrtc/api/location.h
Normal file
31
webrtc/api/location.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2023 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef API_LOCATION_H_
|
||||
#define API_LOCATION_H_
|
||||
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Location provides basic info where of an object was constructed, or was
|
||||
// significantly brought to life. This is a stripped down version of
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:base/location.h
|
||||
// that only specifies an interface compatible to how base::Location is
|
||||
// supposed to be used.
|
||||
// The declaration is overriden inside the Chromium build.
|
||||
class RTC_EXPORT Location {
|
||||
public:
|
||||
static Location Current() { return Location(); }
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_LOCATION_H_
|
130
webrtc/api/make_ref_counted.h
Normal file
130
webrtc/api/make_ref_counted.h
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2022 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef API_MAKE_REF_COUNTED_H_
|
||||
#define API_MAKE_REF_COUNTED_H_
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/nullability.h"
|
||||
#include "api/ref_count.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "rtc_base/ref_counted_object.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace webrtc_make_ref_counted_internal {
|
||||
// Determines if the given class has AddRef and Release methods.
|
||||
template <typename T>
|
||||
class HasAddRefAndRelease {
|
||||
private:
|
||||
template <typename C,
|
||||
decltype(std::declval<C>().AddRef())* = nullptr,
|
||||
decltype(std::declval<C>().Release())* = nullptr>
|
||||
static int Test(int);
|
||||
template <typename>
|
||||
static char Test(...);
|
||||
|
||||
public:
|
||||
static constexpr bool value = std::is_same_v<decltype(Test<T>(0)), int>;
|
||||
};
|
||||
} // namespace webrtc_make_ref_counted_internal
|
||||
|
||||
// General utilities for constructing a reference counted class and the
|
||||
// appropriate reference count implementation for that class.
|
||||
//
|
||||
// These utilities select either the `RefCountedObject` implementation or
|
||||
// `FinalRefCountedObject` depending on whether the to-be-shared class is
|
||||
// derived from the RefCountInterface interface or not (respectively).
|
||||
|
||||
// `make_ref_counted`:
|
||||
//
|
||||
// Use this when you want to construct a reference counted object of type T and
|
||||
// get a `scoped_refptr<>` back. Example:
|
||||
//
|
||||
// auto p = make_ref_counted<Foo>("bar", 123);
|
||||
//
|
||||
// For a class that inherits from RefCountInterface, this is equivalent to:
|
||||
//
|
||||
// auto p = scoped_refptr<Foo>(new RefCountedObject<Foo>("bar", 123));
|
||||
//
|
||||
// If the class does not inherit from RefCountInterface, but does have
|
||||
// AddRef/Release methods (so a T* is convertible to rtc::scoped_refptr), this
|
||||
// is equivalent to just
|
||||
//
|
||||
// auto p = scoped_refptr<Foo>(new Foo("bar", 123));
|
||||
//
|
||||
// Otherwise, the example is equivalent to:
|
||||
//
|
||||
// auto p = scoped_refptr<FinalRefCountedObject<Foo>>(
|
||||
// new FinalRefCountedObject<Foo>("bar", 123));
|
||||
//
|
||||
// In these cases, `make_ref_counted` reduces the amount of boilerplate code but
|
||||
// also helps with the most commonly intended usage of RefCountedObject whereby
|
||||
// methods for reference counting, are virtual and designed to satisfy the need
|
||||
// of an interface. When such a need does not exist, it is more efficient to use
|
||||
// the `FinalRefCountedObject` template, which does not add the vtable overhead.
|
||||
//
|
||||
// Note that in some cases, using RefCountedObject directly may still be what's
|
||||
// needed.
|
||||
|
||||
// `make_ref_counted` for abstract classes that are convertible to
|
||||
// RefCountInterface. The is_abstract requirement rejects classes that inherit
|
||||
// both RefCountInterface and RefCounted object, which is a a discouraged
|
||||
// pattern, and would result in double inheritance of RefCountedObject if this
|
||||
// template was applied.
|
||||
template <
|
||||
typename T,
|
||||
typename... Args,
|
||||
typename std::enable_if<std::is_convertible_v<T*, RefCountInterface*> &&
|
||||
std::is_abstract_v<T>,
|
||||
T>::type* = nullptr>
|
||||
absl::Nonnull<scoped_refptr<T>> make_ref_counted(Args&&... args) {
|
||||
return scoped_refptr<T>(new RefCountedObject<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
// `make_ref_counted` for complete classes that are not convertible to
|
||||
// RefCountInterface and already carry a ref count.
|
||||
template <
|
||||
typename T,
|
||||
typename... Args,
|
||||
typename std::enable_if<
|
||||
!std::is_convertible_v<T*, RefCountInterface*> &&
|
||||
webrtc_make_ref_counted_internal::HasAddRefAndRelease<T>::value,
|
||||
T>::type* = nullptr>
|
||||
absl::Nonnull<scoped_refptr<T>> make_ref_counted(Args&&... args) {
|
||||
return scoped_refptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
// `make_ref_counted` for complete classes that are not convertible to
|
||||
// RefCountInterface and have no ref count of their own.
|
||||
template <
|
||||
typename T,
|
||||
typename... Args,
|
||||
typename std::enable_if<
|
||||
!std::is_convertible_v<T*, RefCountInterface*> &&
|
||||
!webrtc_make_ref_counted_internal::HasAddRefAndRelease<T>::value,
|
||||
|
||||
T>::type* = nullptr>
|
||||
absl::Nonnull<scoped_refptr<FinalRefCountedObject<T>>> make_ref_counted(
|
||||
Args&&... args) {
|
||||
return scoped_refptr<FinalRefCountedObject<T>>(
|
||||
new FinalRefCountedObject<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
namespace rtc {
|
||||
// Backwards compatibe alias.
|
||||
// TODO: bugs.webrtc.org/42225969 - deprecate and remove.
|
||||
using ::webrtc::make_ref_counted;
|
||||
} // namespace rtc
|
||||
|
||||
#endif // API_MAKE_REF_COUNTED_H_
|
@ -1,14 +1,12 @@
|
||||
api_sources = [
|
||||
'audio/audio_frame.cc',
|
||||
'audio/audio_processing.cc',
|
||||
'audio/audio_processing_statistics.cc',
|
||||
'audio/channel_layout.cc',
|
||||
'audio/echo_canceller3_config.cc',
|
||||
'audio_codecs/audio_decoder.cc',
|
||||
'audio_codecs/audio_encoder.cc',
|
||||
'rtp_headers.cc',
|
||||
'rtp_packet_info.cc',
|
||||
'task_queue/task_queue_base.cc',
|
||||
'units/data_rate.cc',
|
||||
'units/data_size.cc',
|
||||
'units/frequency.cc',
|
||||
'units/time_delta.cc',
|
||||
'units/timestamp.cc',
|
||||
|
67
webrtc/api/ref_count.h
Normal file
67
webrtc/api/ref_count.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 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 API_REF_COUNT_H_
|
||||
#define API_REF_COUNT_H_
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Refcounted objects should implement the following informal interface:
|
||||
//
|
||||
// void AddRef() const ;
|
||||
// RefCountReleaseStatus Release() const;
|
||||
//
|
||||
// You may access members of a reference-counted object, including the AddRef()
|
||||
// and Release() methods, only if you already own a reference to it, or if
|
||||
// you're borrowing someone else's reference. (A newly created object is a
|
||||
// special case: the reference count is zero on construction, and the code that
|
||||
// creates the object should immediately call AddRef(), bringing the reference
|
||||
// count from zero to one, e.g., by constructing an rtc::scoped_refptr).
|
||||
//
|
||||
// AddRef() creates a new reference to the object.
|
||||
//
|
||||
// Release() releases a reference to the object; the caller now has one less
|
||||
// reference than before the call. Returns kDroppedLastRef if the number of
|
||||
// references dropped to zero because of this (in which case the object destroys
|
||||
// itself). Otherwise, returns kOtherRefsRemained, to signal that at the precise
|
||||
// time the caller's reference was dropped, other references still remained (but
|
||||
// if other threads own references, this may of course have changed by the time
|
||||
// Release() returns).
|
||||
//
|
||||
// The caller of Release() must treat it in the same way as a delete operation:
|
||||
// Regardless of the return value from Release(), the caller mustn't access the
|
||||
// object. The object might still be alive, due to references held by other
|
||||
// users of the object, but the object can go away at any time, e.g., as the
|
||||
// result of another thread calling Release().
|
||||
//
|
||||
// Calling AddRef() and Release() manually is discouraged. It's recommended to
|
||||
// use rtc::scoped_refptr to manage all pointers to reference counted objects.
|
||||
// Note that rtc::scoped_refptr depends on compile-time duck-typing; formally
|
||||
// implementing the below RefCountInterface is not required.
|
||||
|
||||
enum class RefCountReleaseStatus { kDroppedLastRef, kOtherRefsRemained };
|
||||
|
||||
// Interfaces where refcounting is part of the public api should
|
||||
// inherit this abstract interface. The implementation of these
|
||||
// methods is usually provided by the RefCountedObject template class,
|
||||
// applied as a leaf in the inheritance tree.
|
||||
class RefCountInterface {
|
||||
public:
|
||||
virtual void AddRef() const = 0;
|
||||
virtual RefCountReleaseStatus Release() const = 0;
|
||||
|
||||
// Non-public destructor, because Release() has exclusive responsibility for
|
||||
// destroying the object.
|
||||
protected:
|
||||
virtual ~RefCountInterface() {}
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_REF_COUNT_H_
|
@ -10,16 +10,20 @@
|
||||
#ifndef API_REF_COUNTED_BASE_H_
|
||||
#define API_REF_COUNTED_BASE_H_
|
||||
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/ref_count.h"
|
||||
#include <type_traits>
|
||||
|
||||
#include "api/ref_count.h"
|
||||
#include "rtc_base/ref_counter.h"
|
||||
|
||||
namespace rtc {
|
||||
namespace webrtc {
|
||||
|
||||
class RefCountedBase {
|
||||
public:
|
||||
RefCountedBase() = default;
|
||||
|
||||
RefCountedBase(const RefCountedBase&) = delete;
|
||||
RefCountedBase& operator=(const RefCountedBase&) = delete;
|
||||
|
||||
void AddRef() const { ref_count_.IncRef(); }
|
||||
RefCountReleaseStatus Release() const {
|
||||
const auto status = ref_count_.DecRef();
|
||||
@ -30,14 +34,74 @@ class RefCountedBase {
|
||||
}
|
||||
|
||||
protected:
|
||||
// Provided for internal webrtc subclasses for corner cases where it's
|
||||
// necessary to know whether or not a reference is exclusively held.
|
||||
bool HasOneRef() const { return ref_count_.HasOneRef(); }
|
||||
|
||||
virtual ~RefCountedBase() = default;
|
||||
|
||||
private:
|
||||
mutable webrtc::webrtc_impl::RefCounter ref_count_{0};
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedBase);
|
||||
};
|
||||
|
||||
// Template based version of `RefCountedBase` for simple implementations that do
|
||||
// not need (or want) destruction via virtual destructor or the overhead of a
|
||||
// vtable.
|
||||
//
|
||||
// To use:
|
||||
// struct MyInt : public rtc::RefCountedNonVirtual<MyInt> {
|
||||
// int foo_ = 0;
|
||||
// };
|
||||
//
|
||||
// rtc::scoped_refptr<MyInt> my_int(new MyInt());
|
||||
//
|
||||
// sizeof(MyInt) on a 32 bit system would then be 8, int + refcount and no
|
||||
// vtable generated.
|
||||
template <typename T>
|
||||
class RefCountedNonVirtual {
|
||||
public:
|
||||
RefCountedNonVirtual() = default;
|
||||
|
||||
RefCountedNonVirtual(const RefCountedNonVirtual&) = delete;
|
||||
RefCountedNonVirtual& operator=(const RefCountedNonVirtual&) = delete;
|
||||
|
||||
void AddRef() const { ref_count_.IncRef(); }
|
||||
RefCountReleaseStatus Release() const {
|
||||
// If you run into this assert, T has virtual methods. There are two
|
||||
// options:
|
||||
// 1) The class doesn't actually need virtual methods, the type is complete
|
||||
// so the virtual attribute(s) can be removed.
|
||||
// 2) The virtual methods are a part of the design of the class. In this
|
||||
// case you can consider using `RefCountedBase` instead or alternatively
|
||||
// use `rtc::RefCountedObject`.
|
||||
static_assert(!std::is_polymorphic<T>::value,
|
||||
"T has virtual methods. RefCountedBase is a better fit.");
|
||||
const auto status = ref_count_.DecRef();
|
||||
if (status == RefCountReleaseStatus::kDroppedLastRef) {
|
||||
delete static_cast<const T*>(this);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Provided for internal webrtc subclasses for corner cases where it's
|
||||
// necessary to know whether or not a reference is exclusively held.
|
||||
bool HasOneRef() const { return ref_count_.HasOneRef(); }
|
||||
|
||||
~RefCountedNonVirtual() = default;
|
||||
|
||||
private:
|
||||
mutable webrtc::webrtc_impl::RefCounter ref_count_{0};
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
// Backwards compatibe aliases.
|
||||
// TODO: https://issues.webrtc.org/42225969 - deprecate and remove.
|
||||
namespace rtc {
|
||||
using RefCountedBase = webrtc::RefCountedBase;
|
||||
template <typename T>
|
||||
using RefCountedNonVirtual = webrtc::RefCountedNonVirtual<T>;
|
||||
} // namespace rtc
|
||||
|
||||
#endif // API_REF_COUNTED_BASE_H_
|
||||
|
@ -10,8 +10,20 @@
|
||||
|
||||
#include "api/rtp_headers.h"
|
||||
|
||||
#include "api/video/video_content_type.h"
|
||||
#include "api/video/video_rotation.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
AudioLevel::AudioLevel() : voice_activity_(false), audio_level_(0) {}
|
||||
|
||||
AudioLevel::AudioLevel(bool voice_activity, int audio_level)
|
||||
: voice_activity_(voice_activity), audio_level_(audio_level) {
|
||||
RTC_CHECK_GE(audio_level, 0);
|
||||
RTC_CHECK_LE(audio_level, 127);
|
||||
}
|
||||
|
||||
RTPHeaderExtension::RTPHeaderExtension()
|
||||
: hasTransmissionTimeOffset(false),
|
||||
transmissionTimeOffset(0),
|
||||
@ -19,9 +31,6 @@ RTPHeaderExtension::RTPHeaderExtension()
|
||||
absoluteSendTime(0),
|
||||
hasTransportSequenceNumber(false),
|
||||
transportSequenceNumber(0),
|
||||
hasAudioLevel(false),
|
||||
voiceActivity(false),
|
||||
audioLevel(0),
|
||||
hasVideoRotation(false),
|
||||
videoRotation(kVideoRotation_0),
|
||||
hasVideoContentType(false),
|
||||
@ -44,7 +53,6 @@ RTPHeader::RTPHeader()
|
||||
arrOfCSRCs(),
|
||||
paddingLength(0),
|
||||
headerLength(0),
|
||||
payload_type_frequency(0),
|
||||
extension() {}
|
||||
|
||||
RTPHeader::RTPHeader(const RTPHeader& other) = default;
|
||||
|
@ -14,15 +14,16 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "api/video/color_space.h"
|
||||
#include "api/video/video_content_type.h"
|
||||
#include "api/video/video_rotation.h"
|
||||
#include "api/video/video_timing.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -74,7 +75,30 @@ struct AbsoluteCaptureTime {
|
||||
// system’s NTP clock:
|
||||
//
|
||||
// Capture NTP Clock = Sender NTP Clock + Capture Clock Offset
|
||||
absl::optional<int64_t> estimated_capture_clock_offset;
|
||||
std::optional<int64_t> estimated_capture_clock_offset;
|
||||
};
|
||||
|
||||
// The audio level extension is used to indicate the voice activity and the
|
||||
// audio level of the payload in the RTP stream. See:
|
||||
// https://tools.ietf.org/html/rfc6464#section-3.
|
||||
class AudioLevel {
|
||||
public:
|
||||
AudioLevel();
|
||||
AudioLevel(bool voice_activity, int audio_level);
|
||||
AudioLevel(const AudioLevel& other) = default;
|
||||
AudioLevel& operator=(const AudioLevel& other) = default;
|
||||
|
||||
// Flag indicating whether the encoder believes the audio packet contains
|
||||
// voice activity.
|
||||
bool voice_activity() const { return voice_activity_; }
|
||||
|
||||
// Audio level in -dBov. Values range from 0 to 127, representing 0 to -127
|
||||
// dBov. 127 represents digital silence.
|
||||
int level() const { return audio_level_; }
|
||||
|
||||
private:
|
||||
bool voice_activity_;
|
||||
int audio_level_;
|
||||
};
|
||||
|
||||
inline bool operator==(const AbsoluteCaptureTime& lhs,
|
||||
@ -103,29 +127,22 @@ struct RTPHeaderExtension {
|
||||
(1 << kAbsSendTimeFraction));
|
||||
}
|
||||
|
||||
TimeDelta GetAbsoluteSendTimeDelta(uint32_t previous_sendtime) const {
|
||||
RTC_DCHECK(hasAbsoluteSendTime);
|
||||
RTC_DCHECK(absoluteSendTime < (1ul << 24));
|
||||
RTC_DCHECK(previous_sendtime < (1ul << 24));
|
||||
int32_t delta =
|
||||
static_cast<int32_t>((absoluteSendTime - previous_sendtime) << 8) >> 8;
|
||||
return TimeDelta::Micros((delta * 1000000ll) / (1 << kAbsSendTimeFraction));
|
||||
}
|
||||
|
||||
bool hasTransmissionTimeOffset;
|
||||
int32_t transmissionTimeOffset;
|
||||
bool hasAbsoluteSendTime;
|
||||
uint32_t absoluteSendTime;
|
||||
absl::optional<AbsoluteCaptureTime> absolute_capture_time;
|
||||
std::optional<AbsoluteCaptureTime> absolute_capture_time;
|
||||
bool hasTransportSequenceNumber;
|
||||
uint16_t transportSequenceNumber;
|
||||
absl::optional<FeedbackRequest> feedback_request;
|
||||
std::optional<FeedbackRequest> feedback_request;
|
||||
|
||||
// Audio Level includes both level in dBov and voiced/unvoiced bit. See:
|
||||
// https://tools.ietf.org/html/rfc6464#section-3
|
||||
bool hasAudioLevel;
|
||||
bool voiceActivity;
|
||||
uint8_t audioLevel;
|
||||
std::optional<AudioLevel> audio_level() const { return audio_level_; }
|
||||
|
||||
void set_audio_level(std::optional<AudioLevel> audio_level) {
|
||||
audio_level_ = audio_level;
|
||||
}
|
||||
|
||||
// For Coordination of Video Orientation. See
|
||||
// http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/12.07.00_60/
|
||||
@ -133,7 +150,7 @@ struct RTPHeaderExtension {
|
||||
bool hasVideoRotation;
|
||||
VideoRotation videoRotation;
|
||||
|
||||
// TODO(ilnik): Refactor this and one above to be absl::optional() and remove
|
||||
// TODO(ilnik): Refactor this and one above to be std::optional() and remove
|
||||
// a corresponding bool flag.
|
||||
bool hasVideoContentType;
|
||||
VideoContentType videoContentType;
|
||||
@ -144,21 +161,23 @@ struct RTPHeaderExtension {
|
||||
VideoPlayoutDelay playout_delay;
|
||||
|
||||
// For identification of a stream when ssrc is not signaled. See
|
||||
// https://tools.ietf.org/html/draft-ietf-avtext-rid-09
|
||||
// TODO(danilchap): Update url from draft to release version.
|
||||
// https://tools.ietf.org/html/rfc8852
|
||||
std::string stream_id;
|
||||
std::string repaired_stream_id;
|
||||
|
||||
// For identifying the media section used to interpret this RTP packet. See
|
||||
// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-38
|
||||
// https://tools.ietf.org/html/rfc8843
|
||||
std::string mid;
|
||||
|
||||
absl::optional<ColorSpace> color_space;
|
||||
std::optional<ColorSpace> color_space;
|
||||
|
||||
private:
|
||||
std::optional<AudioLevel> audio_level_;
|
||||
};
|
||||
|
||||
enum { kRtpCsrcSize = 15 }; // RFC 3550 page 13
|
||||
|
||||
struct RTPHeader {
|
||||
struct RTC_EXPORT RTPHeader {
|
||||
RTPHeader();
|
||||
RTPHeader(const RTPHeader& other);
|
||||
RTPHeader& operator=(const RTPHeader& other);
|
||||
@ -172,7 +191,6 @@ struct RTPHeader {
|
||||
uint32_t arrOfCSRCs[kRtpCsrcSize];
|
||||
size_t paddingLength;
|
||||
size_t headerLength;
|
||||
int payload_type_frequency;
|
||||
RTPHeaderExtension extension;
|
||||
};
|
||||
|
||||
|
@ -10,40 +10,42 @@
|
||||
|
||||
#include "api/rtp_packet_info.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/rtp_headers.h"
|
||||
#include "api/units/timestamp.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
RtpPacketInfo::RtpPacketInfo()
|
||||
: ssrc_(0), rtp_timestamp_(0), receive_time_ms_(-1) {}
|
||||
: ssrc_(0), rtp_timestamp_(0), receive_time_(Timestamp::MinusInfinity()) {}
|
||||
|
||||
RtpPacketInfo::RtpPacketInfo(
|
||||
uint32_t ssrc,
|
||||
RtpPacketInfo::RtpPacketInfo(uint32_t ssrc,
|
||||
std::vector<uint32_t> csrcs,
|
||||
uint32_t rtp_timestamp,
|
||||
absl::optional<uint8_t> audio_level,
|
||||
absl::optional<AbsoluteCaptureTime> absolute_capture_time,
|
||||
int64_t receive_time_ms)
|
||||
Timestamp receive_time)
|
||||
: ssrc_(ssrc),
|
||||
csrcs_(std::move(csrcs)),
|
||||
rtp_timestamp_(rtp_timestamp),
|
||||
audio_level_(audio_level),
|
||||
absolute_capture_time_(absolute_capture_time),
|
||||
receive_time_ms_(receive_time_ms) {}
|
||||
receive_time_(receive_time) {}
|
||||
|
||||
RtpPacketInfo::RtpPacketInfo(const RTPHeader& rtp_header,
|
||||
int64_t receive_time_ms)
|
||||
Timestamp receive_time)
|
||||
: ssrc_(rtp_header.ssrc),
|
||||
rtp_timestamp_(rtp_header.timestamp),
|
||||
receive_time_ms_(receive_time_ms) {
|
||||
receive_time_(receive_time) {
|
||||
const auto& extension = rtp_header.extension;
|
||||
const auto csrcs_count = std::min<size_t>(rtp_header.numCSRCs, kRtpCsrcSize);
|
||||
|
||||
csrcs_.assign(&rtp_header.arrOfCSRCs[0], &rtp_header.arrOfCSRCs[csrcs_count]);
|
||||
|
||||
if (extension.hasAudioLevel) {
|
||||
audio_level_ = extension.audioLevel;
|
||||
if (extension.audio_level()) {
|
||||
audio_level_ = extension.audio_level()->level();
|
||||
}
|
||||
|
||||
absolute_capture_time_ = extension.absolute_capture_time;
|
||||
@ -52,9 +54,10 @@ RtpPacketInfo::RtpPacketInfo(const RTPHeader& rtp_header,
|
||||
bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs) {
|
||||
return (lhs.ssrc() == rhs.ssrc()) && (lhs.csrcs() == rhs.csrcs()) &&
|
||||
(lhs.rtp_timestamp() == rhs.rtp_timestamp()) &&
|
||||
(lhs.receive_time() == rhs.receive_time()) &&
|
||||
(lhs.audio_level() == rhs.audio_level()) &&
|
||||
(lhs.absolute_capture_time() == rhs.absolute_capture_time()) &&
|
||||
(lhs.receive_time_ms() == rhs.receive_time_ms());
|
||||
(lhs.local_capture_clock_offset() == rhs.local_capture_clock_offset());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -12,19 +12,21 @@
|
||||
#define API_RTP_PACKET_INFO_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/rtp_headers.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
//
|
||||
// Structure to hold information about a received |RtpPacket|. It is primarily
|
||||
// Structure to hold information about a received `RtpPacket`. It is primarily
|
||||
// used to carry per-packet information from when a packet is received until
|
||||
// the information is passed to |SourceTracker|.
|
||||
// the information is passed to `SourceTracker`.
|
||||
//
|
||||
class RTC_EXPORT RtpPacketInfo {
|
||||
public:
|
||||
@ -33,11 +35,9 @@ class RTC_EXPORT RtpPacketInfo {
|
||||
RtpPacketInfo(uint32_t ssrc,
|
||||
std::vector<uint32_t> csrcs,
|
||||
uint32_t rtp_timestamp,
|
||||
absl::optional<uint8_t> audio_level,
|
||||
absl::optional<AbsoluteCaptureTime> absolute_capture_time,
|
||||
int64_t receive_time_ms);
|
||||
Timestamp receive_time);
|
||||
|
||||
RtpPacketInfo(const RTPHeader& rtp_header, int64_t receive_time_ms);
|
||||
RtpPacketInfo(const RTPHeader& rtp_header, Timestamp receive_time);
|
||||
|
||||
RtpPacketInfo(const RtpPacketInfo& other) = default;
|
||||
RtpPacketInfo(RtpPacketInfo&& other) = default;
|
||||
@ -53,19 +53,32 @@ class RTC_EXPORT RtpPacketInfo {
|
||||
uint32_t rtp_timestamp() const { return rtp_timestamp_; }
|
||||
void set_rtp_timestamp(uint32_t value) { rtp_timestamp_ = value; }
|
||||
|
||||
absl::optional<uint8_t> audio_level() const { return audio_level_; }
|
||||
void set_audio_level(absl::optional<uint8_t> value) { audio_level_ = value; }
|
||||
Timestamp receive_time() const { return receive_time_; }
|
||||
void set_receive_time(Timestamp value) { receive_time_ = value; }
|
||||
|
||||
const absl::optional<AbsoluteCaptureTime>& absolute_capture_time() const {
|
||||
std::optional<uint8_t> audio_level() const { return audio_level_; }
|
||||
RtpPacketInfo& set_audio_level(std::optional<uint8_t> value) {
|
||||
audio_level_ = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::optional<AbsoluteCaptureTime>& absolute_capture_time() const {
|
||||
return absolute_capture_time_;
|
||||
}
|
||||
void set_absolute_capture_time(
|
||||
const absl::optional<AbsoluteCaptureTime>& value) {
|
||||
RtpPacketInfo& set_absolute_capture_time(
|
||||
const std::optional<AbsoluteCaptureTime>& value) {
|
||||
absolute_capture_time_ = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int64_t receive_time_ms() const { return receive_time_ms_; }
|
||||
void set_receive_time_ms(int64_t value) { receive_time_ms_ = value; }
|
||||
const std::optional<TimeDelta>& local_capture_clock_offset() const {
|
||||
return local_capture_clock_offset_;
|
||||
}
|
||||
RtpPacketInfo& set_local_capture_clock_offset(
|
||||
std::optional<TimeDelta> value) {
|
||||
local_capture_clock_offset_ = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
// Fields from the RTP header:
|
||||
@ -74,16 +87,23 @@ class RTC_EXPORT RtpPacketInfo {
|
||||
std::vector<uint32_t> csrcs_;
|
||||
uint32_t rtp_timestamp_;
|
||||
|
||||
// Local `webrtc::Clock`-based timestamp of when the packet was received.
|
||||
Timestamp receive_time_;
|
||||
|
||||
// Fields from the Audio Level header extension:
|
||||
// https://tools.ietf.org/html/rfc6464#section-3
|
||||
absl::optional<uint8_t> audio_level_;
|
||||
std::optional<uint8_t> audio_level_;
|
||||
|
||||
// Fields from the Absolute Capture Time header extension:
|
||||
// http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time
|
||||
absl::optional<AbsoluteCaptureTime> absolute_capture_time_;
|
||||
std::optional<AbsoluteCaptureTime> absolute_capture_time_;
|
||||
|
||||
// Local |webrtc::Clock|-based timestamp of when the packet was received.
|
||||
int64_t receive_time_ms_;
|
||||
// Clock offset between the local clock and the capturer's clock.
|
||||
// Do not confuse with `AbsoluteCaptureTime::estimated_capture_clock_offset`
|
||||
// which instead represents the clock offset between a remote sender and the
|
||||
// capturer. The following holds:
|
||||
// Capture's NTP Clock = Local NTP Clock + Local-Capture Clock Offset
|
||||
std::optional<TimeDelta> local_capture_clock_offset_;
|
||||
};
|
||||
|
||||
bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs);
|
||||
|
@ -11,10 +11,10 @@
|
||||
#ifndef API_RTP_PACKET_INFOS_H_
|
||||
#define API_RTP_PACKET_INFOS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/make_ref_counted.h"
|
||||
#include "api/ref_counted_base.h"
|
||||
#include "api/rtp_packet_info.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
@ -26,8 +26,8 @@ namespace webrtc {
|
||||
// an audio or video frame. Uses internal reference counting to make it very
|
||||
// cheap to copy.
|
||||
//
|
||||
// We should ideally just use |std::vector<RtpPacketInfo>| and have it
|
||||
// |std::move()|-ed as the per-packet information is transferred from one object
|
||||
// We should ideally just use `std::vector<RtpPacketInfo>` and have it
|
||||
// `std::move()`-ed as the per-packet information is transferred from one object
|
||||
// to another. But moving the info, instead of copying it, is not easily done
|
||||
// for the current video code.
|
||||
class RTC_EXPORT RtpPacketInfos {
|
||||
@ -79,7 +79,7 @@ class RTC_EXPORT RtpPacketInfos {
|
||||
size_type size() const { return entries().size(); }
|
||||
|
||||
private:
|
||||
class Data : public rtc::RefCountedBase {
|
||||
class Data final : public rtc::RefCountedNonVirtual<Data> {
|
||||
public:
|
||||
static rtc::scoped_refptr<Data> Create(const vector_type& entries) {
|
||||
// Performance optimization for the empty case.
|
||||
@ -87,7 +87,7 @@ class RTC_EXPORT RtpPacketInfos {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new Data(entries);
|
||||
return rtc::make_ref_counted<Data>(entries);
|
||||
}
|
||||
|
||||
static rtc::scoped_refptr<Data> Create(vector_type&& entries) {
|
||||
@ -96,16 +96,16 @@ class RTC_EXPORT RtpPacketInfos {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new Data(std::move(entries));
|
||||
return rtc::make_ref_counted<Data>(std::move(entries));
|
||||
}
|
||||
|
||||
const vector_type& entries() const { return entries_; }
|
||||
|
||||
private:
|
||||
explicit Data(const vector_type& entries) : entries_(entries) {}
|
||||
explicit Data(vector_type&& entries) : entries_(std::move(entries)) {}
|
||||
~Data() override {}
|
||||
~Data() = default;
|
||||
|
||||
private:
|
||||
const vector_type entries_;
|
||||
};
|
||||
|
||||
|
@ -22,15 +22,15 @@
|
||||
// };
|
||||
//
|
||||
// void some_function() {
|
||||
// scoped_refptr<MyFoo> foo = new MyFoo();
|
||||
// scoped_refptr<MyFoo> foo = make_ref_counted<MyFoo>();
|
||||
// foo->Method(param);
|
||||
// // |foo| is released when this function returns
|
||||
// // `foo` is released when this function returns
|
||||
// }
|
||||
//
|
||||
// void some_other_function() {
|
||||
// scoped_refptr<MyFoo> foo = new MyFoo();
|
||||
// scoped_refptr<MyFoo> foo = make_ref_counted<MyFoo>();
|
||||
// ...
|
||||
// foo = nullptr; // explicitly releases |foo|
|
||||
// foo = nullptr; // explicitly releases `foo`
|
||||
// ...
|
||||
// if (foo)
|
||||
// foo->Method(param);
|
||||
@ -41,41 +41,45 @@
|
||||
// references between the two objects, like so:
|
||||
//
|
||||
// {
|
||||
// scoped_refptr<MyFoo> a = new MyFoo();
|
||||
// scoped_refptr<MyFoo> a = make_ref_counted<MyFoo>();
|
||||
// scoped_refptr<MyFoo> b;
|
||||
//
|
||||
// b.swap(a);
|
||||
// // now, |b| references the MyFoo object, and |a| references null.
|
||||
// // now, `b` references the MyFoo object, and `a` references null.
|
||||
// }
|
||||
//
|
||||
// To make both |a| and |b| in the above example reference the same MyFoo
|
||||
// To make both `a` and `b` in the above example reference the same MyFoo
|
||||
// object, simply use the assignment operator:
|
||||
//
|
||||
// {
|
||||
// scoped_refptr<MyFoo> a = new MyFoo();
|
||||
// scoped_refptr<MyFoo> a = make_ref_counted<MyFoo>();
|
||||
// scoped_refptr<MyFoo> b;
|
||||
//
|
||||
// b = a;
|
||||
// // now, |a| and |b| each own a reference to the same MyFoo object.
|
||||
// // now, `a` and `b` each own a reference to the same MyFoo object.
|
||||
// }
|
||||
//
|
||||
|
||||
#ifndef API_SCOPED_REFPTR_H_
|
||||
#define API_SCOPED_REFPTR_H_
|
||||
|
||||
#include <memory>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
namespace rtc {
|
||||
#include "absl/base/nullability.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
template <class T>
|
||||
class scoped_refptr {
|
||||
class ABSL_NULLABILITY_COMPATIBLE scoped_refptr {
|
||||
public:
|
||||
typedef T element_type;
|
||||
using absl_nullability_compatible = void;
|
||||
using element_type = T;
|
||||
|
||||
scoped_refptr() : ptr_(nullptr) {}
|
||||
scoped_refptr(std::nullptr_t) : ptr_(nullptr) {} // NOLINT(runtime/explicit)
|
||||
|
||||
scoped_refptr(T* p) : ptr_(p) { // NOLINT(runtime/explicit)
|
||||
explicit scoped_refptr(absl::Nullable<T*> p) : ptr_(p) {
|
||||
if (ptr_)
|
||||
ptr_->AddRef();
|
||||
}
|
||||
@ -103,7 +107,8 @@ class scoped_refptr {
|
||||
}
|
||||
|
||||
T* get() const { return ptr_; }
|
||||
operator T*() const { return ptr_; }
|
||||
explicit operator bool() const { return ptr_ != nullptr; }
|
||||
T& operator*() const { return *ptr_; }
|
||||
T* operator->() const { return ptr_; }
|
||||
|
||||
// Returns the (possibly null) raw pointer, and makes the scoped_refptr hold a
|
||||
@ -117,7 +122,7 @@ class scoped_refptr {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
scoped_refptr<T>& operator=(T* p) {
|
||||
scoped_refptr<T>& operator=(absl::Nullable<T*> p) {
|
||||
// AddRef first so that self assignment should work
|
||||
if (p)
|
||||
p->AddRef();
|
||||
@ -147,7 +152,7 @@ class scoped_refptr {
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(T** pp) noexcept {
|
||||
void swap(absl::Nonnull<T**> pp) noexcept {
|
||||
T* p = ptr_;
|
||||
ptr_ = *pp;
|
||||
*pp = p;
|
||||
@ -159,6 +164,66 @@ class scoped_refptr {
|
||||
T* ptr_;
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(const scoped_refptr<T>& a, const scoped_refptr<U>& b) {
|
||||
return a.get() == b.get();
|
||||
}
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const scoped_refptr<T>& a, const scoped_refptr<U>& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator==(const scoped_refptr<T>& a, std::nullptr_t) {
|
||||
return a.get() == nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(const scoped_refptr<T>& a, std::nullptr_t) {
|
||||
return !(a == nullptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator==(std::nullptr_t, const scoped_refptr<T>& a) {
|
||||
return a.get() == nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(std::nullptr_t, const scoped_refptr<T>& a) {
|
||||
return !(a == nullptr);
|
||||
}
|
||||
|
||||
// Comparison with raw pointer.
|
||||
template <typename T, typename U>
|
||||
bool operator==(const scoped_refptr<T>& a, const U* b) {
|
||||
return a.get() == b;
|
||||
}
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const scoped_refptr<T>& a, const U* b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(const T* a, const scoped_refptr<U>& b) {
|
||||
return a == b.get();
|
||||
}
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const T* a, const scoped_refptr<U>& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
// Ordered comparison, needed for use as a std::map key.
|
||||
template <typename T, typename U>
|
||||
bool operator<(const scoped_refptr<T>& a, const scoped_refptr<U>& b) {
|
||||
return a.get() < b.get();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
namespace rtc {
|
||||
// Backwards compatible alias.
|
||||
// TODO: bugs.webrtc.org/42225969 - Deprecate and remove.
|
||||
using ::webrtc::scoped_refptr;
|
||||
} // namespace rtc
|
||||
|
||||
#endif // API_SCOPED_REFPTR_H_
|
||||
|
141
webrtc/api/sequence_checker.h
Normal file
141
webrtc/api/sequence_checker.h
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2019 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef API_SEQUENCE_CHECKER_H_
|
||||
#define API_SEQUENCE_CHECKER_H_
|
||||
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/synchronization/sequence_checker_internal.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// SequenceChecker is a helper class used to help verify that some methods
|
||||
// of a class are called on the same task queue or thread. A
|
||||
// SequenceChecker is bound to a a task queue if the object is
|
||||
// created on a task queue, or a thread otherwise.
|
||||
//
|
||||
//
|
||||
// Example:
|
||||
// class MyClass {
|
||||
// public:
|
||||
// void Foo() {
|
||||
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
// ... (do stuff) ...
|
||||
// }
|
||||
//
|
||||
// private:
|
||||
// SequenceChecker sequence_checker_;
|
||||
// }
|
||||
//
|
||||
// In Release mode, IsCurrent will always return true.
|
||||
class RTC_LOCKABLE SequenceChecker
|
||||
#if RTC_DCHECK_IS_ON
|
||||
: public webrtc_sequence_checker_internal::SequenceCheckerImpl {
|
||||
using Impl = webrtc_sequence_checker_internal::SequenceCheckerImpl;
|
||||
#else
|
||||
: public webrtc_sequence_checker_internal::SequenceCheckerDoNothing {
|
||||
using Impl = webrtc_sequence_checker_internal::SequenceCheckerDoNothing;
|
||||
#endif
|
||||
public:
|
||||
enum InitialState : bool { kDetached = false, kAttached = true };
|
||||
|
||||
// TODO(tommi): We could maybe join these two ctors and have fewer factory
|
||||
// functions. At the moment they're separate to minimize code changes when
|
||||
// we added the second ctor as well as avoiding to have unnecessary code at
|
||||
// the SequenceChecker which much only run for the SequenceCheckerImpl
|
||||
// implementation.
|
||||
// In theory we could have something like:
|
||||
//
|
||||
// SequenceChecker(InitialState initial_state = kAttached,
|
||||
// TaskQueueBase* attached_queue = TaskQueueBase::Current());
|
||||
//
|
||||
// But the problem with that is having the call to `Current()` exist for
|
||||
// `SequenceCheckerDoNothing`.
|
||||
explicit SequenceChecker(InitialState initial_state = kAttached)
|
||||
: Impl(initial_state) {}
|
||||
explicit SequenceChecker(TaskQueueBase* attached_queue)
|
||||
: Impl(attached_queue) {}
|
||||
|
||||
// Returns true if sequence checker is attached to the current sequence.
|
||||
bool IsCurrent() const { return Impl::IsCurrent(); }
|
||||
// Detaches checker from sequence to which it is attached. Next attempt
|
||||
// to do a check with this checker will result in attaching this checker
|
||||
// to the sequence on which check was performed.
|
||||
void Detach() { Impl::Detach(); }
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
// RTC_RUN_ON/RTC_GUARDED_BY/RTC_DCHECK_RUN_ON macros allows to annotate
|
||||
// variables are accessed from same thread/task queue.
|
||||
// Using tools designed to check mutexes, it checks at compile time everywhere
|
||||
// variable is access, there is a run-time dcheck thread/task queue is correct.
|
||||
//
|
||||
// class SequenceCheckerExample {
|
||||
// public:
|
||||
// int CalledFromPacer() RTC_RUN_ON(pacer_sequence_checker_) {
|
||||
// return var2_;
|
||||
// }
|
||||
//
|
||||
// void CallMeFromPacer() {
|
||||
// RTC_DCHECK_RUN_ON(&pacer_sequence_checker_)
|
||||
// << "Should be called from pacer";
|
||||
// CalledFromPacer();
|
||||
// }
|
||||
//
|
||||
// private:
|
||||
// int pacer_var_ RTC_GUARDED_BY(pacer_sequence_checker_);
|
||||
// SequenceChecker pacer_sequence_checker_;
|
||||
// };
|
||||
//
|
||||
// class TaskQueueExample {
|
||||
// public:
|
||||
// class Encoder {
|
||||
// public:
|
||||
// rtc::TaskQueueBase& Queue() { return encoder_queue_; }
|
||||
// void Encode() {
|
||||
// RTC_DCHECK_RUN_ON(&encoder_queue_);
|
||||
// DoSomething(var_);
|
||||
// }
|
||||
//
|
||||
// private:
|
||||
// rtc::TaskQueueBase& encoder_queue_;
|
||||
// Frame var_ RTC_GUARDED_BY(encoder_queue_);
|
||||
// };
|
||||
//
|
||||
// void Encode() {
|
||||
// // Will fail at runtime when DCHECK is enabled:
|
||||
// // encoder_->Encode();
|
||||
// // Will work:
|
||||
// rtc::scoped_refptr<Encoder> encoder = encoder_;
|
||||
// encoder_->Queue().PostTask([encoder] { encoder->Encode(); });
|
||||
// }
|
||||
//
|
||||
// private:
|
||||
// rtc::scoped_refptr<Encoder> encoder_;
|
||||
// }
|
||||
|
||||
// Document if a function expected to be called from same thread/task queue.
|
||||
#define RTC_RUN_ON(x) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(x))
|
||||
|
||||
// Checks current code is running on the desired sequence.
|
||||
//
|
||||
// First statement validates it is running on the sequence `x`.
|
||||
// Second statement annotates for the thread safety analyzer the check was done.
|
||||
// Such annotation has to be attached to a function, and that function has to be
|
||||
// called. Thus current implementation creates a noop lambda and calls it.
|
||||
#define RTC_DCHECK_RUN_ON(x) \
|
||||
RTC_DCHECK((x)->IsCurrent()) \
|
||||
<< webrtc::webrtc_sequence_checker_internal::ExpectationToString(x); \
|
||||
[]() RTC_ASSERT_EXCLUSIVE_LOCK(x) {}()
|
||||
|
||||
#endif // API_SEQUENCE_CHECKER_H_
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef API_TASK_QUEUE_QUEUED_TASK_H_
|
||||
#define API_TASK_QUEUE_QUEUED_TASK_H_
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Base interface for asynchronously executed tasks.
|
||||
// The interface basically consists of a single function, Run(), that executes
|
||||
// on the target queue. For more details see the Run() method and TaskQueue.
|
||||
class QueuedTask {
|
||||
public:
|
||||
virtual ~QueuedTask() = default;
|
||||
|
||||
// Main routine that will run when the task is executed on the desired queue.
|
||||
// The task should return |true| to indicate that it should be deleted or
|
||||
// |false| to indicate that the queue should consider ownership of the task
|
||||
// having been transferred. Returning |false| can be useful if a task has
|
||||
// re-posted itself to a different queue or is otherwise being re-used.
|
||||
virtual bool Run() = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_TASK_QUEUE_QUEUED_TASK_H_
|
@ -11,7 +11,6 @@
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/base/config.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
#if defined(ABSL_HAVE_THREAD_LOCAL)
|
||||
|
||||
|
@ -10,9 +10,11 @@
|
||||
#ifndef API_TASK_QUEUE_TASK_QUEUE_BASE_H_
|
||||
#define API_TASK_QUEUE_TASK_QUEUE_BASE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "api/task_queue/queued_task.h"
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "api/location.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
@ -24,41 +26,139 @@ namespace webrtc {
|
||||
// known task queue, use IsCurrent().
|
||||
class RTC_LOCKABLE RTC_EXPORT TaskQueueBase {
|
||||
public:
|
||||
enum class DelayPrecision {
|
||||
// This may include up to a 17 ms leeway in addition to OS timer precision.
|
||||
// See PostDelayedTask() for more information.
|
||||
kLow,
|
||||
// This does not have the additional delay that kLow has, but it is still
|
||||
// limited by OS timer precision. See PostDelayedHighPrecisionTask() for
|
||||
// more information.
|
||||
kHigh,
|
||||
};
|
||||
|
||||
// Starts destruction of the task queue.
|
||||
// On return ensures no task are running and no new tasks are able to start
|
||||
// on the task queue.
|
||||
// Responsible for deallocation. Deallocation may happen syncrhoniously during
|
||||
// Responsible for deallocation. Deallocation may happen synchronously during
|
||||
// Delete or asynchronously after Delete returns.
|
||||
// Code not running on the TaskQueue should not make any assumption when
|
||||
// TaskQueue is deallocated and thus should not call any methods after Delete.
|
||||
// Code running on the TaskQueue should not call Delete, but can assume
|
||||
// TaskQueue still exists and may call other methods, e.g. PostTask.
|
||||
// Should be called on the same task queue or thread that this task queue
|
||||
// was created on.
|
||||
virtual void Delete() = 0;
|
||||
|
||||
// Schedules a task to execute. Tasks are executed in FIFO order.
|
||||
// If |task->Run()| returns true, task is deleted on the task queue
|
||||
// before next QueuedTask starts executing.
|
||||
// Schedules a `task` to execute. Tasks are executed in FIFO order.
|
||||
// When a TaskQueue is deleted, pending tasks will not be executed but they
|
||||
// will be deleted. The deletion of tasks may happen synchronously on the
|
||||
// TaskQueue or it may happen asynchronously after TaskQueue is deleted.
|
||||
// This may vary from one implementation to the next so assumptions about
|
||||
// lifetimes of pending tasks should not be made.
|
||||
virtual void PostTask(std::unique_ptr<QueuedTask> task) = 0;
|
||||
// will be deleted.
|
||||
//
|
||||
// As long as tasks are not posted from task destruction, posted tasks are
|
||||
// guaranteed to be destroyed with Current() pointing to the task queue they
|
||||
// were posted to, whether they're executed or not. That means SequenceChecker
|
||||
// works during task destruction, a fact that can be used to guarantee
|
||||
// thread-compatible object deletion happening on a particular task queue
|
||||
// which can simplify class design.
|
||||
// Note that this guarantee does not apply to delayed tasks.
|
||||
//
|
||||
// May be called on any thread or task queue, including this task queue.
|
||||
void PostTask(absl::AnyInvocable<void() &&> task,
|
||||
const Location& location = Location::Current()) {
|
||||
PostTaskImpl(std::move(task), PostTaskTraits{}, location);
|
||||
}
|
||||
|
||||
// Schedules a task to execute a specified number of milliseconds from when
|
||||
// the call is made. The precision should be considered as "best effort"
|
||||
// and in some cases, such as on Windows when all high precision timers have
|
||||
// been used up, can be off by as much as 15 millseconds.
|
||||
virtual void PostDelayedTask(std::unique_ptr<QueuedTask> task,
|
||||
uint32_t milliseconds) = 0;
|
||||
// Prefer PostDelayedTask() over PostDelayedHighPrecisionTask() whenever
|
||||
// possible.
|
||||
//
|
||||
// Schedules a `task` to execute a specified `delay` from when the call is
|
||||
// made, using "low" precision. All scheduling is affected by OS-specific
|
||||
// leeway and current workloads which means that in terms of precision there
|
||||
// are no hard guarantees, but in addition to the OS induced leeway, "low"
|
||||
// precision adds up to a 17 ms additional leeway. The purpose of this leeway
|
||||
// is to achieve more efficient CPU scheduling and reduce Idle Wake Up
|
||||
// frequency.
|
||||
//
|
||||
// The task may execute with [-1, 17 + OS induced leeway) ms additional delay.
|
||||
//
|
||||
// Avoid making assumptions about the precision of the OS scheduler. On macOS,
|
||||
// the OS induced leeway may be 10% of sleep interval. On Windows, 1 ms
|
||||
// precision timers may be used but there are cases, such as when running on
|
||||
// battery, when the timer precision can be as poor as 15 ms.
|
||||
//
|
||||
// "Low" precision is not implemented everywhere yet. Where not yet
|
||||
// implemented, PostDelayedTask() has "high" precision. See
|
||||
// https://crbug.com/webrtc/13583 for more information.
|
||||
//
|
||||
// May be called on any thread or task queue, including this task queue.
|
||||
void PostDelayedTask(absl::AnyInvocable<void() &&> task,
|
||||
TimeDelta delay,
|
||||
const Location& location = Location::Current()) {
|
||||
PostDelayedTaskImpl(std::move(task), delay, PostDelayedTaskTraits{},
|
||||
location);
|
||||
}
|
||||
|
||||
// Prefer PostDelayedTask() over PostDelayedHighPrecisionTask() whenever
|
||||
// possible.
|
||||
//
|
||||
// Schedules a `task` to execute a specified `delay` from when the call is
|
||||
// made, using "high" precision. All scheduling is affected by OS-specific
|
||||
// leeway and current workloads which means that in terms of precision there
|
||||
// are no hard guarantees.
|
||||
//
|
||||
// The task may execute with [-1, OS induced leeway] ms additional delay.
|
||||
//
|
||||
// Avoid making assumptions about the precision of the OS scheduler. On macOS,
|
||||
// the OS induced leeway may be 10% of sleep interval. On Windows, 1 ms
|
||||
// precision timers may be used but there are cases, such as when running on
|
||||
// battery, when the timer precision can be as poor as 15 ms.
|
||||
//
|
||||
// May be called on any thread or task queue, including this task queue.
|
||||
void PostDelayedHighPrecisionTask(
|
||||
absl::AnyInvocable<void() &&> task,
|
||||
TimeDelta delay,
|
||||
const Location& location = Location::Current()) {
|
||||
PostDelayedTaskTraits traits;
|
||||
traits.high_precision = true;
|
||||
PostDelayedTaskImpl(std::move(task), delay, traits, location);
|
||||
}
|
||||
|
||||
// As specified by `precision`, calls either PostDelayedTask() or
|
||||
// PostDelayedHighPrecisionTask().
|
||||
void PostDelayedTaskWithPrecision(
|
||||
DelayPrecision precision,
|
||||
absl::AnyInvocable<void() &&> task,
|
||||
TimeDelta delay,
|
||||
const Location& location = Location::Current()) {
|
||||
switch (precision) {
|
||||
case DelayPrecision::kLow:
|
||||
PostDelayedTask(std::move(task), delay, location);
|
||||
break;
|
||||
case DelayPrecision::kHigh:
|
||||
PostDelayedHighPrecisionTask(std::move(task), delay, location);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the task queue that is running the current thread.
|
||||
// Returns nullptr if this thread is not associated with any task queue.
|
||||
// May be called on any thread or task queue, including this task queue.
|
||||
static TaskQueueBase* Current();
|
||||
bool IsCurrent() const { return Current() == this; }
|
||||
|
||||
protected:
|
||||
class CurrentTaskQueueSetter {
|
||||
// This is currently only present here to simplify introduction of future
|
||||
// planned task queue changes.
|
||||
struct PostTaskTraits {};
|
||||
|
||||
struct PostDelayedTaskTraits {
|
||||
// If `high_precision` is false, tasks may execute within up to a 17 ms
|
||||
// leeway in addition to OS timer precision. Otherwise the task should be
|
||||
// limited to OS timer precision. See PostDelayedTask() and
|
||||
// PostDelayedHighPrecisionTask() for more information.
|
||||
bool high_precision = false;
|
||||
};
|
||||
|
||||
class RTC_EXPORT CurrentTaskQueueSetter {
|
||||
public:
|
||||
explicit CurrentTaskQueueSetter(TaskQueueBase* task_queue);
|
||||
CurrentTaskQueueSetter(const CurrentTaskQueueSetter&) = delete;
|
||||
@ -69,6 +169,20 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueueBase {
|
||||
TaskQueueBase* const previous_;
|
||||
};
|
||||
|
||||
// Subclasses should implement this method to support the behavior defined in
|
||||
// the PostTask and PostTaskTraits docs above.
|
||||
virtual void PostTaskImpl(absl::AnyInvocable<void() &&> task,
|
||||
const PostTaskTraits& traits,
|
||||
const Location& location) = 0;
|
||||
|
||||
// Subclasses should implement this method to support the behavior defined in
|
||||
// the PostDelayedTask/PostHighPrecisionDelayedTask and PostDelayedTaskTraits
|
||||
// docs above.
|
||||
virtual void PostDelayedTaskImpl(absl::AnyInvocable<void() &&> task,
|
||||
TimeDelta delay,
|
||||
const PostDelayedTaskTraits& traits,
|
||||
const Location& location) = 0;
|
||||
|
||||
// Users of the TaskQueue should call Delete instead of directly deleting
|
||||
// this object.
|
||||
virtual ~TaskQueueBase() = default;
|
||||
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "api/units/data_rate.h"
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
std::string ToString(DataRate value) {
|
||||
char buf[64];
|
||||
rtc::SimpleStringBuilder sb(buf);
|
||||
if (value.IsPlusInfinity()) {
|
||||
sb << "+inf bps";
|
||||
} else if (value.IsMinusInfinity()) {
|
||||
sb << "-inf bps";
|
||||
} else {
|
||||
if (value.bps() == 0 || value.bps() % 1000 != 0) {
|
||||
sb << value.bps() << " bps";
|
||||
} else {
|
||||
sb << value.kbps() << " kbps";
|
||||
}
|
||||
}
|
||||
return sb.str();
|
||||
}
|
||||
} // namespace webrtc
|
@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef API_UNITS_DATA_RATE_H_
|
||||
#define API_UNITS_DATA_RATE_H_
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#include <ostream> // no-presubmit-check TODO(webrtc:8982)
|
||||
#endif // UNIT_TEST
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/frequency.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/units/unit_base.h"
|
||||
|
||||
namespace webrtc {
|
||||
// DataRate is a class that represents a given data rate. This can be used to
|
||||
// represent bandwidth, encoding bitrate, etc. The internal storage is bits per
|
||||
// second (bps).
|
||||
class DataRate final : public rtc_units_impl::RelativeUnit<DataRate> {
|
||||
public:
|
||||
template <typename T>
|
||||
static constexpr DataRate BitsPerSec(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromValue(value);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr DataRate BytesPerSec(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(8, value);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr DataRate KilobitsPerSec(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1000, value);
|
||||
}
|
||||
static constexpr DataRate Infinity() { return PlusInfinity(); }
|
||||
|
||||
DataRate() = delete;
|
||||
|
||||
template <typename T = int64_t>
|
||||
constexpr T bps() const {
|
||||
return ToValue<T>();
|
||||
}
|
||||
template <typename T = int64_t>
|
||||
constexpr T bytes_per_sec() const {
|
||||
return ToFraction<8, T>();
|
||||
}
|
||||
template <typename T = int64_t>
|
||||
constexpr T kbps() const {
|
||||
return ToFraction<1000, T>();
|
||||
}
|
||||
constexpr int64_t bps_or(int64_t fallback_value) const {
|
||||
return ToValueOr(fallback_value);
|
||||
}
|
||||
constexpr int64_t kbps_or(int64_t fallback_value) const {
|
||||
return ToFractionOr<1000>(fallback_value);
|
||||
}
|
||||
|
||||
private:
|
||||
// Bits per second used internally to simplify debugging by making the value
|
||||
// more recognizable.
|
||||
friend class rtc_units_impl::UnitBase<DataRate>;
|
||||
using RelativeUnit::RelativeUnit;
|
||||
static constexpr bool one_sided = true;
|
||||
};
|
||||
|
||||
namespace data_rate_impl {
|
||||
inline constexpr int64_t Microbits(const DataSize& size) {
|
||||
constexpr int64_t kMaxBeforeConversion =
|
||||
std::numeric_limits<int64_t>::max() / 8000000;
|
||||
RTC_DCHECK_LE(size.bytes(), kMaxBeforeConversion)
|
||||
<< "size is too large to be expressed in microbits";
|
||||
return size.bytes() * 8000000;
|
||||
}
|
||||
|
||||
inline constexpr int64_t MillibytePerSec(const DataRate& size) {
|
||||
constexpr int64_t kMaxBeforeConversion =
|
||||
std::numeric_limits<int64_t>::max() / (1000 / 8);
|
||||
RTC_DCHECK_LE(size.bps(), kMaxBeforeConversion)
|
||||
<< "rate is too large to be expressed in microbytes per second";
|
||||
return size.bps() * (1000 / 8);
|
||||
}
|
||||
} // namespace data_rate_impl
|
||||
|
||||
inline constexpr DataRate operator/(const DataSize size,
|
||||
const TimeDelta duration) {
|
||||
return DataRate::BitsPerSec(data_rate_impl::Microbits(size) / duration.us());
|
||||
}
|
||||
inline constexpr TimeDelta operator/(const DataSize size, const DataRate rate) {
|
||||
return TimeDelta::Micros(data_rate_impl::Microbits(size) / rate.bps());
|
||||
}
|
||||
inline constexpr DataSize operator*(const DataRate rate,
|
||||
const TimeDelta duration) {
|
||||
int64_t microbits = rate.bps() * duration.us();
|
||||
return DataSize::Bytes((microbits + 4000000) / 8000000);
|
||||
}
|
||||
inline constexpr DataSize operator*(const TimeDelta duration,
|
||||
const DataRate rate) {
|
||||
return rate * duration;
|
||||
}
|
||||
|
||||
inline constexpr DataSize operator/(const DataRate rate,
|
||||
const Frequency frequency) {
|
||||
int64_t millihertz = frequency.millihertz<int64_t>();
|
||||
// Note that the value is truncated here reather than rounded, potentially
|
||||
// introducing an error of .5 bytes if rounding were expected.
|
||||
return DataSize::Bytes(data_rate_impl::MillibytePerSec(rate) / millihertz);
|
||||
}
|
||||
inline constexpr Frequency operator/(const DataRate rate, const DataSize size) {
|
||||
return Frequency::MilliHertz(data_rate_impl::MillibytePerSec(rate) /
|
||||
size.bytes());
|
||||
}
|
||||
inline constexpr DataRate operator*(const DataSize size,
|
||||
const Frequency frequency) {
|
||||
RTC_DCHECK(frequency.IsZero() ||
|
||||
size.bytes() <= std::numeric_limits<int64_t>::max() / 8 /
|
||||
frequency.millihertz<int64_t>());
|
||||
int64_t millibits_per_second =
|
||||
size.bytes() * 8 * frequency.millihertz<int64_t>();
|
||||
return DataRate::BitsPerSec((millibits_per_second + 500) / 1000);
|
||||
}
|
||||
inline constexpr DataRate operator*(const Frequency frequency,
|
||||
const DataSize size) {
|
||||
return size * frequency;
|
||||
}
|
||||
|
||||
std::string ToString(DataRate value);
|
||||
inline std::string ToLogString(DataRate value) {
|
||||
return ToString(value);
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
|
||||
std::ostream& stream, // no-presubmit-check TODO(webrtc:8982)
|
||||
DataRate value) {
|
||||
return stream << ToString(value);
|
||||
}
|
||||
#endif // UNIT_TEST
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_UNITS_DATA_RATE_H_
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "api/units/data_size.h"
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
std::string ToString(DataSize value) {
|
||||
char buf[64];
|
||||
rtc::SimpleStringBuilder sb(buf);
|
||||
if (value.IsPlusInfinity()) {
|
||||
sb << "+inf bytes";
|
||||
} else if (value.IsMinusInfinity()) {
|
||||
sb << "-inf bytes";
|
||||
} else {
|
||||
sb << value.bytes() << " bytes";
|
||||
}
|
||||
return sb.str();
|
||||
}
|
||||
} // namespace webrtc
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef API_UNITS_DATA_SIZE_H_
|
||||
#define API_UNITS_DATA_SIZE_H_
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#include <ostream> // no-presubmit-check TODO(webrtc:8982)
|
||||
#endif // UNIT_TEST
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "rtc_base/units/unit_base.h"
|
||||
|
||||
namespace webrtc {
|
||||
// DataSize is a class represeting a count of bytes.
|
||||
class DataSize final : public rtc_units_impl::RelativeUnit<DataSize> {
|
||||
public:
|
||||
template <typename T>
|
||||
static constexpr DataSize Bytes(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromValue(value);
|
||||
}
|
||||
static constexpr DataSize Infinity() { return PlusInfinity(); }
|
||||
|
||||
DataSize() = delete;
|
||||
|
||||
template <typename T = int64_t>
|
||||
constexpr T bytes() const {
|
||||
return ToValue<T>();
|
||||
}
|
||||
|
||||
constexpr int64_t bytes_or(int64_t fallback_value) const {
|
||||
return ToValueOr(fallback_value);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class rtc_units_impl::UnitBase<DataSize>;
|
||||
using RelativeUnit::RelativeUnit;
|
||||
static constexpr bool one_sided = true;
|
||||
};
|
||||
|
||||
std::string ToString(DataSize value);
|
||||
inline std::string ToLogString(DataSize value) {
|
||||
return ToString(value);
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
|
||||
std::ostream& stream, // no-presubmit-check TODO(webrtc:8982)
|
||||
DataSize value) {
|
||||
return stream << ToString(value);
|
||||
}
|
||||
#endif // UNIT_TEST
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_UNITS_DATA_SIZE_H_
|
@ -9,6 +9,9 @@
|
||||
*/
|
||||
#include "api/units/frequency.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
@ -10,17 +10,16 @@
|
||||
#ifndef API_UNITS_FREQUENCY_H_
|
||||
#define API_UNITS_FREQUENCY_H_
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#include <ostream> // no-presubmit-check TODO(webrtc:8982)
|
||||
#endif // UNIT_TEST
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/units/unit_base.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/units/unit_base.h" // IWYU pragma: export
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -44,6 +43,9 @@ class Frequency final : public rtc_units_impl::RelativeUnit<Frequency> {
|
||||
|
||||
Frequency() = delete;
|
||||
|
||||
template <typename Sink>
|
||||
friend void AbslStringify(Sink& sink, Frequency value);
|
||||
|
||||
template <typename T = int64_t>
|
||||
constexpr T hertz() const {
|
||||
return ToFraction<1000, T>();
|
||||
@ -84,18 +86,15 @@ inline constexpr double operator*(TimeDelta time_delta, Frequency frequency) {
|
||||
return frequency * time_delta;
|
||||
}
|
||||
|
||||
std::string ToString(Frequency value);
|
||||
RTC_EXPORT std::string ToString(Frequency value);
|
||||
inline std::string ToLogString(Frequency value) {
|
||||
return ToString(value);
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
|
||||
std::ostream& stream, // no-presubmit-check TODO(webrtc:8982)
|
||||
Frequency value) {
|
||||
return stream << ToString(value);
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, Frequency value) {
|
||||
sink.Append(ToString(value));
|
||||
}
|
||||
#endif // UNIT_TEST
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // API_UNITS_FREQUENCY_H_
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "api/units/time_delta.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
|
@ -11,15 +11,13 @@
|
||||
#ifndef API_UNITS_TIME_DELTA_H_
|
||||
#define API_UNITS_TIME_DELTA_H_
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#include <ostream> // no-presubmit-check TODO(webrtc:8982)
|
||||
#endif // UNIT_TEST
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "rtc_base/units/unit_base.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/units/unit_base.h" // IWYU pragma: export
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -32,6 +30,11 @@ namespace webrtc {
|
||||
// microseconds (us).
|
||||
class TimeDelta final : public rtc_units_impl::RelativeUnit<TimeDelta> {
|
||||
public:
|
||||
template <typename T>
|
||||
static constexpr TimeDelta Minutes(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return Seconds(value * 60);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr TimeDelta Seconds(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
@ -50,6 +53,9 @@ class TimeDelta final : public rtc_units_impl::RelativeUnit<TimeDelta> {
|
||||
|
||||
TimeDelta() = delete;
|
||||
|
||||
template <typename Sink>
|
||||
friend void AbslStringify(Sink& sink, TimeDelta value);
|
||||
|
||||
template <typename T = int64_t>
|
||||
constexpr T seconds() const {
|
||||
return ToFraction<1000000, T>();
|
||||
@ -87,18 +93,15 @@ class TimeDelta final : public rtc_units_impl::RelativeUnit<TimeDelta> {
|
||||
static constexpr bool one_sided = false;
|
||||
};
|
||||
|
||||
std::string ToString(TimeDelta value);
|
||||
RTC_EXPORT std::string ToString(TimeDelta value);
|
||||
inline std::string ToLogString(TimeDelta value) {
|
||||
return ToString(value);
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
|
||||
std::ostream& stream, // no-presubmit-check TODO(webrtc:8982)
|
||||
TimeDelta value) {
|
||||
return stream << ToString(value);
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, TimeDelta value) {
|
||||
sink.Append(ToString(value));
|
||||
}
|
||||
#endif // UNIT_TEST
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "api/units/timestamp.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
|
@ -11,15 +11,14 @@
|
||||
#ifndef API_UNITS_TIMESTAMP_H_
|
||||
#define API_UNITS_TIMESTAMP_H_
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#include <ostream> // no-presubmit-check TODO(webrtc:8982)
|
||||
#endif // UNIT_TEST
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/units/unit_base.h" // IWYU pragma: export
|
||||
|
||||
namespace webrtc {
|
||||
// Timestamp represents the time that has passed since some unspecified epoch.
|
||||
@ -46,6 +45,9 @@ class Timestamp final : public rtc_units_impl::UnitBase<Timestamp> {
|
||||
|
||||
Timestamp() = delete;
|
||||
|
||||
template <typename Sink>
|
||||
friend void AbslStringify(Sink& sink, Timestamp value);
|
||||
|
||||
template <typename T = int64_t>
|
||||
constexpr T seconds() const {
|
||||
return ToFraction<1000000, T>();
|
||||
@ -120,18 +122,15 @@ class Timestamp final : public rtc_units_impl::UnitBase<Timestamp> {
|
||||
static constexpr bool one_sided = true;
|
||||
};
|
||||
|
||||
std::string ToString(Timestamp value);
|
||||
RTC_EXPORT std::string ToString(Timestamp value);
|
||||
inline std::string ToLogString(Timestamp value) {
|
||||
return ToString(value);
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
|
||||
std::ostream& stream, // no-presubmit-check TODO(webrtc:8982)
|
||||
Timestamp value) {
|
||||
return stream << ToString(value);
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, Timestamp value) {
|
||||
sink.Append(ToString(value));
|
||||
}
|
||||
#endif // UNIT_TEST
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -10,9 +10,17 @@
|
||||
|
||||
#include "api/video/color_space.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "api/video/hdr_metadata.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
// Try to convert |enum_value| into the enum class T. |enum_bitmask| is created
|
||||
// Try to convert `enum_value` into the enum class T. `enum_bitmask` is created
|
||||
// by the funciton below. Returns true if conversion was successful, false
|
||||
// otherwise.
|
||||
template <typename T>
|
||||
@ -43,7 +51,7 @@ constexpr int MakeMask(const int index, const int length, T (&values)[N]) {
|
||||
}
|
||||
|
||||
// Create a bitmask where each bit corresponds to one potential enum value.
|
||||
// |values| should be an array listing all possible enum values. The bit is set
|
||||
// `values` should be an array listing all possible enum values. The bit is set
|
||||
// to one if the corresponding enum exists. Only works for enums with values
|
||||
// less than 64.
|
||||
template <typename T, size_t N>
|
||||
@ -93,8 +101,8 @@ ColorSpace::ColorSpace(PrimaryID primaries,
|
||||
range_(range),
|
||||
chroma_siting_horizontal_(chroma_siting_horz),
|
||||
chroma_siting_vertical_(chroma_siting_vert),
|
||||
hdr_metadata_(hdr_metadata ? absl::make_optional(*hdr_metadata)
|
||||
: absl::nullopt) {}
|
||||
hdr_metadata_(hdr_metadata ? std::make_optional(*hdr_metadata)
|
||||
: std::nullopt) {}
|
||||
|
||||
ColorSpace::PrimaryID ColorSpace::primaries() const {
|
||||
return primaries_;
|
||||
@ -124,6 +132,80 @@ const HdrMetadata* ColorSpace::hdr_metadata() const {
|
||||
return hdr_metadata_ ? &*hdr_metadata_ : nullptr;
|
||||
}
|
||||
|
||||
#define PRINT_ENUM_CASE(TYPE, NAME) \
|
||||
case TYPE::NAME: \
|
||||
ss << #NAME; \
|
||||
break;
|
||||
|
||||
std::string ColorSpace::AsString() const {
|
||||
char buf[1024];
|
||||
rtc::SimpleStringBuilder ss(buf);
|
||||
ss << "{primaries:";
|
||||
switch (primaries_) {
|
||||
PRINT_ENUM_CASE(PrimaryID, kBT709)
|
||||
PRINT_ENUM_CASE(PrimaryID, kUnspecified)
|
||||
PRINT_ENUM_CASE(PrimaryID, kBT470M)
|
||||
PRINT_ENUM_CASE(PrimaryID, kBT470BG)
|
||||
PRINT_ENUM_CASE(PrimaryID, kSMPTE170M)
|
||||
PRINT_ENUM_CASE(PrimaryID, kSMPTE240M)
|
||||
PRINT_ENUM_CASE(PrimaryID, kFILM)
|
||||
PRINT_ENUM_CASE(PrimaryID, kBT2020)
|
||||
PRINT_ENUM_CASE(PrimaryID, kSMPTEST428)
|
||||
PRINT_ENUM_CASE(PrimaryID, kSMPTEST431)
|
||||
PRINT_ENUM_CASE(PrimaryID, kSMPTEST432)
|
||||
PRINT_ENUM_CASE(PrimaryID, kJEDECP22)
|
||||
}
|
||||
ss << ", transfer:";
|
||||
switch (transfer_) {
|
||||
PRINT_ENUM_CASE(TransferID, kBT709)
|
||||
PRINT_ENUM_CASE(TransferID, kUnspecified)
|
||||
PRINT_ENUM_CASE(TransferID, kGAMMA22)
|
||||
PRINT_ENUM_CASE(TransferID, kGAMMA28)
|
||||
PRINT_ENUM_CASE(TransferID, kSMPTE170M)
|
||||
PRINT_ENUM_CASE(TransferID, kSMPTE240M)
|
||||
PRINT_ENUM_CASE(TransferID, kLINEAR)
|
||||
PRINT_ENUM_CASE(TransferID, kLOG)
|
||||
PRINT_ENUM_CASE(TransferID, kLOG_SQRT)
|
||||
PRINT_ENUM_CASE(TransferID, kIEC61966_2_4)
|
||||
PRINT_ENUM_CASE(TransferID, kBT1361_ECG)
|
||||
PRINT_ENUM_CASE(TransferID, kIEC61966_2_1)
|
||||
PRINT_ENUM_CASE(TransferID, kBT2020_10)
|
||||
PRINT_ENUM_CASE(TransferID, kBT2020_12)
|
||||
PRINT_ENUM_CASE(TransferID, kSMPTEST2084)
|
||||
PRINT_ENUM_CASE(TransferID, kSMPTEST428)
|
||||
PRINT_ENUM_CASE(TransferID, kARIB_STD_B67)
|
||||
}
|
||||
ss << ", matrix:";
|
||||
switch (matrix_) {
|
||||
PRINT_ENUM_CASE(MatrixID, kRGB)
|
||||
PRINT_ENUM_CASE(MatrixID, kBT709)
|
||||
PRINT_ENUM_CASE(MatrixID, kUnspecified)
|
||||
PRINT_ENUM_CASE(MatrixID, kFCC)
|
||||
PRINT_ENUM_CASE(MatrixID, kBT470BG)
|
||||
PRINT_ENUM_CASE(MatrixID, kSMPTE170M)
|
||||
PRINT_ENUM_CASE(MatrixID, kSMPTE240M)
|
||||
PRINT_ENUM_CASE(MatrixID, kYCOCG)
|
||||
PRINT_ENUM_CASE(MatrixID, kBT2020_NCL)
|
||||
PRINT_ENUM_CASE(MatrixID, kBT2020_CL)
|
||||
PRINT_ENUM_CASE(MatrixID, kSMPTE2085)
|
||||
PRINT_ENUM_CASE(MatrixID, kCDNCLS)
|
||||
PRINT_ENUM_CASE(MatrixID, kCDCLS)
|
||||
PRINT_ENUM_CASE(MatrixID, kBT2100_ICTCP)
|
||||
}
|
||||
|
||||
ss << ", range:";
|
||||
switch (range_) {
|
||||
PRINT_ENUM_CASE(RangeID, kInvalid)
|
||||
PRINT_ENUM_CASE(RangeID, kLimited)
|
||||
PRINT_ENUM_CASE(RangeID, kFull)
|
||||
PRINT_ENUM_CASE(RangeID, kDerived)
|
||||
}
|
||||
ss << "}";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#undef PRINT_ENUM_CASE
|
||||
|
||||
bool ColorSpace::set_primaries_from_uint8(uint8_t enum_value) {
|
||||
constexpr PrimaryID kPrimaryIds[] = {
|
||||
PrimaryID::kBT709, PrimaryID::kUnspecified, PrimaryID::kBT470M,
|
||||
@ -181,7 +263,7 @@ bool ColorSpace::set_chroma_siting_vertical_from_uint8(uint8_t enum_value) {
|
||||
|
||||
void ColorSpace::set_hdr_metadata(const HdrMetadata* hdr_metadata) {
|
||||
hdr_metadata_ =
|
||||
hdr_metadata ? absl::make_optional(*hdr_metadata) : absl::nullopt;
|
||||
hdr_metadata ? std::make_optional(*hdr_metadata) : std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -13,7 +13,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "api/video/hdr_metadata.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
@ -101,7 +103,7 @@ class RTC_EXPORT ColorSpace {
|
||||
kInvalid = 0,
|
||||
// Limited Rec. 709 color range with RGB values ranging from 16 to 235.
|
||||
kLimited = 1,
|
||||
// Full RGB color range with RGB valees from 0 to 255.
|
||||
// Full RGB color range with RGB values from 0 to 255.
|
||||
kFull = 2,
|
||||
// Range is defined by MatrixCoefficients/TransferCharacteristics.
|
||||
kDerived = 3,
|
||||
@ -155,6 +157,7 @@ class RTC_EXPORT ColorSpace {
|
||||
ChromaSiting chroma_siting_horizontal() const;
|
||||
ChromaSiting chroma_siting_vertical() const;
|
||||
const HdrMetadata* hdr_metadata() const;
|
||||
std::string AsString() const;
|
||||
|
||||
bool set_primaries_from_uint8(uint8_t enum_value);
|
||||
bool set_transfer_from_uint8(uint8_t enum_value);
|
||||
@ -171,7 +174,7 @@ class RTC_EXPORT ColorSpace {
|
||||
RangeID range_ = RangeID::kInvalid;
|
||||
ChromaSiting chroma_siting_horizontal_ = ChromaSiting::kUnspecified;
|
||||
ChromaSiting chroma_siting_vertical_ = ChromaSiting::kUnspecified;
|
||||
absl::optional<HdrMetadata> hdr_metadata_;
|
||||
std::optional<HdrMetadata> hdr_metadata_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -10,21 +10,9 @@
|
||||
|
||||
#include "api/video/video_content_type.h"
|
||||
|
||||
// VideoContentType stored as a single byte, which is sent over the network.
|
||||
// Structure:
|
||||
//
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +---------------+
|
||||
// |r r e e e s s c|
|
||||
//
|
||||
// where:
|
||||
// r - reserved bits.
|
||||
// e - 3-bit number of an experiment group counted from 1. 0 means there's no
|
||||
// experiment ongoing.
|
||||
// s - 2-bit simulcast stream id or spatial layer, counted from 1. 0 means that
|
||||
// no simulcast information is set.
|
||||
// c - content type. 0 means real-time video, 1 means screenshare.
|
||||
//
|
||||
#include <cstdint>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace videocontenttypehelpers {
|
||||
@ -33,57 +21,21 @@ namespace {
|
||||
static constexpr uint8_t kScreenshareBitsSize = 1;
|
||||
static constexpr uint8_t kScreenshareBitsMask =
|
||||
(1u << kScreenshareBitsSize) - 1;
|
||||
|
||||
static constexpr uint8_t kSimulcastShift = 1;
|
||||
static constexpr uint8_t kSimulcastBitsSize = 2;
|
||||
static constexpr uint8_t kSimulcastBitsMask = ((1u << kSimulcastBitsSize) - 1)
|
||||
<< kSimulcastShift; // 0b00000110
|
||||
|
||||
static constexpr uint8_t kExperimentShift = 3;
|
||||
static constexpr uint8_t kExperimentBitsSize = 3;
|
||||
static constexpr uint8_t kExperimentBitsMask =
|
||||
((1u << kExperimentBitsSize) - 1) << kExperimentShift; // 0b00111000
|
||||
|
||||
static constexpr uint8_t kTotalBitsSize =
|
||||
kScreenshareBitsSize + kSimulcastBitsSize + kExperimentBitsSize;
|
||||
} // namespace
|
||||
|
||||
bool SetExperimentId(VideoContentType* content_type, uint8_t experiment_id) {
|
||||
// Store in bits 2-4.
|
||||
if (experiment_id >= (1 << kExperimentBitsSize))
|
||||
return false;
|
||||
*content_type = static_cast<VideoContentType>(
|
||||
(static_cast<uint8_t>(*content_type) & ~kExperimentBitsMask) |
|
||||
((experiment_id << kExperimentShift) & kExperimentBitsMask));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetSimulcastId(VideoContentType* content_type, uint8_t simulcast_id) {
|
||||
// Store in bits 5-6.
|
||||
if (simulcast_id >= (1 << kSimulcastBitsSize))
|
||||
return false;
|
||||
*content_type = static_cast<VideoContentType>(
|
||||
(static_cast<uint8_t>(*content_type) & ~kSimulcastBitsMask) |
|
||||
((simulcast_id << kSimulcastShift) & kSimulcastBitsMask));
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t GetExperimentId(const VideoContentType& content_type) {
|
||||
return (static_cast<uint8_t>(content_type) & kExperimentBitsMask) >>
|
||||
kExperimentShift;
|
||||
}
|
||||
uint8_t GetSimulcastId(const VideoContentType& content_type) {
|
||||
return (static_cast<uint8_t>(content_type) & kSimulcastBitsMask) >>
|
||||
kSimulcastShift;
|
||||
}
|
||||
|
||||
bool IsScreenshare(const VideoContentType& content_type) {
|
||||
// Ensure no bits apart from the screenshare bit is set.
|
||||
// This CHECK is a temporary measure to detect code that introduces
|
||||
// values according to old versions.
|
||||
RTC_CHECK((static_cast<uint8_t>(content_type) & !kScreenshareBitsMask) == 0);
|
||||
return (static_cast<uint8_t>(content_type) & kScreenshareBitsMask) > 0;
|
||||
}
|
||||
|
||||
bool IsValidContentType(uint8_t value) {
|
||||
// Any 6-bit value is allowed.
|
||||
return value < (1 << kTotalBitsSize);
|
||||
// Only the screenshare bit is allowed.
|
||||
// However, due to previous usage of the next 5 bits, we allow
|
||||
// the lower 6 bits to be set.
|
||||
return value < (1 << 6);
|
||||
}
|
||||
|
||||
const char* ToString(const VideoContentType& content_type) {
|
||||
|
@ -15,18 +15,15 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// VideoContentType stored as a single byte, which is sent over the network
|
||||
// in the rtp-hdrext/video-content-type extension.
|
||||
// Only the lowest bit is used, per the enum.
|
||||
enum class VideoContentType : uint8_t {
|
||||
UNSPECIFIED = 0,
|
||||
SCREENSHARE = 1,
|
||||
};
|
||||
|
||||
namespace videocontenttypehelpers {
|
||||
bool SetExperimentId(VideoContentType* content_type, uint8_t experiment_id);
|
||||
bool SetSimulcastId(VideoContentType* content_type, uint8_t simulcast_id);
|
||||
|
||||
uint8_t GetExperimentId(const VideoContentType& content_type);
|
||||
uint8_t GetSimulcastId(const VideoContentType& content_type);
|
||||
|
||||
bool IsScreenshare(const VideoContentType& content_type);
|
||||
|
||||
bool IsValidContentType(uint8_t value);
|
||||
|
@ -10,7 +10,12 @@
|
||||
|
||||
#include "api/video/video_timing.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
@ -25,6 +30,14 @@ uint16_t VideoSendTiming::GetDeltaCappedMs(int64_t base_ms, int64_t time_ms) {
|
||||
return rtc::saturated_cast<uint16_t>(time_ms - base_ms);
|
||||
}
|
||||
|
||||
uint16_t VideoSendTiming::GetDeltaCappedMs(TimeDelta delta) {
|
||||
if (delta < TimeDelta::Zero()) {
|
||||
RTC_DLOG(LS_ERROR) << "Delta " << delta.ms()
|
||||
<< "ms expected to be positive";
|
||||
}
|
||||
return rtc::saturated_cast<uint16_t>(delta.ms());
|
||||
}
|
||||
|
||||
TimingFrameInfo::TimingFrameInfo()
|
||||
: rtp_timestamp(0),
|
||||
capture_time_ms(-1),
|
||||
@ -89,4 +102,23 @@ std::string TimingFrameInfo::ToString() const {
|
||||
return sb.str();
|
||||
}
|
||||
|
||||
VideoPlayoutDelay::VideoPlayoutDelay(TimeDelta min, TimeDelta max)
|
||||
: min_(std::clamp(min, TimeDelta::Zero(), kMax)),
|
||||
max_(std::clamp(max, min_, kMax)) {
|
||||
if (!(TimeDelta::Zero() <= min && min <= max && max <= kMax)) {
|
||||
RTC_LOG(LS_ERROR) << "Invalid video playout delay: [" << min << "," << max
|
||||
<< "]. Clamped to [" << this->min() << "," << this->max()
|
||||
<< "]";
|
||||
}
|
||||
}
|
||||
|
||||
bool VideoPlayoutDelay::Set(TimeDelta min, TimeDelta max) {
|
||||
if (TimeDelta::Zero() <= min && min <= max && max <= kMax) {
|
||||
min_ = min;
|
||||
max_ = max;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -16,11 +16,14 @@
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Video timing timestamps in ms counted from capture_time_ms of a frame.
|
||||
// This structure represents data sent in video-timing RTP header extension.
|
||||
struct VideoSendTiming {
|
||||
struct RTC_EXPORT VideoSendTiming {
|
||||
enum TimingFrameFlags : uint8_t {
|
||||
kNotTriggered = 0, // Timing info valid, but not to be transmitted.
|
||||
// Used on send-side only.
|
||||
@ -34,6 +37,7 @@ struct VideoSendTiming {
|
||||
// https://webrtc.org/experiments/rtp-hdrext/video-timing/ extension stores
|
||||
// 16-bit deltas of timestamps from packet capture time.
|
||||
static uint16_t GetDeltaCappedMs(int64_t base_ms, int64_t time_ms);
|
||||
static uint16_t GetDeltaCappedMs(TimeDelta delta);
|
||||
|
||||
uint16_t encode_start_delta_ms;
|
||||
uint16_t encode_finish_delta_ms;
|
||||
@ -41,21 +45,21 @@ struct VideoSendTiming {
|
||||
uint16_t pacer_exit_delta_ms;
|
||||
uint16_t network_timestamp_delta_ms;
|
||||
uint16_t network2_timestamp_delta_ms;
|
||||
uint8_t flags;
|
||||
uint8_t flags = TimingFrameFlags::kInvalid;
|
||||
};
|
||||
|
||||
// Used to report precise timings of a 'timing frames'. Contains all important
|
||||
// timestamps for a lifetime of that specific frame. Reported as a string via
|
||||
// GetStats(). Only frame which took the longest between two GetStats calls is
|
||||
// reported.
|
||||
struct TimingFrameInfo {
|
||||
struct RTC_EXPORT TimingFrameInfo {
|
||||
TimingFrameInfo();
|
||||
|
||||
// Returns end-to-end delay of a frame, if sender and receiver timestamps are
|
||||
// synchronized, -1 otherwise.
|
||||
int64_t EndToEndDelay() const;
|
||||
|
||||
// Returns true if current frame took longer to process than |other| frame.
|
||||
// Returns true if current frame took longer to process than `other` frame.
|
||||
// If other frame's clocks are not synchronized, current frame is always
|
||||
// preferred.
|
||||
bool IsLongerThan(const TimingFrameInfo& other) const;
|
||||
@ -103,26 +107,43 @@ struct TimingFrameInfo {
|
||||
// Minimum and maximum playout delay values from capture to render.
|
||||
// These are best effort values.
|
||||
//
|
||||
// A value < 0 indicates no change from previous valid value.
|
||||
//
|
||||
// min = max = 0 indicates that the receiver should try and render
|
||||
// frame as soon as possible.
|
||||
//
|
||||
// min = x, max = y indicates that the receiver is free to adapt
|
||||
// in the range (x, y) based on network jitter.
|
||||
struct VideoPlayoutDelay {
|
||||
VideoPlayoutDelay() = default;
|
||||
VideoPlayoutDelay(int min_ms, int max_ms) : min_ms(min_ms), max_ms(max_ms) {}
|
||||
int min_ms = -1;
|
||||
int max_ms = -1;
|
||||
// This class ensures invariant 0 <= min <= max <= kMax.
|
||||
class RTC_EXPORT VideoPlayoutDelay {
|
||||
public:
|
||||
// Maximum supported value for the delay limit.
|
||||
static constexpr TimeDelta kMax = TimeDelta::Millis(10) * 0xFFF;
|
||||
|
||||
bool operator==(const VideoPlayoutDelay& rhs) const {
|
||||
return min_ms == rhs.min_ms && max_ms == rhs.max_ms;
|
||||
// Creates delay limits that indicates receiver should try to render frame
|
||||
// as soon as possible.
|
||||
static VideoPlayoutDelay Minimal() {
|
||||
return VideoPlayoutDelay(TimeDelta::Zero(), TimeDelta::Zero());
|
||||
}
|
||||
};
|
||||
|
||||
// TODO(bugs.webrtc.org/7660): Old name, delete after downstream use is updated.
|
||||
using PlayoutDelay = VideoPlayoutDelay;
|
||||
// Creates valid, but unspecified limits.
|
||||
VideoPlayoutDelay() = default;
|
||||
VideoPlayoutDelay(const VideoPlayoutDelay&) = default;
|
||||
VideoPlayoutDelay& operator=(const VideoPlayoutDelay&) = default;
|
||||
VideoPlayoutDelay(TimeDelta min, TimeDelta max);
|
||||
|
||||
bool Set(TimeDelta min, TimeDelta max);
|
||||
|
||||
TimeDelta min() const { return min_; }
|
||||
TimeDelta max() const { return max_; }
|
||||
|
||||
friend bool operator==(const VideoPlayoutDelay& lhs,
|
||||
const VideoPlayoutDelay& rhs) {
|
||||
return lhs.min_ == rhs.min_ && lhs.max_ == rhs.max_;
|
||||
}
|
||||
|
||||
private:
|
||||
TimeDelta min_ = TimeDelta::Zero();
|
||||
TimeDelta max_ = kMax;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -23,12 +23,13 @@ rtc_library("audio_frame_operations") {
|
||||
]
|
||||
|
||||
deps = [
|
||||
"../../api:array_view",
|
||||
"../../api/audio:audio_frame_api",
|
||||
"../../common_audio",
|
||||
"../../rtc_base:checks",
|
||||
"../../rtc_base:deprecation",
|
||||
"../../rtc_base:rtc_base_approved",
|
||||
"../../system_wrappers:field_trial",
|
||||
"../../rtc_base:logging",
|
||||
"../../rtc_base:safe_conversions",
|
||||
"//third_party/abseil-cpp/absl/base:core_headers",
|
||||
]
|
||||
}
|
||||
|
||||
@ -44,8 +45,9 @@ if (rtc_include_tests) {
|
||||
":audio_frame_operations",
|
||||
"../../api/audio:audio_frame_api",
|
||||
"../../rtc_base:checks",
|
||||
"../../rtc_base:rtc_base_approved",
|
||||
"../../test:field_trial",
|
||||
"../../rtc_base:logging",
|
||||
"../../rtc_base:macromagic",
|
||||
"../../rtc_base:stringutils",
|
||||
"../../test:test_support",
|
||||
"//testing/gtest",
|
||||
]
|
||||
|
@ -29,72 +29,17 @@ const float kMuteFadeInc = 1.0f / kMuteFadeFrames;
|
||||
|
||||
} // namespace
|
||||
|
||||
void AudioFrameOperations::Add(const AudioFrame& frame_to_add,
|
||||
AudioFrame* result_frame) {
|
||||
// Sanity check.
|
||||
RTC_DCHECK(result_frame);
|
||||
RTC_DCHECK_GT(result_frame->num_channels_, 0);
|
||||
RTC_DCHECK_EQ(result_frame->num_channels_, frame_to_add.num_channels_);
|
||||
|
||||
bool no_previous_data = result_frame->muted();
|
||||
if (result_frame->samples_per_channel_ != frame_to_add.samples_per_channel_) {
|
||||
// Special case we have no data to start with.
|
||||
RTC_DCHECK_EQ(result_frame->samples_per_channel_, 0);
|
||||
result_frame->samples_per_channel_ = frame_to_add.samples_per_channel_;
|
||||
no_previous_data = true;
|
||||
}
|
||||
|
||||
if (result_frame->vad_activity_ == AudioFrame::kVadActive ||
|
||||
frame_to_add.vad_activity_ == AudioFrame::kVadActive) {
|
||||
result_frame->vad_activity_ = AudioFrame::kVadActive;
|
||||
} else if (result_frame->vad_activity_ == AudioFrame::kVadUnknown ||
|
||||
frame_to_add.vad_activity_ == AudioFrame::kVadUnknown) {
|
||||
result_frame->vad_activity_ = AudioFrame::kVadUnknown;
|
||||
}
|
||||
|
||||
if (result_frame->speech_type_ != frame_to_add.speech_type_)
|
||||
result_frame->speech_type_ = AudioFrame::kUndefined;
|
||||
|
||||
if (!frame_to_add.muted()) {
|
||||
const int16_t* in_data = frame_to_add.data();
|
||||
int16_t* out_data = result_frame->mutable_data();
|
||||
size_t length =
|
||||
frame_to_add.samples_per_channel_ * frame_to_add.num_channels_;
|
||||
if (no_previous_data) {
|
||||
std::copy(in_data, in_data + length, out_data);
|
||||
} else {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
const int32_t wrap_guard = static_cast<int32_t>(out_data[i]) +
|
||||
static_cast<int32_t>(in_data[i]);
|
||||
out_data[i] = rtc::saturated_cast<int16_t>(wrap_guard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int AudioFrameOperations::MonoToStereo(AudioFrame* frame) {
|
||||
if (frame->num_channels_ != 1) {
|
||||
return -1;
|
||||
}
|
||||
UpmixChannels(2, frame);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AudioFrameOperations::StereoToMono(AudioFrame* frame) {
|
||||
if (frame->num_channels_ != 2) {
|
||||
return -1;
|
||||
}
|
||||
DownmixChannels(1, frame);
|
||||
return frame->num_channels_ == 1 ? 0 : -1;
|
||||
}
|
||||
|
||||
void AudioFrameOperations::QuadToStereo(const int16_t* src_audio,
|
||||
size_t samples_per_channel,
|
||||
int16_t* dst_audio) {
|
||||
for (size_t i = 0; i < samples_per_channel; i++) {
|
||||
dst_audio[i * 2] =
|
||||
void AudioFrameOperations::QuadToStereo(
|
||||
InterleavedView<const int16_t> src_audio,
|
||||
InterleavedView<int16_t> dst_audio) {
|
||||
RTC_DCHECK_EQ(NumChannels(src_audio), 4);
|
||||
RTC_DCHECK_EQ(NumChannels(dst_audio), 2);
|
||||
RTC_DCHECK_EQ(SamplesPerChannel(src_audio), SamplesPerChannel(dst_audio));
|
||||
for (size_t i = 0; i < SamplesPerChannel(src_audio); ++i) {
|
||||
auto dst_frame = i * 2;
|
||||
dst_audio[dst_frame] =
|
||||
(static_cast<int32_t>(src_audio[4 * i]) + src_audio[4 * i + 1]) >> 1;
|
||||
dst_audio[i * 2 + 1] =
|
||||
dst_audio[dst_frame + 1] =
|
||||
(static_cast<int32_t>(src_audio[4 * i + 2]) + src_audio[4 * i + 3]) >>
|
||||
1;
|
||||
}
|
||||
@ -109,30 +54,34 @@ int AudioFrameOperations::QuadToStereo(AudioFrame* frame) {
|
||||
AudioFrame::kMaxDataSizeSamples);
|
||||
|
||||
if (!frame->muted()) {
|
||||
QuadToStereo(frame->data(), frame->samples_per_channel_,
|
||||
frame->mutable_data());
|
||||
}
|
||||
// Note that `src` and `dst` will map in to the same buffer, but the call
|
||||
// to `mutable_data()` changes the layout of `frame`, so `src` and `dst`
|
||||
// will have different dimensions (important to call `data_view()` first).
|
||||
auto src = frame->data_view();
|
||||
auto dst = frame->mutable_data(frame->samples_per_channel_, 2);
|
||||
QuadToStereo(src, dst);
|
||||
} else {
|
||||
frame->num_channels_ = 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AudioFrameOperations::DownmixChannels(const int16_t* src_audio,
|
||||
size_t src_channels,
|
||||
size_t samples_per_channel,
|
||||
size_t dst_channels,
|
||||
int16_t* dst_audio) {
|
||||
if (src_channels > 1 && dst_channels == 1) {
|
||||
DownmixInterleavedToMono(src_audio, samples_per_channel, src_channels,
|
||||
dst_audio);
|
||||
return;
|
||||
} else if (src_channels == 4 && dst_channels == 2) {
|
||||
QuadToStereo(src_audio, samples_per_channel, dst_audio);
|
||||
return;
|
||||
void AudioFrameOperations::DownmixChannels(
|
||||
InterleavedView<const int16_t> src_audio,
|
||||
InterleavedView<int16_t> dst_audio) {
|
||||
RTC_DCHECK_EQ(SamplesPerChannel(src_audio), SamplesPerChannel(dst_audio));
|
||||
if (NumChannels(src_audio) > 1 && IsMono(dst_audio)) {
|
||||
// TODO(tommi): change DownmixInterleavedToMono to support InterleavedView
|
||||
// and MonoView.
|
||||
DownmixInterleavedToMono(&src_audio.data()[0], SamplesPerChannel(src_audio),
|
||||
NumChannels(src_audio), &dst_audio.data()[0]);
|
||||
} else if (NumChannels(src_audio) == 4 && NumChannels(dst_audio) == 2) {
|
||||
QuadToStereo(src_audio, dst_audio);
|
||||
} else {
|
||||
RTC_DCHECK_NOTREACHED() << "src_channels: " << NumChannels(src_audio)
|
||||
<< ", dst_channels: " << NumChannels(dst_audio);
|
||||
}
|
||||
|
||||
RTC_NOTREACHED() << "src_channels: " << src_channels
|
||||
<< ", dst_channels: " << dst_channels;
|
||||
}
|
||||
|
||||
void AudioFrameOperations::DownmixChannels(size_t dst_channels,
|
||||
@ -149,7 +98,7 @@ void AudioFrameOperations::DownmixChannels(size_t dst_channels,
|
||||
int err = QuadToStereo(frame);
|
||||
RTC_DCHECK_EQ(err, 0);
|
||||
} else {
|
||||
RTC_NOTREACHED() << "src_channels: " << frame->num_channels_
|
||||
RTC_DCHECK_NOTREACHED() << "src_channels: " << frame->num_channels_
|
||||
<< ", dst_channels: " << dst_channels;
|
||||
}
|
||||
}
|
||||
@ -169,14 +118,16 @@ void AudioFrameOperations::UpmixChannels(size_t target_number_of_channels,
|
||||
if (!frame->muted()) {
|
||||
// Up-mixing done in place. Going backwards through the frame ensure nothing
|
||||
// is irrevocably overwritten.
|
||||
for (int i = frame->samples_per_channel_ - 1; i >= 0; i--) {
|
||||
auto frame_data = frame->mutable_data(frame->samples_per_channel_,
|
||||
target_number_of_channels);
|
||||
for (int i = frame->samples_per_channel_ - 1; i >= 0; --i) {
|
||||
for (size_t j = 0; j < target_number_of_channels; ++j) {
|
||||
frame->mutable_data()[target_number_of_channels * i + j] =
|
||||
frame->data()[i];
|
||||
}
|
||||
frame_data[target_number_of_channels * i + j] = frame_data[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
frame->num_channels_ = target_number_of_channels;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioFrameOperations::SwapStereoChannels(AudioFrame* frame) {
|
||||
@ -222,14 +173,14 @@ void AudioFrameOperations::Mute(AudioFrame* frame,
|
||||
size_t end = count;
|
||||
float start_g = 0.0f;
|
||||
if (current_frame_muted) {
|
||||
// Fade out the last |count| samples of frame.
|
||||
// Fade out the last `count` samples of frame.
|
||||
RTC_DCHECK(!previous_frame_muted);
|
||||
start = frame->samples_per_channel_ - count;
|
||||
end = frame->samples_per_channel_;
|
||||
start_g = 1.0f;
|
||||
inc = -inc;
|
||||
} else {
|
||||
// Fade in the first |count| samples of frame.
|
||||
// Fade in the first `count` samples of frame.
|
||||
RTC_DCHECK(previous_frame_muted);
|
||||
}
|
||||
|
||||
@ -250,35 +201,6 @@ void AudioFrameOperations::Mute(AudioFrame* frame) {
|
||||
Mute(frame, true, true);
|
||||
}
|
||||
|
||||
void AudioFrameOperations::ApplyHalfGain(AudioFrame* frame) {
|
||||
RTC_DCHECK(frame);
|
||||
RTC_DCHECK_GT(frame->num_channels_, 0);
|
||||
if (frame->num_channels_ < 1 || frame->muted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t* frame_data = frame->mutable_data();
|
||||
for (size_t i = 0; i < frame->samples_per_channel_ * frame->num_channels_;
|
||||
i++) {
|
||||
frame_data[i] = frame_data[i] >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
int AudioFrameOperations::Scale(float left, float right, AudioFrame* frame) {
|
||||
if (frame->num_channels_ != 2) {
|
||||
return -1;
|
||||
} else if (frame->muted()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t* frame_data = frame->mutable_data();
|
||||
for (size_t i = 0; i < frame->samples_per_channel_; i++) {
|
||||
frame_data[2 * i] = static_cast<int16_t>(left * frame_data[2 * i]);
|
||||
frame_data[2 * i + 1] = static_cast<int16_t>(right * frame_data[2 * i + 1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AudioFrameOperations::ScaleWithSat(float scale, AudioFrame* frame) {
|
||||
if (frame->muted()) {
|
||||
return 0;
|
||||
|
@ -14,8 +14,9 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/audio/audio_frame.h"
|
||||
#include "rtc_base/deprecation.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -24,67 +25,44 @@ namespace webrtc {
|
||||
// than a class.
|
||||
class AudioFrameOperations {
|
||||
public:
|
||||
// Add samples in |frame_to_add| with samples in |result_frame|
|
||||
// putting the results in |results_frame|. The fields
|
||||
// |vad_activity_| and |speech_type_| of the result frame are
|
||||
// updated. If |result_frame| is empty (|samples_per_channel_|==0),
|
||||
// the samples in |frame_to_add| are added to it. The number of
|
||||
// channels and number of samples per channel must match except when
|
||||
// |result_frame| is empty.
|
||||
static void Add(const AudioFrame& frame_to_add, AudioFrame* result_frame);
|
||||
|
||||
// |frame.num_channels_| will be updated. This version checks for sufficient
|
||||
// buffer size and that |num_channels_| is mono. Use UpmixChannels
|
||||
// instead. TODO(bugs.webrtc.org/8649): remove.
|
||||
RTC_DEPRECATED static int MonoToStereo(AudioFrame* frame);
|
||||
|
||||
// |frame.num_channels_| will be updated. This version checks that
|
||||
// |num_channels_| is stereo. Use DownmixChannels
|
||||
// instead. TODO(bugs.webrtc.org/8649): remove.
|
||||
RTC_DEPRECATED static int StereoToMono(AudioFrame* frame);
|
||||
|
||||
// Downmixes 4 channels |src_audio| to stereo |dst_audio|. This is an in-place
|
||||
// operation, meaning |src_audio| and |dst_audio| may point to the same
|
||||
// Downmixes 4 channels `src_audio` to stereo `dst_audio`. This is an in-place
|
||||
// operation, meaning `src_audio` and `dst_audio` may point to the same
|
||||
// buffer.
|
||||
static void QuadToStereo(const int16_t* src_audio,
|
||||
size_t samples_per_channel,
|
||||
int16_t* dst_audio);
|
||||
static void QuadToStereo(InterleavedView<const int16_t> src_audio,
|
||||
InterleavedView<int16_t> dst_audio);
|
||||
|
||||
// |frame.num_channels_| will be updated. This version checks that
|
||||
// |num_channels_| is 4 channels.
|
||||
// `frame.num_channels_` will be updated. This version checks that
|
||||
// `num_channels_` is 4 channels.
|
||||
static int QuadToStereo(AudioFrame* frame);
|
||||
|
||||
// Downmixes |src_channels| |src_audio| to |dst_channels| |dst_audio|.
|
||||
// This is an in-place operation, meaning |src_audio| and |dst_audio|
|
||||
// Downmixes `src_channels` `src_audio` to `dst_channels` `dst_audio`.
|
||||
// This is an in-place operation, meaning `src_audio` and `dst_audio`
|
||||
// may point to the same buffer. Supported channel combinations are
|
||||
// Stereo to Mono, Quad to Mono, and Quad to Stereo.
|
||||
static void DownmixChannels(const int16_t* src_audio,
|
||||
size_t src_channels,
|
||||
size_t samples_per_channel,
|
||||
size_t dst_channels,
|
||||
int16_t* dst_audio);
|
||||
static void DownmixChannels(InterleavedView<const int16_t> src_audio,
|
||||
InterleavedView<int16_t> dst_audio);
|
||||
|
||||
// |frame.num_channels_| will be updated. This version checks that
|
||||
// |num_channels_| and |dst_channels| are valid and performs relevant downmix.
|
||||
// `frame.num_channels_` will be updated. This version checks that
|
||||
// `num_channels_` and `dst_channels` are valid and performs relevant downmix.
|
||||
// Supported channel combinations are N channels to Mono, and Quad to Stereo.
|
||||
static void DownmixChannels(size_t dst_channels, AudioFrame* frame);
|
||||
|
||||
// |frame.num_channels_| will be updated. This version checks that
|
||||
// |num_channels_| and |dst_channels| are valid and performs relevant
|
||||
// `frame.num_channels_` will be updated. This version checks that
|
||||
// `num_channels_` and `dst_channels` are valid and performs relevant
|
||||
// downmix. Supported channel combinations are Mono to N
|
||||
// channels. The single channel is replicated.
|
||||
static void UpmixChannels(size_t target_number_of_channels,
|
||||
AudioFrame* frame);
|
||||
|
||||
// Swap the left and right channels of |frame|. Fails silently if |frame| is
|
||||
// Swap the left and right channels of `frame`. Fails silently if `frame` is
|
||||
// not stereo.
|
||||
static void SwapStereoChannels(AudioFrame* frame);
|
||||
|
||||
// Conditionally zero out contents of |frame| for implementing audio mute:
|
||||
// |previous_frame_muted| && |current_frame_muted| - Zero out whole frame.
|
||||
// |previous_frame_muted| && !|current_frame_muted| - Fade-in at frame start.
|
||||
// !|previous_frame_muted| && |current_frame_muted| - Fade-out at frame end.
|
||||
// !|previous_frame_muted| && !|current_frame_muted| - Leave frame untouched.
|
||||
// Conditionally zero out contents of `frame` for implementing audio mute:
|
||||
// `previous_frame_muted` && `current_frame_muted` - Zero out whole frame.
|
||||
// `previous_frame_muted` && !`current_frame_muted` - Fade-in at frame start.
|
||||
// !`previous_frame_muted` && `current_frame_muted` - Fade-out at frame end.
|
||||
// !`previous_frame_muted` && !`current_frame_muted` - Leave frame untouched.
|
||||
static void Mute(AudioFrame* frame,
|
||||
bool previous_frame_muted,
|
||||
bool current_frame_muted);
|
||||
@ -92,11 +70,6 @@ class AudioFrameOperations {
|
||||
// Zero out contents of frame.
|
||||
static void Mute(AudioFrame* frame);
|
||||
|
||||
// Halve samples in |frame|.
|
||||
static void ApplyHalfGain(AudioFrame* frame);
|
||||
|
||||
static int Scale(float left, float right, AudioFrame* frame);
|
||||
|
||||
static int ScaleWithSat(float scale, AudioFrame* frame);
|
||||
};
|
||||
|
||||
|
@ -46,17 +46,19 @@ rtc_library("common_audio") {
|
||||
":common_audio_c",
|
||||
":sinc_resampler",
|
||||
"../api:array_view",
|
||||
"../api/audio:audio_frame_api",
|
||||
"../rtc_base:checks",
|
||||
"../rtc_base:gtest_prod",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base:logging",
|
||||
"../rtc_base:safe_conversions",
|
||||
"../rtc_base:sanitizer",
|
||||
"../rtc_base:timeutils",
|
||||
"../rtc_base/memory:aligned_malloc",
|
||||
"../rtc_base/system:arch",
|
||||
"../rtc_base/system:file_wrapper",
|
||||
"../system_wrappers",
|
||||
"third_party/ooura:fft_size_256",
|
||||
]
|
||||
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
||||
|
||||
defines = []
|
||||
|
||||
@ -180,7 +182,6 @@ rtc_library("common_audio_c") {
|
||||
":common_audio_cc",
|
||||
"../rtc_base:checks",
|
||||
"../rtc_base:compile_assert_c",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base:sanitizer",
|
||||
"../rtc_base/system:arch",
|
||||
"../system_wrappers",
|
||||
@ -196,7 +197,7 @@ rtc_library("common_audio_cc") {
|
||||
]
|
||||
|
||||
deps = [
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base:safe_conversions",
|
||||
"../system_wrappers",
|
||||
]
|
||||
}
|
||||
@ -205,7 +206,6 @@ rtc_source_set("sinc_resampler") {
|
||||
sources = [ "resampler/sinc_resampler.h" ]
|
||||
deps = [
|
||||
"../rtc_base:gtest_prod",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base/memory:aligned_malloc",
|
||||
"../rtc_base/system:arch",
|
||||
"../system_wrappers",
|
||||
@ -228,7 +228,6 @@ rtc_library("fir_filter_factory") {
|
||||
deps = [
|
||||
":fir_filter",
|
||||
"../rtc_base:checks",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base/system:arch",
|
||||
"../system_wrappers",
|
||||
]
|
||||
@ -257,7 +256,6 @@ if (current_cpu == "x86" || current_cpu == "x64") {
|
||||
":fir_filter",
|
||||
":sinc_resampler",
|
||||
"../rtc_base:checks",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base/memory:aligned_malloc",
|
||||
]
|
||||
}
|
||||
@ -282,7 +280,6 @@ if (current_cpu == "x86" || current_cpu == "x64") {
|
||||
":fir_filter",
|
||||
":sinc_resampler",
|
||||
"../rtc_base:checks",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base/memory:aligned_malloc",
|
||||
]
|
||||
}
|
||||
@ -307,7 +304,6 @@ if (rtc_build_with_neon) {
|
||||
":fir_filter",
|
||||
":sinc_resampler",
|
||||
"../rtc_base:checks",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base/memory:aligned_malloc",
|
||||
]
|
||||
}
|
||||
@ -329,13 +325,12 @@ if (rtc_build_with_neon) {
|
||||
deps = [
|
||||
":common_audio_c",
|
||||
"../rtc_base:checks",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base/system:arch",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if (rtc_include_tests) {
|
||||
if (rtc_include_tests && !build_with_chromium) {
|
||||
rtc_test("common_audio_unittests") {
|
||||
visibility += webrtc_default_visibility
|
||||
testonly = true
|
||||
@ -378,8 +373,10 @@ if (rtc_include_tests) {
|
||||
":fir_filter_factory",
|
||||
":sinc_resampler",
|
||||
"../rtc_base:checks",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base:macromagic",
|
||||
"../rtc_base:rtc_base_tests_utils",
|
||||
"../rtc_base:stringutils",
|
||||
"../rtc_base:timeutils",
|
||||
"../rtc_base/system:arch",
|
||||
"../system_wrappers",
|
||||
"../test:fileutils",
|
||||
|
@ -15,12 +15,10 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Format conversion (remixing and resampling) for audio. Only simple remixing
|
||||
// conversions are supported: downmix to mono (i.e. |dst_channels| == 1) or
|
||||
// conversions are supported: downmix to mono (i.e. `dst_channels` == 1) or
|
||||
// upmix from mono (i.e. |src_channels == 1|).
|
||||
//
|
||||
// The source and destination chunks have the same duration in time; specifying
|
||||
@ -35,8 +33,11 @@ class AudioConverter {
|
||||
size_t dst_frames);
|
||||
virtual ~AudioConverter() {}
|
||||
|
||||
// Convert |src|, containing |src_size| samples, to |dst|, having a sample
|
||||
// capacity of |dst_capacity|. Both point to a series of buffers containing
|
||||
AudioConverter(const AudioConverter&) = delete;
|
||||
AudioConverter& operator=(const AudioConverter&) = delete;
|
||||
|
||||
// Convert `src`, containing `src_size` samples, to `dst`, having a sample
|
||||
// capacity of `dst_capacity`. Both point to a series of buffers containing
|
||||
// the samples for each channel. The sizes must correspond to the format
|
||||
// passed to Create().
|
||||
virtual void Convert(const float* const* src,
|
||||
@ -64,8 +65,6 @@ class AudioConverter {
|
||||
const size_t src_frames_;
|
||||
const size_t dst_channels_;
|
||||
const size_t dst_frames_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioConverter);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -23,21 +23,59 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// TODO: b/335805780 - Remove this method. Instead, use Deinterleave() from
|
||||
// audio_util.h which requires size checked buffer views.
|
||||
template <typename T>
|
||||
void Deinterleave(const T* interleaved,
|
||||
size_t samples_per_channel,
|
||||
size_t num_channels,
|
||||
T* const* deinterleaved) {
|
||||
for (size_t i = 0; i < num_channels; ++i) {
|
||||
T* channel = deinterleaved[i];
|
||||
size_t interleaved_idx = i;
|
||||
for (size_t j = 0; j < samples_per_channel; ++j) {
|
||||
channel[j] = interleaved[interleaved_idx];
|
||||
interleaved_idx += num_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `Interleave()` variant for cases where the deinterleaved channels aren't
|
||||
// represented by a `DeinterleavedView`.
|
||||
// TODO: b/335805780 - Remove this method. Instead, use Deinterleave() from
|
||||
// audio_util.h which requires size checked buffer views.
|
||||
template <typename T>
|
||||
void Interleave(const T* const* deinterleaved,
|
||||
size_t samples_per_channel,
|
||||
size_t num_channels,
|
||||
InterleavedView<T>& interleaved) {
|
||||
RTC_DCHECK_EQ(NumChannels(interleaved), num_channels);
|
||||
RTC_DCHECK_EQ(SamplesPerChannel(interleaved), samples_per_channel);
|
||||
for (size_t i = 0; i < num_channels; ++i) {
|
||||
const T* channel = deinterleaved[i];
|
||||
size_t interleaved_idx = i;
|
||||
for (size_t j = 0; j < samples_per_channel; ++j) {
|
||||
interleaved[interleaved_idx] = channel[j];
|
||||
interleaved_idx += num_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to encapsulate a contiguous data buffer, full or split into frequency
|
||||
// bands, with access to a pointer arrays of the deinterleaved channels and
|
||||
// bands. The buffer is zero initialized at creation.
|
||||
//
|
||||
// The buffer structure is showed below for a 2 channel and 2 bands case:
|
||||
//
|
||||
// |data_|:
|
||||
// `data_`:
|
||||
// { [ --- b1ch1 --- ] [ --- b2ch1 --- ] [ --- b1ch2 --- ] [ --- b2ch2 --- ] }
|
||||
//
|
||||
// The pointer arrays for the same example are as follows:
|
||||
//
|
||||
// |channels_|:
|
||||
// `channels_`:
|
||||
// { [ b1ch1* ] [ b1ch2* ] [ b2ch1* ] [ b2ch2* ] }
|
||||
//
|
||||
// |bands_|:
|
||||
// `bands_`:
|
||||
// { [ b1ch1* ] [ b2ch1* ] [ b1ch2* ] [ b2ch2* ] }
|
||||
template <typename T>
|
||||
class ChannelBuffer {
|
||||
@ -81,15 +119,15 @@ class ChannelBuffer {
|
||||
// If band is explicitly specificed, the channels for a specific band are
|
||||
// returned and the usage becomes: channels(band)[channel][sample].
|
||||
// Where:
|
||||
// 0 <= band < |num_bands_|
|
||||
// 0 <= channel < |num_allocated_channels_|
|
||||
// 0 <= sample < |num_frames_per_band_|
|
||||
// 0 <= band < `num_bands_`
|
||||
// 0 <= channel < `num_allocated_channels_`
|
||||
// 0 <= sample < `num_frames_per_band_`
|
||||
|
||||
// If band is not explicitly specified, the full-band channels (or lower band
|
||||
// channels) are returned and the usage becomes: channels()[channel][sample].
|
||||
// Where:
|
||||
// 0 <= channel < |num_allocated_channels_|
|
||||
// 0 <= sample < |num_frames_|
|
||||
// 0 <= channel < `num_allocated_channels_`
|
||||
// 0 <= sample < `num_frames_`
|
||||
const T* const* channels(size_t band = 0) const {
|
||||
RTC_DCHECK_LT(band, num_bands_);
|
||||
return &channels_[band * num_allocated_channels_];
|
||||
@ -109,9 +147,9 @@ class ChannelBuffer {
|
||||
// Usage:
|
||||
// bands(channel)[band][sample].
|
||||
// Where:
|
||||
// 0 <= channel < |num_channels_|
|
||||
// 0 <= band < |num_bands_|
|
||||
// 0 <= sample < |num_frames_per_band_|
|
||||
// 0 <= channel < `num_channels_`
|
||||
// 0 <= band < `num_bands_`
|
||||
// 0 <= sample < `num_frames_per_band_`
|
||||
const T* const* bands(size_t channel) const {
|
||||
RTC_DCHECK_LT(channel, num_channels_);
|
||||
RTC_DCHECK_GE(channel, 0);
|
||||
@ -129,8 +167,8 @@ class ChannelBuffer {
|
||||
return bands_view_[channel];
|
||||
}
|
||||
|
||||
// Sets the |slice| pointers to the |start_frame| position for each channel.
|
||||
// Returns |slice| for convenience.
|
||||
// Sets the `slice` pointers to the `start_frame` position for each channel.
|
||||
// Returns `slice` for convenience.
|
||||
const T* const* Slice(T** slice, size_t start_frame) const {
|
||||
RTC_DCHECK_LT(start_frame, num_frames_);
|
||||
for (size_t i = 0; i < num_channels_; ++i)
|
||||
|
@ -20,8 +20,8 @@ class FIRFilter {
|
||||
public:
|
||||
virtual ~FIRFilter() {}
|
||||
|
||||
// Filters the |in| data supplied.
|
||||
// |out| must be previously allocated and it must be at least of |length|.
|
||||
// Filters the `in` data supplied.
|
||||
// `out` must be previously allocated and it must be at least of `length`.
|
||||
virtual void Filter(const float* in, size_t length, float* out) = 0;
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ void FIRFilterAVX2::Filter(const float* in, size_t length, float* out) {
|
||||
|
||||
memcpy(&state_[state_length_], in, length * sizeof(*in));
|
||||
|
||||
// Convolves the input signal |in| with the filter kernel |coefficients_|
|
||||
// Convolves the input signal `in` with the filter kernel `coefficients_`
|
||||
// taking into account the previous state.
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
float* in_ptr = &state_[i];
|
||||
|
@ -34,7 +34,7 @@ FIRFilterC::FIRFilterC(const float* coefficients, size_t coefficients_length)
|
||||
void FIRFilterC::Filter(const float* in, size_t length, float* out) {
|
||||
RTC_DCHECK_GT(length, 0);
|
||||
|
||||
// Convolves the input signal |in| with the filter kernel |coefficients_|
|
||||
// Convolves the input signal `in` with the filter kernel `coefficients_`
|
||||
// taking into account the previous state.
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
out[i] = 0.f;
|
||||
|
@ -28,7 +28,7 @@ FIRFilter* CreateFirFilter(const float* coefficients,
|
||||
size_t coefficients_length,
|
||||
size_t max_input_length) {
|
||||
if (!coefficients || coefficients_length <= 0 || max_input_length <= 0) {
|
||||
RTC_NOTREACHED();
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ class FIRFilter;
|
||||
// Creates a filter with the given coefficients. All initial state values will
|
||||
// be zeros.
|
||||
// The length of the chunks fed to the filter should never be greater than
|
||||
// |max_input_length|. This is needed because, when vectorizing it is
|
||||
// `max_input_length`. This is needed because, when vectorizing it is
|
||||
// necessary to concatenate the input after the state, and resizing this array
|
||||
// dynamically is expensive.
|
||||
FIRFilter* CreateFirFilter(const float* coefficients,
|
||||
|
@ -48,7 +48,7 @@ void FIRFilterNEON::Filter(const float* in, size_t length, float* out) {
|
||||
|
||||
memcpy(&state_[state_length_], in, length * sizeof(*in));
|
||||
|
||||
// Convolves the input signal |in| with the filter kernel |coefficients_|
|
||||
// Convolves the input signal `in` with the filter kernel `coefficients_`
|
||||
// taking into account the previous state.
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
float* in_ptr = &state_[i];
|
||||
|
@ -49,7 +49,7 @@ void FIRFilterSSE2::Filter(const float* in, size_t length, float* out) {
|
||||
|
||||
memcpy(&state_[state_length_], in, length * sizeof(*in));
|
||||
|
||||
// Convolves the input signal |in| with the filter kernel |coefficients_|
|
||||
// Convolves the input signal `in` with the filter kernel `coefficients_`
|
||||
// taking into account the previous state.
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
float* in_ptr = &state_[i];
|
||||
|
@ -18,12 +18,24 @@
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#include "api/audio/audio_view.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
typedef std::numeric_limits<int16_t> limits_int16;
|
||||
|
||||
// TODO(tommi, peah): Move these constants to their own header, e.g.
|
||||
// `audio_constants.h`. Also consider if they should be in api/.
|
||||
|
||||
// Absolute highest acceptable sample rate supported for audio processing,
|
||||
// capture and codecs. Note that for some components some cases a lower limit
|
||||
// applies which typically is 48000 but in some cases is lower.
|
||||
constexpr int kMaxSampleRateHz = 384000;
|
||||
|
||||
// Number of samples per channel for 10ms of audio at the highest sample rate.
|
||||
constexpr size_t kMaxSamplesPerChannel10ms = kMaxSampleRateHz / 100u;
|
||||
|
||||
// The conversion functions use the following naming convention:
|
||||
// S16: int16_t [-32768, 32767]
|
||||
// Float: float [-1.0, 1.0]
|
||||
@ -91,9 +103,10 @@ inline float FloatS16ToDbfs(float v) {
|
||||
return 20.0f * std::log10(v) + kMinDbfs;
|
||||
}
|
||||
|
||||
// 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|.
|
||||
// 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.
|
||||
template <typename T>
|
||||
void CopyAudioIfNeeded(const T* const* src,
|
||||
int num_frames,
|
||||
@ -106,17 +119,20 @@ void CopyAudioIfNeeded(const T* const* src,
|
||||
}
|
||||
}
|
||||
|
||||
// Deinterleave audio from |interleaved| to the channel buffers pointed to
|
||||
// by |deinterleaved|. There must be sufficient space allocated in the
|
||||
// |deinterleaved| buffers (|num_channel| buffers with |samples_per_channel|
|
||||
// Deinterleave audio from `interleaved` to the channel buffers pointed to
|
||||
// by `deinterleaved`. There must be sufficient space allocated in the
|
||||
// `deinterleaved` buffers (`num_channel` buffers with `samples_per_channel`
|
||||
// per buffer).
|
||||
template <typename T>
|
||||
void Deinterleave(const T* interleaved,
|
||||
size_t samples_per_channel,
|
||||
size_t num_channels,
|
||||
T* const* deinterleaved) {
|
||||
void Deinterleave(const InterleavedView<const T>& interleaved,
|
||||
const DeinterleavedView<T>& deinterleaved) {
|
||||
RTC_DCHECK_EQ(NumChannels(interleaved), NumChannels(deinterleaved));
|
||||
RTC_DCHECK_EQ(SamplesPerChannel(interleaved),
|
||||
SamplesPerChannel(deinterleaved));
|
||||
const auto num_channels = NumChannels(interleaved);
|
||||
const auto samples_per_channel = SamplesPerChannel(interleaved);
|
||||
for (size_t i = 0; i < num_channels; ++i) {
|
||||
T* channel = deinterleaved[i];
|
||||
MonoView<T> channel = deinterleaved[i];
|
||||
size_t interleaved_idx = i;
|
||||
for (size_t j = 0; j < samples_per_channel; ++j) {
|
||||
channel[j] = interleaved[interleaved_idx];
|
||||
@ -125,56 +141,28 @@ void Deinterleave(const T* interleaved,
|
||||
}
|
||||
}
|
||||
|
||||
// Interleave audio from the channel buffers pointed to by |deinterleaved| to
|
||||
// |interleaved|. There must be sufficient space allocated in |interleaved|
|
||||
// (|samples_per_channel| * |num_channels|).
|
||||
// Interleave audio from the channel buffers pointed to by `deinterleaved` to
|
||||
// `interleaved`. There must be sufficient space allocated in `interleaved`
|
||||
// (`samples_per_channel` * `num_channels`).
|
||||
template <typename T>
|
||||
void Interleave(const T* const* deinterleaved,
|
||||
size_t samples_per_channel,
|
||||
size_t num_channels,
|
||||
T* interleaved) {
|
||||
for (size_t i = 0; i < num_channels; ++i) {
|
||||
const T* channel = deinterleaved[i];
|
||||
void Interleave(const DeinterleavedView<const T>& deinterleaved,
|
||||
const InterleavedView<T>& interleaved) {
|
||||
RTC_DCHECK_EQ(NumChannels(interleaved), NumChannels(deinterleaved));
|
||||
RTC_DCHECK_EQ(SamplesPerChannel(interleaved),
|
||||
SamplesPerChannel(deinterleaved));
|
||||
for (size_t i = 0; i < deinterleaved.num_channels(); ++i) {
|
||||
const auto channel = deinterleaved[i];
|
||||
size_t interleaved_idx = i;
|
||||
for (size_t j = 0; j < samples_per_channel; ++j) {
|
||||
for (size_t j = 0; j < deinterleaved.samples_per_channel(); ++j) {
|
||||
interleaved[interleaved_idx] = channel[j];
|
||||
interleaved_idx += num_channels;
|
||||
interleaved_idx += deinterleaved.num_channels();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copies audio from a single channel buffer pointed to by |mono| to each
|
||||
// channel of |interleaved|. There must be sufficient space allocated in
|
||||
// |interleaved| (|samples_per_channel| * |num_channels|).
|
||||
template <typename T>
|
||||
void UpmixMonoToInterleaved(const T* mono,
|
||||
int num_frames,
|
||||
int num_channels,
|
||||
T* interleaved) {
|
||||
int interleaved_idx = 0;
|
||||
for (int i = 0; i < num_frames; ++i) {
|
||||
for (int j = 0; j < num_channels; ++j) {
|
||||
interleaved[interleaved_idx++] = mono[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Intermediate>
|
||||
void DownmixToMono(const T* const* input_channels,
|
||||
size_t num_frames,
|
||||
int num_channels,
|
||||
T* out) {
|
||||
for (size_t i = 0; i < num_frames; ++i) {
|
||||
Intermediate value = input_channels[0][i];
|
||||
for (int j = 1; j < num_channels; ++j) {
|
||||
value += input_channels[j][i];
|
||||
}
|
||||
out[i] = value / num_channels;
|
||||
}
|
||||
}
|
||||
|
||||
// Downmixes an interleaved multichannel signal to a single channel by averaging
|
||||
// all channels.
|
||||
// TODO: b/335805780 - Accept InterleavedView and DeinterleavedView.
|
||||
template <typename T, typename Intermediate>
|
||||
void DownmixInterleavedToMonoImpl(const T* interleaved,
|
||||
size_t num_frames,
|
||||
@ -197,12 +185,14 @@ void DownmixInterleavedToMonoImpl(const T* interleaved,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: b/335805780 - Accept InterleavedView and DeinterleavedView.
|
||||
template <typename T>
|
||||
void DownmixInterleavedToMono(const T* interleaved,
|
||||
size_t num_frames,
|
||||
int num_channels,
|
||||
T* deinterleaved);
|
||||
|
||||
// TODO: b/335805780 - Accept InterleavedView and DeinterleavedView.
|
||||
template <>
|
||||
void DownmixInterleavedToMono<int16_t>(const int16_t* interleaved,
|
||||
size_t num_frames,
|
||||
|
@ -4,8 +4,6 @@ common_audio_sources = [
|
||||
'channel_buffer.cc',
|
||||
'fir_filter_c.cc',
|
||||
'fir_filter_factory.cc',
|
||||
'real_fourier.cc',
|
||||
'real_fourier_ooura.cc',
|
||||
'resampler/push_resampler.cc',
|
||||
'resampler/push_sinc_resampler.cc',
|
||||
'resampler/resampler.cc',
|
||||
@ -55,9 +53,6 @@ common_audio_sources = [
|
||||
'vad/vad_gmm.c',
|
||||
'vad/vad_sp.c',
|
||||
'vad/webrtc_vad.c',
|
||||
'wav_file.cc',
|
||||
'wav_header.cc',
|
||||
'window_generator.cc',
|
||||
]
|
||||
|
||||
arch_libs = []
|
||||
@ -83,8 +78,8 @@ if have_x86
|
||||
],
|
||||
dependencies: common_deps,
|
||||
include_directories: webrtc_inc,
|
||||
c_args: common_cflags + ['-mavx2', '-mfma'],
|
||||
cpp_args: common_cxxflags + ['-mavx2', '-mfma']
|
||||
c_args: common_cflags + avx_flags,
|
||||
cpp_args: common_cxxflags + avx_flags
|
||||
)
|
||||
]
|
||||
endif
|
||||
@ -99,7 +94,7 @@ if have_mips
|
||||
'signal_processing/min_max_operations_mips.c',
|
||||
'signal_processing/resample_by_2_mips.c',
|
||||
'signal_processing/vector_scaling_operations_mips.c',
|
||||
'third_party/ooura/fft_size_128/ooura_fft_mips.c',
|
||||
'third_party/ooura/fft_size_128/ooura_fft_mips.cc',
|
||||
'third_party/spl_sqrt_floor/spl_sqrt_floor_mips.c',
|
||||
]
|
||||
endif
|
||||
@ -117,7 +112,7 @@ if have_armv7
|
||||
]
|
||||
endif
|
||||
|
||||
if have_neon
|
||||
if neon_opt.enabled()
|
||||
common_audio_sources += [
|
||||
'fir_filter_neon.cc',
|
||||
'resampler/sinc_resampler_neon.cc',
|
||||
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "common_audio/real_fourier.h"
|
||||
|
||||
#include "common_audio/real_fourier_ooura.h"
|
||||
#include "common_audio/signal_processing/include/signal_processing_library.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using std::complex;
|
||||
|
||||
const size_t RealFourier::kFftBufferAlignment = 32;
|
||||
|
||||
std::unique_ptr<RealFourier> RealFourier::Create(int fft_order) {
|
||||
return std::unique_ptr<RealFourier>(new RealFourierOoura(fft_order));
|
||||
}
|
||||
|
||||
int RealFourier::FftOrder(size_t length) {
|
||||
RTC_CHECK_GT(length, 0U);
|
||||
return WebRtcSpl_GetSizeInBits(static_cast<uint32_t>(length - 1));
|
||||
}
|
||||
|
||||
size_t RealFourier::FftLength(int order) {
|
||||
RTC_CHECK_GE(order, 0);
|
||||
return size_t{1} << order;
|
||||
}
|
||||
|
||||
size_t RealFourier::ComplexLength(int order) {
|
||||
return FftLength(order) / 2 + 1;
|
||||
}
|
||||
|
||||
RealFourier::fft_real_scoper RealFourier::AllocRealBuffer(int count) {
|
||||
return fft_real_scoper(static_cast<float*>(
|
||||
AlignedMalloc(sizeof(float) * count, kFftBufferAlignment)));
|
||||
}
|
||||
|
||||
RealFourier::fft_cplx_scoper RealFourier::AllocCplxBuffer(int count) {
|
||||
return fft_cplx_scoper(static_cast<complex<float>*>(
|
||||
AlignedMalloc(sizeof(complex<float>) * count, kFftBufferAlignment)));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef COMMON_AUDIO_REAL_FOURIER_H_
|
||||
#define COMMON_AUDIO_REAL_FOURIER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <complex>
|
||||
#include <memory>
|
||||
|
||||
#include "rtc_base/memory/aligned_malloc.h"
|
||||
|
||||
// Uniform interface class for the real DFT and its inverse, for power-of-2
|
||||
// input lengths. Also contains helper functions for buffer allocation, taking
|
||||
// care of any memory alignment requirements the underlying library might have.
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RealFourier {
|
||||
public:
|
||||
// Shorthand typenames for the scopers used by the buffer allocation helpers.
|
||||
typedef std::unique_ptr<float[], AlignedFreeDeleter> fft_real_scoper;
|
||||
typedef std::unique_ptr<std::complex<float>[], AlignedFreeDeleter>
|
||||
fft_cplx_scoper;
|
||||
|
||||
// The alignment required for all input and output buffers, in bytes.
|
||||
static const size_t kFftBufferAlignment;
|
||||
|
||||
// Construct a wrapper instance for the given input order, which must be
|
||||
// between 1 and kMaxFftOrder, inclusively.
|
||||
static std::unique_ptr<RealFourier> Create(int fft_order);
|
||||
virtual ~RealFourier() {}
|
||||
|
||||
// Helper to compute the smallest FFT order (a power of 2) which will contain
|
||||
// the given input length.
|
||||
static int FftOrder(size_t length);
|
||||
|
||||
// Helper to compute the input length from the FFT order.
|
||||
static size_t FftLength(int order);
|
||||
|
||||
// Helper to compute the exact length, in complex floats, of the transform
|
||||
// output (i.e. |2^order / 2 + 1|).
|
||||
static size_t ComplexLength(int order);
|
||||
|
||||
// Buffer allocation helpers. The buffers are large enough to hold |count|
|
||||
// floats/complexes and suitably aligned for use by the implementation.
|
||||
// The returned scopers are set up with proper deleters; the caller owns
|
||||
// the allocated memory.
|
||||
static fft_real_scoper AllocRealBuffer(int count);
|
||||
static fft_cplx_scoper AllocCplxBuffer(int count);
|
||||
|
||||
// Main forward transform interface. The output array need only be big
|
||||
// enough for |2^order / 2 + 1| elements - the conjugate pairs are not
|
||||
// returned. Input and output must be properly aligned (e.g. through
|
||||
// AllocRealBuffer and AllocCplxBuffer) and input length must be
|
||||
// |2^order| (same as given at construction time).
|
||||
virtual void Forward(const float* src, std::complex<float>* dest) const = 0;
|
||||
|
||||
// Inverse transform. Same input format as output above, conjugate pairs
|
||||
// not needed.
|
||||
virtual void Inverse(const std::complex<float>* src, float* dest) const = 0;
|
||||
|
||||
virtual int order() const = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_AUDIO_REAL_FOURIER_H_
|
@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "common_audio/real_fourier_ooura.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "common_audio/third_party/ooura/fft_size_256/fft4g.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using std::complex;
|
||||
|
||||
namespace {
|
||||
|
||||
void Conjugate(complex<float>* array, size_t complex_length) {
|
||||
std::for_each(array, array + complex_length,
|
||||
[=](complex<float>& v) { v = std::conj(v); });
|
||||
}
|
||||
|
||||
size_t ComputeWorkIpSize(size_t fft_length) {
|
||||
return static_cast<size_t>(
|
||||
2 + std::ceil(std::sqrt(static_cast<float>(fft_length))));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RealFourierOoura::RealFourierOoura(int fft_order)
|
||||
: order_(fft_order),
|
||||
length_(FftLength(order_)),
|
||||
complex_length_(ComplexLength(order_)),
|
||||
// Zero-initializing work_ip_ will cause rdft to initialize these work
|
||||
// arrays on the first call.
|
||||
work_ip_(new size_t[ComputeWorkIpSize(length_)]()),
|
||||
work_w_(new float[complex_length_]()) {
|
||||
RTC_CHECK_GE(fft_order, 1);
|
||||
}
|
||||
|
||||
RealFourierOoura::~RealFourierOoura() = default;
|
||||
|
||||
void RealFourierOoura::Forward(const float* src, complex<float>* dest) const {
|
||||
{
|
||||
// This cast is well-defined since C++11. See "Non-static data members" at:
|
||||
// http://en.cppreference.com/w/cpp/numeric/complex
|
||||
auto* dest_float = reinterpret_cast<float*>(dest);
|
||||
std::copy(src, src + length_, dest_float);
|
||||
WebRtc_rdft(length_, 1, dest_float, work_ip_.get(), work_w_.get());
|
||||
}
|
||||
|
||||
// Ooura places real[n/2] in imag[0].
|
||||
dest[complex_length_ - 1] = complex<float>(dest[0].imag(), 0.0f);
|
||||
dest[0] = complex<float>(dest[0].real(), 0.0f);
|
||||
// Ooura returns the conjugate of the usual Fourier definition.
|
||||
Conjugate(dest, complex_length_);
|
||||
}
|
||||
|
||||
void RealFourierOoura::Inverse(const complex<float>* src, float* dest) const {
|
||||
{
|
||||
auto* dest_complex = reinterpret_cast<complex<float>*>(dest);
|
||||
// The real output array is shorter than the input complex array by one
|
||||
// complex element.
|
||||
const size_t dest_complex_length = complex_length_ - 1;
|
||||
std::copy(src, src + dest_complex_length, dest_complex);
|
||||
// Restore Ooura's conjugate definition.
|
||||
Conjugate(dest_complex, dest_complex_length);
|
||||
// Restore real[n/2] to imag[0].
|
||||
dest_complex[0] =
|
||||
complex<float>(dest_complex[0].real(), src[complex_length_ - 1].real());
|
||||
}
|
||||
|
||||
WebRtc_rdft(length_, -1, dest, work_ip_.get(), work_w_.get());
|
||||
|
||||
// Ooura returns a scaled version.
|
||||
const float scale = 2.0f / length_;
|
||||
std::for_each(dest, dest + length_, [scale](float& v) { v *= scale; });
|
||||
}
|
||||
|
||||
int RealFourierOoura::order() const {
|
||||
return order_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef COMMON_AUDIO_REAL_FOURIER_OOURA_H_
|
||||
#define COMMON_AUDIO_REAL_FOURIER_OOURA_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <complex>
|
||||
#include <memory>
|
||||
|
||||
#include "common_audio/real_fourier.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RealFourierOoura : public RealFourier {
|
||||
public:
|
||||
explicit RealFourierOoura(int fft_order);
|
||||
~RealFourierOoura() override;
|
||||
|
||||
void Forward(const float* src, std::complex<float>* dest) const override;
|
||||
void Inverse(const std::complex<float>* src, float* dest) const override;
|
||||
|
||||
int order() const override;
|
||||
|
||||
private:
|
||||
const int order_;
|
||||
const size_t length_;
|
||||
const size_t complex_length_;
|
||||
// These are work arrays for Ooura. The names are based on the comments in
|
||||
// common_audio/third_party/ooura/fft_size_256/fft4g.cc.
|
||||
const std::unique_ptr<size_t[]> work_ip_;
|
||||
const std::unique_ptr<float[]> work_w_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // COMMON_AUDIO_REAL_FOURIER_OOURA_H_
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_REAL_FOURIER_OPENMAX_H_
|
||||
#define WEBRTC_COMMON_AUDIO_REAL_FOURIER_OPENMAX_H_
|
||||
|
||||
#include <complex>
|
||||
|
||||
#include "webrtc/common_audio/real_fourier.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RealFourierOpenmax : public RealFourier {
|
||||
public:
|
||||
explicit RealFourierOpenmax(int fft_order);
|
||||
~RealFourierOpenmax() override;
|
||||
|
||||
void Forward(const float* src, std::complex<float>* dest) const override;
|
||||
void Inverse(const std::complex<float>* src, float* dest) const override;
|
||||
|
||||
int order() const override {
|
||||
return order_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Basically a forward declare of OMXFFTSpec_R_F32. To get rid of the
|
||||
// dependency on openmax.
|
||||
typedef void OMXFFTSpec_R_F32_;
|
||||
const int order_;
|
||||
|
||||
OMXFFTSpec_R_F32_* const omx_spec_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_REAL_FOURIER_OPENMAX_H_
|
||||
|
@ -14,45 +14,44 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "api/audio/audio_view.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class PushSincResampler;
|
||||
|
||||
// Wraps PushSincResampler to provide stereo support.
|
||||
// TODO(ajm): add support for an arbitrary number of channels.
|
||||
// Note: This implementation assumes 10ms buffer sizes throughout.
|
||||
template <typename T>
|
||||
class PushResampler {
|
||||
class PushResampler final {
|
||||
public:
|
||||
PushResampler();
|
||||
virtual ~PushResampler();
|
||||
|
||||
// Must be called whenever the parameters change. Free to be called at any
|
||||
// time as it is a no-op if parameters have not changed since the last call.
|
||||
int InitializeIfNeeded(int src_sample_rate_hz,
|
||||
int dst_sample_rate_hz,
|
||||
PushResampler(size_t src_samples_per_channel,
|
||||
size_t dst_samples_per_channel,
|
||||
size_t num_channels);
|
||||
~PushResampler();
|
||||
|
||||
// Returns the total number of samples provided in destination (e.g. 32 kHz,
|
||||
// 2 channel audio gives 640 samples).
|
||||
int Resample(const T* src, size_t src_length, T* dst, size_t dst_capacity);
|
||||
int Resample(InterleavedView<const T> src, InterleavedView<T> dst);
|
||||
// For when a deinterleaved/mono channel already exists and we can skip the
|
||||
// deinterleaved operation.
|
||||
int Resample(MonoView<const T> src, MonoView<T> dst);
|
||||
|
||||
private:
|
||||
int src_sample_rate_hz_;
|
||||
int dst_sample_rate_hz_;
|
||||
size_t num_channels_;
|
||||
// Vector that is needed to provide the proper inputs and outputs to the
|
||||
// interleave/de-interleave methods used in Resample. This needs to be
|
||||
// heap-allocated on the state to support an arbitrary number of channels
|
||||
// without doing run-time heap-allocations in the Resample method.
|
||||
std::vector<T*> channel_data_array_;
|
||||
// Ensures that source and destination buffers for deinterleaving are
|
||||
// correctly configured prior to resampling that requires deinterleaving.
|
||||
void EnsureInitialized(size_t src_samples_per_channel,
|
||||
size_t dst_samples_per_channel,
|
||||
size_t num_channels);
|
||||
|
||||
struct ChannelResampler {
|
||||
std::unique_ptr<PushSincResampler> resampler;
|
||||
std::vector<T> source;
|
||||
std::vector<T> destination;
|
||||
};
|
||||
// Buffers used for when a deinterleaving step is necessary.
|
||||
std::unique_ptr<T[]> source_;
|
||||
std::unique_ptr<T[]> destination_;
|
||||
DeinterleavedView<T> source_view_;
|
||||
DeinterleavedView<T> destination_view_;
|
||||
|
||||
std::vector<ChannelResampler> channel_resamplers_;
|
||||
std::vector<std::unique_ptr<PushSincResampler>> resamplers_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -15,133 +15,109 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/audio/audio_frame.h"
|
||||
#include "common_audio/include/audio_util.h"
|
||||
#include "common_audio/resampler/push_sinc_resampler.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
// These checks were factored out into a non-templatized function
|
||||
// due to problems with clang on Windows in debug builds.
|
||||
// For some reason having the DCHECKs inline in the template code
|
||||
// caused the compiler to generate code that threw off the linker.
|
||||
// TODO(tommi): Re-enable when we've figured out what the problem is.
|
||||
// http://crbug.com/615050
|
||||
void CheckValidInitParams(int src_sample_rate_hz,
|
||||
int dst_sample_rate_hz,
|
||||
size_t num_channels) {
|
||||
// The below checks are temporarily disabled on WEBRTC_WIN due to problems
|
||||
// with clang debug builds.
|
||||
#if !defined(WEBRTC_WIN) && defined(__clang__)
|
||||
RTC_DCHECK_GT(src_sample_rate_hz, 0);
|
||||
RTC_DCHECK_GT(dst_sample_rate_hz, 0);
|
||||
RTC_DCHECK_GT(num_channels, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CheckExpectedBufferSizes(size_t src_length,
|
||||
size_t dst_capacity,
|
||||
size_t num_channels,
|
||||
int src_sample_rate,
|
||||
int dst_sample_rate) {
|
||||
// The below checks are temporarily disabled on WEBRTC_WIN due to problems
|
||||
// with clang debug builds.
|
||||
// TODO(tommi): Re-enable when we've figured out what the problem is.
|
||||
// http://crbug.com/615050
|
||||
#if !defined(WEBRTC_WIN) && defined(__clang__)
|
||||
const size_t src_size_10ms = src_sample_rate * num_channels / 100;
|
||||
const size_t dst_size_10ms = dst_sample_rate * num_channels / 100;
|
||||
RTC_DCHECK_EQ(src_length, src_size_10ms);
|
||||
RTC_DCHECK_GE(dst_capacity, dst_size_10ms);
|
||||
#endif
|
||||
}
|
||||
namespace {
|
||||
// Maximum concurrent number of channels for `PushResampler<>`.
|
||||
// Note that this may be different from what the maximum is for audio codecs.
|
||||
constexpr int kMaxNumberOfChannels = 8;
|
||||
} // namespace
|
||||
|
||||
template <typename T>
|
||||
PushResampler<T>::PushResampler()
|
||||
: src_sample_rate_hz_(0), dst_sample_rate_hz_(0), num_channels_(0) {}
|
||||
PushResampler<T>::PushResampler() = default;
|
||||
|
||||
template <typename T>
|
||||
PushResampler<T>::~PushResampler() {}
|
||||
|
||||
template <typename T>
|
||||
int PushResampler<T>::InitializeIfNeeded(int src_sample_rate_hz,
|
||||
int dst_sample_rate_hz,
|
||||
PushResampler<T>::PushResampler(size_t src_samples_per_channel,
|
||||
size_t dst_samples_per_channel,
|
||||
size_t num_channels) {
|
||||
CheckValidInitParams(src_sample_rate_hz, dst_sample_rate_hz, num_channels);
|
||||
|
||||
if (src_sample_rate_hz == src_sample_rate_hz_ &&
|
||||
dst_sample_rate_hz == dst_sample_rate_hz_ &&
|
||||
num_channels == num_channels_) {
|
||||
// No-op if settings haven't changed.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (src_sample_rate_hz <= 0 || dst_sample_rate_hz <= 0 || num_channels <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
src_sample_rate_hz_ = src_sample_rate_hz;
|
||||
dst_sample_rate_hz_ = dst_sample_rate_hz;
|
||||
num_channels_ = num_channels;
|
||||
|
||||
const size_t src_size_10ms_mono =
|
||||
static_cast<size_t>(src_sample_rate_hz / 100);
|
||||
const size_t dst_size_10ms_mono =
|
||||
static_cast<size_t>(dst_sample_rate_hz / 100);
|
||||
channel_resamplers_.clear();
|
||||
for (size_t i = 0; i < num_channels; ++i) {
|
||||
channel_resamplers_.push_back(ChannelResampler());
|
||||
auto channel_resampler = channel_resamplers_.rbegin();
|
||||
channel_resampler->resampler = std::make_unique<PushSincResampler>(
|
||||
src_size_10ms_mono, dst_size_10ms_mono);
|
||||
channel_resampler->source.resize(src_size_10ms_mono);
|
||||
channel_resampler->destination.resize(dst_size_10ms_mono);
|
||||
}
|
||||
|
||||
channel_data_array_.resize(num_channels_);
|
||||
|
||||
return 0;
|
||||
EnsureInitialized(src_samples_per_channel, dst_samples_per_channel,
|
||||
num_channels);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int PushResampler<T>::Resample(const T* src,
|
||||
size_t src_length,
|
||||
T* dst,
|
||||
size_t dst_capacity) {
|
||||
CheckExpectedBufferSizes(src_length, dst_capacity, num_channels_,
|
||||
src_sample_rate_hz_, dst_sample_rate_hz_);
|
||||
PushResampler<T>::~PushResampler() = default;
|
||||
|
||||
if (src_sample_rate_hz_ == dst_sample_rate_hz_) {
|
||||
template <typename T>
|
||||
void PushResampler<T>::EnsureInitialized(size_t src_samples_per_channel,
|
||||
size_t dst_samples_per_channel,
|
||||
size_t num_channels) {
|
||||
RTC_DCHECK_GT(src_samples_per_channel, 0);
|
||||
RTC_DCHECK_GT(dst_samples_per_channel, 0);
|
||||
RTC_DCHECK_GT(num_channels, 0);
|
||||
RTC_DCHECK_LE(src_samples_per_channel, kMaxSamplesPerChannel10ms);
|
||||
RTC_DCHECK_LE(dst_samples_per_channel, kMaxSamplesPerChannel10ms);
|
||||
RTC_DCHECK_LE(num_channels, kMaxNumberOfChannels);
|
||||
|
||||
if (src_samples_per_channel == SamplesPerChannel(source_view_) &&
|
||||
dst_samples_per_channel == SamplesPerChannel(destination_view_) &&
|
||||
num_channels == NumChannels(source_view_)) {
|
||||
// No-op if settings haven't changed.
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate two buffers for all source and destination channels.
|
||||
// Then organize source and destination views together with an array of
|
||||
// resamplers for each channel in the deinterlaved buffers.
|
||||
source_.reset(new T[src_samples_per_channel * num_channels]);
|
||||
destination_.reset(new T[dst_samples_per_channel * num_channels]);
|
||||
source_view_ = DeinterleavedView<T>(source_.get(), src_samples_per_channel,
|
||||
num_channels);
|
||||
destination_view_ = DeinterleavedView<T>(
|
||||
destination_.get(), dst_samples_per_channel, num_channels);
|
||||
resamplers_.resize(num_channels);
|
||||
for (size_t i = 0; i < num_channels; ++i) {
|
||||
resamplers_[i] = std::make_unique<PushSincResampler>(
|
||||
src_samples_per_channel, dst_samples_per_channel);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int PushResampler<T>::Resample(InterleavedView<const T> src,
|
||||
InterleavedView<T> dst) {
|
||||
EnsureInitialized(SamplesPerChannel(src), SamplesPerChannel(dst),
|
||||
NumChannels(src));
|
||||
|
||||
RTC_DCHECK_EQ(NumChannels(src), NumChannels(source_view_));
|
||||
RTC_DCHECK_EQ(NumChannels(dst), NumChannels(destination_view_));
|
||||
RTC_DCHECK_EQ(SamplesPerChannel(src), SamplesPerChannel(source_view_));
|
||||
RTC_DCHECK_EQ(SamplesPerChannel(dst), SamplesPerChannel(destination_view_));
|
||||
|
||||
if (SamplesPerChannel(src) == SamplesPerChannel(dst)) {
|
||||
// The old resampler provides this memcpy facility in the case of matching
|
||||
// sample rates, so reproduce it here for the sinc resampler.
|
||||
memcpy(dst, src, src_length * sizeof(T));
|
||||
return static_cast<int>(src_length);
|
||||
CopySamples(dst, src);
|
||||
return static_cast<int>(src.data().size());
|
||||
}
|
||||
|
||||
const size_t src_length_mono = src_length / num_channels_;
|
||||
const size_t dst_capacity_mono = dst_capacity / num_channels_;
|
||||
Deinterleave(src, source_view_);
|
||||
|
||||
for (size_t ch = 0; ch < num_channels_; ++ch) {
|
||||
channel_data_array_[ch] = channel_resamplers_[ch].source.data();
|
||||
for (size_t i = 0; i < resamplers_.size(); ++i) {
|
||||
size_t dst_length_mono =
|
||||
resamplers_[i]->Resample(source_view_[i], destination_view_[i]);
|
||||
RTC_DCHECK_EQ(dst_length_mono, SamplesPerChannel(dst));
|
||||
}
|
||||
|
||||
Deinterleave(src, src_length_mono, num_channels_, channel_data_array_.data());
|
||||
Interleave<T>(destination_view_, dst);
|
||||
return static_cast<int>(dst.size());
|
||||
}
|
||||
|
||||
size_t dst_length_mono = 0;
|
||||
template <typename T>
|
||||
int PushResampler<T>::Resample(MonoView<const T> src, MonoView<T> dst) {
|
||||
RTC_DCHECK_EQ(resamplers_.size(), 1);
|
||||
RTC_DCHECK_EQ(SamplesPerChannel(src), SamplesPerChannel(source_view_));
|
||||
RTC_DCHECK_EQ(SamplesPerChannel(dst), SamplesPerChannel(destination_view_));
|
||||
|
||||
for (auto& resampler : channel_resamplers_) {
|
||||
dst_length_mono = resampler.resampler->Resample(
|
||||
resampler.source.data(), src_length_mono, resampler.destination.data(),
|
||||
dst_capacity_mono);
|
||||
if (SamplesPerChannel(src) == SamplesPerChannel(dst)) {
|
||||
CopySamples(dst, src);
|
||||
return static_cast<int>(src.size());
|
||||
}
|
||||
|
||||
for (size_t ch = 0; ch < num_channels_; ++ch) {
|
||||
channel_data_array_[ch] = channel_resamplers_[ch].destination.data();
|
||||
}
|
||||
|
||||
Interleave(channel_data_array_.data(), dst_length_mono, num_channels_, dst);
|
||||
return static_cast<int>(dst_length_mono * num_channels_);
|
||||
return resamplers_[0]->Resample(src, dst);
|
||||
}
|
||||
|
||||
// Explictly generate required instantiations.
|
||||
|
@ -63,12 +63,12 @@ size_t PushSincResampler::Resample(const float* source,
|
||||
// request through Run().
|
||||
//
|
||||
// If this wasn't done, SincResampler would call Run() twice on the first
|
||||
// pass, and we'd have to introduce an entire |source_frames| of delay, rather
|
||||
// pass, and we'd have to introduce an entire `source_frames` of delay, rather
|
||||
// than the minimum half kernel.
|
||||
//
|
||||
// It works out that ChunkSize() is exactly the amount of output we need to
|
||||
// request in order to prime the buffer with a single Run() request for
|
||||
// |source_frames|.
|
||||
// `source_frames`.
|
||||
if (first_pass_)
|
||||
resampler_->Resample(resampler_->ChunkSize(), destination);
|
||||
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/audio/audio_view.h"
|
||||
#include "common_audio/resampler/sinc_resampler.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -33,11 +33,20 @@ class PushSincResampler : public SincResamplerCallback {
|
||||
PushSincResampler(size_t source_frames, size_t destination_frames);
|
||||
~PushSincResampler() override;
|
||||
|
||||
// Perform the resampling. |source_frames| must always equal the
|
||||
// |source_frames| provided at construction. |destination_capacity| must be
|
||||
// at least as large as |destination_frames|. Returns the number of samples
|
||||
PushSincResampler(const PushSincResampler&) = delete;
|
||||
PushSincResampler& operator=(const PushSincResampler&) = delete;
|
||||
|
||||
// Perform the resampling. `source_frames` must always equal the
|
||||
// `source_frames` provided at construction. `destination_capacity` must be
|
||||
// at least as large as `destination_frames`. Returns the number of samples
|
||||
// provided in destination (for convenience, since this will always be equal
|
||||
// to |destination_frames|).
|
||||
// to `destination_frames`).
|
||||
template <typename S, typename D>
|
||||
size_t Resample(const MonoView<S>& source, const MonoView<D>& destination) {
|
||||
return Resample(&source[0], SamplesPerChannel(source), &destination[0],
|
||||
SamplesPerChannel(destination));
|
||||
}
|
||||
|
||||
size_t Resample(const int16_t* source,
|
||||
size_t source_frames,
|
||||
int16_t* destination,
|
||||
@ -72,8 +81,6 @@ class PushSincResampler : public SincResamplerCallback {
|
||||
|
||||
// Used to assert we are only requested for as much data as is available.
|
||||
size_t source_available_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(PushSincResampler);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -916,7 +916,6 @@ int Resampler::Push(const int16_t* samplesIn,
|
||||
outLen = (lengthIn * 8) / 11;
|
||||
free(tmp_mem);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -80,7 +80,7 @@
|
||||
// 8) Else, if we're not on the second load, goto (4).
|
||||
//
|
||||
// Note: we're glossing over how the sub-sample handling works with
|
||||
// |virtual_source_idx_|, etc.
|
||||
// `virtual_source_idx_`, etc.
|
||||
|
||||
// MSVC++ requires this to be set before any other includes to get M_PI.
|
||||
#define _USE_MATH_DEFINES
|
||||
@ -102,7 +102,7 @@ namespace webrtc {
|
||||
namespace {
|
||||
|
||||
double SincScaleFactor(double io_ratio) {
|
||||
// |sinc_scale_factor| is basically the normalized cutoff frequency of the
|
||||
// `sinc_scale_factor` is basically the normalized cutoff frequency of the
|
||||
// low-pass filter.
|
||||
double sinc_scale_factor = io_ratio > 1.0 ? 1.0 / io_ratio : 1.0;
|
||||
|
||||
@ -126,8 +126,8 @@ void SincResampler::InitializeCPUSpecificFeatures() {
|
||||
#if defined(WEBRTC_HAS_NEON)
|
||||
convolve_proc_ = Convolve_NEON;
|
||||
#elif defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
// Using AVX2 instead of SSE2 when AVX2 supported.
|
||||
if (GetCPUInfo(kAVX2))
|
||||
// Using AVX2 instead of SSE2 when AVX2/FMA3 supported.
|
||||
if (GetCPUInfo(kAVX2) && GetCPUInfo(kFMA3))
|
||||
convolve_proc_ = Convolve_AVX2;
|
||||
else if (GetCPUInfo(kSSE2))
|
||||
convolve_proc_ = Convolve_SSE;
|
||||
@ -238,7 +238,7 @@ void SincResampler::SetRatio(double io_sample_rate_ratio) {
|
||||
io_sample_rate_ratio_ = io_sample_rate_ratio;
|
||||
|
||||
// Optimize reinitialization by reusing values which are independent of
|
||||
// |sinc_scale_factor|. Provides a 3x speedup.
|
||||
// `sinc_scale_factor`. Provides a 3x speedup.
|
||||
const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_);
|
||||
for (size_t offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) {
|
||||
for (size_t i = 0; i < kKernelSize; ++i) {
|
||||
@ -268,8 +268,8 @@ void SincResampler::Resample(size_t frames, float* destination) {
|
||||
const double current_io_ratio = io_sample_rate_ratio_;
|
||||
const float* const kernel_ptr = kernel_storage_.get();
|
||||
while (remaining_frames) {
|
||||
// |i| may be negative if the last Resample() call ended on an iteration
|
||||
// that put |virtual_source_idx_| over the limit.
|
||||
// `i` may be negative if the last Resample() call ended on an iteration
|
||||
// that put `virtual_source_idx_` over the limit.
|
||||
//
|
||||
// Note: The loop construct here can severely impact performance on ARM
|
||||
// or when built with clang. See https://codereview.chromium.org/18566009/
|
||||
@ -278,7 +278,7 @@ void SincResampler::Resample(size_t frames, float* destination) {
|
||||
i > 0; --i) {
|
||||
RTC_DCHECK_LT(virtual_source_idx_, block_size_);
|
||||
|
||||
// |virtual_source_idx_| lies in between two kernel offsets so figure out
|
||||
// `virtual_source_idx_` lies in between two kernel offsets so figure out
|
||||
// what they are.
|
||||
const int source_idx = static_cast<int>(virtual_source_idx_);
|
||||
const double subsample_remainder = virtual_source_idx_ - source_idx;
|
||||
@ -288,16 +288,16 @@ void SincResampler::Resample(size_t frames, float* destination) {
|
||||
const int offset_idx = static_cast<int>(virtual_offset_idx);
|
||||
|
||||
// We'll compute "convolutions" for the two kernels which straddle
|
||||
// |virtual_source_idx_|.
|
||||
// `virtual_source_idx_`.
|
||||
const float* const k1 = kernel_ptr + offset_idx * kKernelSize;
|
||||
const float* const k2 = k1 + kKernelSize;
|
||||
|
||||
// Ensure |k1|, |k2| are 32-byte aligned for SIMD usage. Should always be
|
||||
// Ensure `k1`, `k2` are 32-byte aligned for SIMD usage. Should always be
|
||||
// true so long as kKernelSize is a multiple of 32.
|
||||
RTC_DCHECK_EQ(0, reinterpret_cast<uintptr_t>(k1) % 32);
|
||||
RTC_DCHECK_EQ(0, reinterpret_cast<uintptr_t>(k2) % 32);
|
||||
|
||||
// Initialize input pointer based on quantized |virtual_source_idx_|.
|
||||
// Initialize input pointer based on quantized `virtual_source_idx_`.
|
||||
const float* const input_ptr = r1_ + source_idx;
|
||||
|
||||
// Figure out how much to weight each kernel's "convolution".
|
||||
|
@ -18,15 +18,14 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/gtest_prod_util.h"
|
||||
#include "rtc_base/memory/aligned_malloc.h"
|
||||
#include "rtc_base/system/arch.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Callback class for providing more data into the resampler. Expects |frames|
|
||||
// of data to be rendered into |destination|; zero padded if not enough frames
|
||||
// Callback class for providing more data into the resampler. Expects `frames`
|
||||
// of data to be rendered into `destination`; zero padded if not enough frames
|
||||
// are available to satisfy the request.
|
||||
class SincResamplerCallback {
|
||||
public:
|
||||
@ -53,10 +52,10 @@ class SincResampler {
|
||||
static const size_t kKernelStorageSize =
|
||||
kKernelSize * (kKernelOffsetCount + 1);
|
||||
|
||||
// Constructs a SincResampler with the specified |read_cb|, which is used to
|
||||
// acquire audio data for resampling. |io_sample_rate_ratio| is the ratio
|
||||
// of input / output sample rates. |request_frames| controls the size in
|
||||
// frames of the buffer requested by each |read_cb| call. The value must be
|
||||
// Constructs a SincResampler with the specified `read_cb`, which is used to
|
||||
// acquire audio data for resampling. `io_sample_rate_ratio` is the ratio
|
||||
// of input / output sample rates. `request_frames` controls the size in
|
||||
// frames of the buffer requested by each `read_cb` call. The value must be
|
||||
// greater than kKernelSize. Specify kDefaultRequestSize if there are no
|
||||
// request size constraints.
|
||||
SincResampler(double io_sample_rate_ratio,
|
||||
@ -64,11 +63,14 @@ class SincResampler {
|
||||
SincResamplerCallback* read_cb);
|
||||
virtual ~SincResampler();
|
||||
|
||||
// Resample |frames| of data from |read_cb_| into |destination|.
|
||||
SincResampler(const SincResampler&) = delete;
|
||||
SincResampler& operator=(const SincResampler&) = delete;
|
||||
|
||||
// Resample `frames` of data from `read_cb_` into `destination`.
|
||||
void Resample(size_t frames, float* destination);
|
||||
|
||||
// The maximum size in frames that guarantees Resample() will only make a
|
||||
// single call to |read_cb_| for more data.
|
||||
// single call to `read_cb_` for more data.
|
||||
size_t ChunkSize() const;
|
||||
|
||||
size_t request_frames() const { return request_frames_; }
|
||||
@ -77,12 +79,12 @@ class SincResampler {
|
||||
// not call while Resample() is in progress.
|
||||
void Flush();
|
||||
|
||||
// Update |io_sample_rate_ratio_|. SetRatio() will cause a reconstruction of
|
||||
// Update `io_sample_rate_ratio_`. SetRatio() will cause a reconstruction of
|
||||
// the kernels used for resampling. Not thread safe, do not call while
|
||||
// Resample() is in progress.
|
||||
//
|
||||
// TODO(ajm): Use this in PushSincResampler rather than reconstructing
|
||||
// SincResampler. We would also need a way to update |request_frames_|.
|
||||
// SincResampler. We would also need a way to update `request_frames_`.
|
||||
void SetRatio(double io_sample_rate_ratio);
|
||||
|
||||
float* get_kernel_for_testing() { return kernel_storage_.get(); }
|
||||
@ -97,11 +99,11 @@ class SincResampler {
|
||||
// Selects runtime specific CPU features like SSE. Must be called before
|
||||
// using SincResampler.
|
||||
// TODO(ajm): Currently managed by the class internally. See the note with
|
||||
// |convolve_proc_| below.
|
||||
// `convolve_proc_` below.
|
||||
void InitializeCPUSpecificFeatures();
|
||||
|
||||
// Compute convolution of |k1| and |k2| over |input_ptr|, resultant sums are
|
||||
// linearly interpolated using |kernel_interpolation_factor|. On x86 and ARM
|
||||
// Compute convolution of `k1` and `k2` over `input_ptr`, resultant sums are
|
||||
// linearly interpolated using `kernel_interpolation_factor`. On x86 and ARM
|
||||
// the underlying implementation is chosen at run time.
|
||||
static float Convolve_C(const float* input_ptr,
|
||||
const float* k1,
|
||||
@ -136,7 +138,7 @@ class SincResampler {
|
||||
// Source of data for resampling.
|
||||
SincResamplerCallback* read_cb_;
|
||||
|
||||
// The size (in samples) to request from each |read_cb_| execution.
|
||||
// The size (in samples) to request from each `read_cb_` execution.
|
||||
const size_t request_frames_;
|
||||
|
||||
// The number of source frames processed per pass.
|
||||
@ -155,25 +157,23 @@ class SincResampler {
|
||||
// Data from the source is copied into this buffer for each processing pass.
|
||||
std::unique_ptr<float[], AlignedFreeDeleter> input_buffer_;
|
||||
|
||||
// Stores the runtime selection of which Convolve function to use.
|
||||
// TODO(ajm): Move to using a global static which must only be initialized
|
||||
// once by the user. We're not doing this initially, because we don't have
|
||||
// e.g. a LazyInstance helper in webrtc.
|
||||
// Stores the runtime selection of which Convolve function to use.
|
||||
// TODO(ajm): Move to using a global static which must only be initialized
|
||||
// once by the user. We're not doing this initially, because we don't have
|
||||
// e.g. a LazyInstance helper in webrtc.
|
||||
typedef float (*ConvolveProc)(const float*,
|
||||
const float*,
|
||||
const float*,
|
||||
double);
|
||||
ConvolveProc convolve_proc_;
|
||||
|
||||
// Pointers to the various regions inside |input_buffer_|. See the diagram at
|
||||
// Pointers to the various regions inside `input_buffer_`. See the diagram at
|
||||
// the top of the .cc file for more information.
|
||||
float* r0_;
|
||||
float* const r1_;
|
||||
float* const r2_;
|
||||
float* r3_;
|
||||
float* r4_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(SincResampler);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -25,7 +25,7 @@ float SincResampler::Convolve_AVX2(const float* input_ptr,
|
||||
__m256 m_sums1 = _mm256_setzero_ps();
|
||||
__m256 m_sums2 = _mm256_setzero_ps();
|
||||
|
||||
// Based on |input_ptr| alignment, we need to use loadu or load. Unrolling
|
||||
// Based on `input_ptr` alignment, we need to use loadu or load. Unrolling
|
||||
// these loops has not been tested or benchmarked.
|
||||
bool aligned_input = (reinterpret_cast<uintptr_t>(input_ptr) & 0x1F) == 0;
|
||||
if (!aligned_input) {
|
||||
|
@ -27,7 +27,7 @@ float SincResampler::Convolve_SSE(const float* input_ptr,
|
||||
__m128 m_sums1 = _mm_setzero_ps();
|
||||
__m128 m_sums2 = _mm_setzero_ps();
|
||||
|
||||
// Based on |input_ptr| alignment, we need to use loadu or load. Unrolling
|
||||
// Based on `input_ptr` alignment, we need to use loadu or load. Unrolling
|
||||
// these loops hurt performance in local testing.
|
||||
if (reinterpret_cast<uintptr_t>(input_ptr) & 0x0F) {
|
||||
for (size_t i = 0; i < kKernelSize; i += 4) {
|
||||
|
@ -15,7 +15,6 @@
|
||||
#define COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_
|
||||
|
||||
#include "common_audio/resampler/sinc_resampler.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -24,7 +23,7 @@ namespace webrtc {
|
||||
// resampler for the specific sample rate conversion being used.
|
||||
class SinusoidalLinearChirpSource : public SincResamplerCallback {
|
||||
public:
|
||||
// |delay_samples| can be used to insert a fractional sample delay into the
|
||||
// `delay_samples` can be used to insert a fractional sample delay into the
|
||||
// source. It will produce zeros until non-negative time is reached.
|
||||
SinusoidalLinearChirpSource(int sample_rate,
|
||||
size_t samples,
|
||||
@ -33,12 +32,16 @@ class SinusoidalLinearChirpSource : public SincResamplerCallback {
|
||||
|
||||
~SinusoidalLinearChirpSource() override {}
|
||||
|
||||
SinusoidalLinearChirpSource(const SinusoidalLinearChirpSource&) = delete;
|
||||
SinusoidalLinearChirpSource& operator=(const SinusoidalLinearChirpSource&) =
|
||||
delete;
|
||||
|
||||
void Run(size_t frames, float* destination) override;
|
||||
|
||||
double Frequency(size_t position);
|
||||
|
||||
private:
|
||||
enum { kMinFrequency = 5 };
|
||||
static constexpr int kMinFrequency = 5;
|
||||
|
||||
int sample_rate_;
|
||||
size_t total_samples_;
|
||||
@ -46,8 +49,6 @@ class SinusoidalLinearChirpSource : public SincResamplerCallback {
|
||||
double k_;
|
||||
size_t current_index_;
|
||||
double delay_samples_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(SinusoidalLinearChirpSource);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -18,9 +18,9 @@
|
||||
#include <string.h>
|
||||
|
||||
// Get address of region(s) from which we can read data.
|
||||
// If the region is contiguous, |data_ptr_bytes_2| will be zero.
|
||||
// If non-contiguous, |data_ptr_bytes_2| will be the size in bytes of the second
|
||||
// region. Returns room available to be read or |element_count|, whichever is
|
||||
// If the region is contiguous, `data_ptr_bytes_2` will be zero.
|
||||
// If non-contiguous, `data_ptr_bytes_2` will be the size in bytes of the second
|
||||
// region. Returns room available to be read or `element_count`, whichever is
|
||||
// smaller.
|
||||
static size_t GetBufferReadRegions(RingBuffer* buf,
|
||||
size_t element_count,
|
||||
@ -120,7 +120,7 @@ size_t WebRtc_ReadBuffer(RingBuffer* self,
|
||||
&buf_ptr_bytes_2);
|
||||
if (buf_ptr_bytes_2 > 0) {
|
||||
// We have a wrap around when reading the buffer. Copy the buffer data to
|
||||
// |data| and point to it.
|
||||
// `data` and point to it.
|
||||
memcpy(data, buf_ptr_1, buf_ptr_bytes_1);
|
||||
memcpy(((char*) data) + buf_ptr_bytes_1, buf_ptr_2, buf_ptr_bytes_2);
|
||||
buf_ptr_1 = data;
|
||||
@ -129,7 +129,7 @@ size_t WebRtc_ReadBuffer(RingBuffer* self,
|
||||
memcpy(data, buf_ptr_1, buf_ptr_bytes_1);
|
||||
}
|
||||
if (data_ptr) {
|
||||
// |buf_ptr_1| == |data| in the case of a wrap.
|
||||
// `buf_ptr_1` == `data` in the case of a wrap.
|
||||
*data_ptr = read_count == 0 ? NULL : buf_ptr_1;
|
||||
}
|
||||
|
||||
|
@ -39,14 +39,14 @@ void WebRtc_InitBuffer(RingBuffer* handle);
|
||||
void WebRtc_FreeBuffer(void* handle);
|
||||
|
||||
// Reads data from the buffer. Returns the number of elements that were read.
|
||||
// The |data_ptr| will point to the address where the read data is located.
|
||||
// If no data can be read, |data_ptr| is set to |NULL|. If all data can be read
|
||||
// without buffer wrap around then |data_ptr| will point to the location in the
|
||||
// buffer. Otherwise, the data will be copied to |data| (memory allocation done
|
||||
// by the user) and |data_ptr| points to the address of |data|. |data_ptr| is
|
||||
// The `data_ptr` will point to the address where the read data is located.
|
||||
// If no data can be read, `data_ptr` is set to `NULL`. If all data can be read
|
||||
// without buffer wrap around then `data_ptr` will point to the location in the
|
||||
// buffer. Otherwise, the data will be copied to `data` (memory allocation done
|
||||
// by the user) and `data_ptr` points to the address of `data`. `data_ptr` is
|
||||
// only guaranteed to be valid until the next call to WebRtc_WriteBuffer().
|
||||
//
|
||||
// To force a copying to |data|, pass a null |data_ptr|.
|
||||
// To force a copying to `data`, pass a null `data_ptr`.
|
||||
//
|
||||
// Returns number of elements read.
|
||||
size_t WebRtc_ReadBuffer(RingBuffer* handle,
|
||||
@ -54,14 +54,14 @@ size_t WebRtc_ReadBuffer(RingBuffer* handle,
|
||||
void* data,
|
||||
size_t element_count);
|
||||
|
||||
// Writes |data| to buffer and returns the number of elements written.
|
||||
// Writes `data` to buffer and returns the number of elements written.
|
||||
size_t WebRtc_WriteBuffer(RingBuffer* handle,
|
||||
const void* data,
|
||||
size_t element_count);
|
||||
|
||||
// Moves the buffer read position and returns the number of elements moved.
|
||||
// Positive |element_count| moves the read position towards the write position,
|
||||
// that is, flushing the buffer. Negative |element_count| moves the read
|
||||
// Positive `element_count` moves the read position towards the write position,
|
||||
// that is, flushing the buffer. Negative `element_count` moves the read
|
||||
// position away from the the write position, that is, stuffing the buffer.
|
||||
// Returns number of elements moved.
|
||||
int WebRtc_MoveReadPtr(RingBuffer* handle, int element_count);
|
||||
|
@ -72,9 +72,9 @@ void WebRtcSpl_CrossCorrelationNeon(int32_t* cross_correlation,
|
||||
size_t dim_cross_correlation,
|
||||
int right_shifts,
|
||||
int step_seq2) {
|
||||
size_t i = 0;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < dim_cross_correlation; i++) {
|
||||
for (i = 0; i < (int)dim_cross_correlation; i++) {
|
||||
const int16_t* seq1_ptr = seq1;
|
||||
const int16_t* seq2_ptr = seq2 + (step_seq2 * i);
|
||||
|
||||
|
@ -98,8 +98,7 @@ int32_t WebRtcSpl_DivResultInQ31(int32_t num, int32_t den)
|
||||
return div;
|
||||
}
|
||||
|
||||
int32_t RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/5486
|
||||
WebRtcSpl_DivW32HiLow(int32_t num, int16_t den_hi, int16_t den_low)
|
||||
int32_t WebRtcSpl_DivW32HiLow(int32_t num, int16_t den_hi, int16_t den_low)
|
||||
{
|
||||
int16_t approx, tmp_hi, tmp_low, num_hi, num_low;
|
||||
int32_t tmpW32;
|
||||
@ -111,8 +110,8 @@ WebRtcSpl_DivW32HiLow(int32_t num, int16_t den_hi, int16_t den_low)
|
||||
tmpW32 = (den_hi * approx << 1) + ((den_low * approx >> 15) << 1);
|
||||
// tmpW32 = den * approx
|
||||
|
||||
tmpW32 = (int32_t)0x7fffffffL - tmpW32; // result in Q30 (tmpW32 = 2.0-(den*approx))
|
||||
// UBSan: 2147483647 - -2 cannot be represented in type 'int'
|
||||
// result in Q30 (tmpW32 = 2.0-(den*approx))
|
||||
tmpW32 = (int32_t)((int64_t)0x7fffffffL - tmpW32);
|
||||
|
||||
// Store tmpW32 in hi and low format
|
||||
tmp_hi = (int16_t)(tmpW32 >> 16);
|
||||
|
@ -26,7 +26,7 @@ extern "C" {
|
||||
// - vector_length : Number of samples used in the dot product
|
||||
// - scaling : The number of right bit shifts to apply on each term
|
||||
// during calculation to avoid overflow, i.e., the
|
||||
// output will be in Q(-|scaling|)
|
||||
// output will be in Q(-`scaling`)
|
||||
//
|
||||
// Return value : The dot product in Q(-scaling)
|
||||
int32_t WebRtcSpl_DotProductWithScale(const int16_t* vector1,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user