From 7db4006fab6ab356b53d70a04edf9612de83e43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Monnom?= Date: Tue, 3 Mar 2026 12:31:04 -0800 Subject: [PATCH] add `lk console` command with PortAudio + WebRTC AEC --- .gitattributes | 8 + .github/workflows/build-binaries.yaml | 170 + .github/workflows/build.yaml | 139 +- .github/workflows/release.yaml | 217 +- .github/workflows/test.yaml | 2 + .gitmodules | 3 + .goreleaser.yaml | 60 - Formula/lk.rb | 47 + Makefile | 3 + cmd/lk/console.go | 186 + cmd/lk/console_stub.go | 9 + cmd/lk/console_tui.go | 169 + cmd/lk/main.go | 1 + pkg/apm/apm.go | 147 + pkg/apm/bridge.cpp | 76 + pkg/apm/bridge.go | 16 + pkg/apm/bridge.h | 35 + pkg/apm/webrtc/api/adaptation/resource.h | 66 + pkg/apm/webrtc/api/api.go | 10 + pkg/apm/webrtc/api/array_view.h | 347 ++ pkg/apm/webrtc/api/async_dns_resolver.h | 102 + pkg/apm/webrtc/api/audio/audio.go | 10 + pkg/apm/webrtc/api/audio/audio_device.h | 265 ++ .../webrtc/api/audio/audio_device_defines.h | 178 + pkg/apm/webrtc/api/audio/audio_frame.cc | 235 + pkg/apm/webrtc/api/audio/audio_frame.h | 230 + .../webrtc/api/audio/audio_frame_processor.h | 43 + pkg/apm/webrtc/api/audio/audio_mixer.h | 80 + pkg/apm/webrtc/api/audio/audio_processing.cc | 238 + pkg/apm/webrtc/api/audio/audio_processing.h | 898 ++++ .../api/audio/audio_processing_statistics.cc | 22 + .../api/audio/audio_processing_statistics.h | 68 + pkg/apm/webrtc/api/audio/audio_view.h | 272 ++ .../audio/builtin_audio_processing_builder.cc | 31 + .../audio/builtin_audio_processing_builder.h | 96 + pkg/apm/webrtc/api/audio/channel_layout.cc | 282 ++ pkg/apm/webrtc/api/audio/channel_layout.h | 165 + .../api/audio/echo_canceller3_config.cc | 295 ++ .../webrtc/api/audio/echo_canceller3_config.h | 253 ++ .../api/audio/echo_canceller3_factory.cc | 43 + .../api/audio/echo_canceller3_factory.h | 53 + pkg/apm/webrtc/api/audio/echo_control.h | 78 + .../webrtc/api/audio/echo_detector_creator.cc | 23 + .../webrtc/api/audio/echo_detector_creator.h | 26 + .../api/audio_codecs/L16/audio_decoder_L16.h | 49 + .../api/audio_codecs/L16/audio_encoder_L16.h | 54 + .../api/audio_codecs/audio_codec_pair_id.h | 74 + .../webrtc/api/audio_codecs/audio_decoder.h | 195 + .../api/audio_codecs/audio_decoder_factory.h | 56 + .../audio_decoder_factory_template.h | 187 + .../webrtc/api/audio_codecs/audio_encoder.h | 269 ++ .../api/audio_codecs/audio_encoder_factory.h | 69 + .../audio_encoder_factory_template.h | 199 + .../webrtc/api/audio_codecs/audio_format.h | 169 + .../builtin_audio_decoder_factory.h | 28 + .../builtin_audio_encoder_factory.h | 28 + .../audio_codecs/g711/audio_decoder_g711.h | 49 + .../audio_codecs/g711/audio_encoder_g711.h | 54 + .../audio_codecs/g722/audio_decoder_g722.h | 43 + .../audio_codecs/g722/audio_encoder_g722.h | 44 + .../g722/audio_encoder_g722_config.h | 29 + .../opus/audio_decoder_multi_channel_opus.h | 42 + .../audio_decoder_multi_channel_opus_config.h | 67 + .../audio_codecs/opus/audio_decoder_opus.h | 49 + .../opus/audio_encoder_multi_channel_opus.h | 43 + .../audio_encoder_multi_channel_opus_config.h | 64 + .../audio_codecs/opus/audio_encoder_opus.h | 43 + .../opus/audio_encoder_opus_config.h | 74 + .../audio_codecs/opus_audio_decoder_factory.h | 26 + .../audio_codecs/opus_audio_encoder_factory.h | 26 + pkg/apm/webrtc/api/audio_options.h | 81 + pkg/apm/webrtc/api/call/audio_sink.h | 48 + pkg/apm/webrtc/api/call/bitrate_allocation.h | 45 + pkg/apm/webrtc/api/call/transport.h | 53 + pkg/apm/webrtc/api/candidate.h | 305 ++ .../api/create_peerconnection_factory.h | 53 + pkg/apm/webrtc/api/crypto/crypto_options.h | 73 + .../api/crypto/frame_crypto_transformer.h | 524 +++ .../api/crypto/frame_decryptor_interface.h | 78 + .../api/crypto/frame_encryptor_interface.h | 57 + .../data_channel_event_observer_interface.h | 76 + pkg/apm/webrtc/api/data_channel_interface.h | 224 + pkg/apm/webrtc/api/dtls_transport_interface.h | 124 + pkg/apm/webrtc/api/dtmf_sender_interface.h | 124 + pkg/apm/webrtc/api/environment/environment.go | 10 + pkg/apm/webrtc/api/environment/environment.h | 143 + .../api/environment/environment_factory.cc | 128 + .../api/environment/environment_factory.h | 144 + pkg/apm/webrtc/api/fec_controller.h | 101 + pkg/apm/webrtc/api/fec_controller_override.h | 28 + pkg/apm/webrtc/api/field_trials.h | 59 + pkg/apm/webrtc/api/field_trials_registry.cc | 37 + pkg/apm/webrtc/api/field_trials_registry.h | 54 + pkg/apm/webrtc/api/field_trials_view.h | 46 + .../webrtc/api/frame_transformer_factory.h | 38 + .../webrtc/api/frame_transformer_interface.h | 213 + pkg/apm/webrtc/api/function_view.h | 139 + pkg/apm/webrtc/api/ice_transport_factory.h | 44 + pkg/apm/webrtc/api/ice_transport_interface.h | 131 + pkg/apm/webrtc/api/jsep.h | 267 ++ pkg/apm/webrtc/api/jsep_ice_candidate.h | 88 + pkg/apm/webrtc/api/jsep_session_description.h | 83 + pkg/apm/webrtc/api/legacy_stats_types.h | 482 +++ pkg/apm/webrtc/api/location.h | 31 + pkg/apm/webrtc/api/make_ref_counted.h | 132 + pkg/apm/webrtc/api/metronome/metronome.h | 48 + .../webrtc/api/neteq/custom_neteq_factory.h | 45 + .../neteq/default_neteq_controller_factory.h | 37 + .../webrtc/api/neteq/default_neteq_factory.h | 42 + pkg/apm/webrtc/api/neteq/neteq.h | 342 ++ pkg/apm/webrtc/api/neteq/neteq_controller.h | 185 + .../api/neteq/neteq_controller_factory.h | 34 + pkg/apm/webrtc/api/neteq/neteq_factory.h | 38 + pkg/apm/webrtc/api/neteq/tick_timer.h | 112 + pkg/apm/webrtc/api/network_state_predictor.h | 51 + pkg/apm/webrtc/api/notifier.h | 70 + pkg/apm/webrtc/api/numerics/numerics.go | 10 + .../api/numerics/samples_stats_counter.cc | 104 + .../api/numerics/samples_stats_counter.h | 135 + pkg/apm/webrtc/api/packet_socket_factory.h | 90 + .../webrtc/api/peer_connection_interface.h | 1717 ++++++++ pkg/apm/webrtc/api/priority.h | 56 + pkg/apm/webrtc/api/ref_count.h | 67 + pkg/apm/webrtc/api/ref_counted_base.h | 109 + pkg/apm/webrtc/api/rtc_error.h | 352 ++ .../webrtc/api/rtc_event_log/rtc_event_log.h | 19 + pkg/apm/webrtc/api/rtp_headers.h | 22 + pkg/apm/webrtc/api/rtp_packet_info.h | 19 + pkg/apm/webrtc/api/rtp_packet_infos.h | 130 + pkg/apm/webrtc/api/rtp_packet_sender.h | 40 + pkg/apm/webrtc/api/rtp_parameters.h | 687 +++ pkg/apm/webrtc/api/rtp_receiver_interface.h | 137 + pkg/apm/webrtc/api/rtp_sender_interface.h | 145 + .../webrtc/api/rtp_transceiver_direction.h | 27 + .../webrtc/api/rtp_transceiver_interface.h | 177 + pkg/apm/webrtc/api/scoped_refptr.h | 230 + pkg/apm/webrtc/api/sctp_transport_interface.h | 112 + pkg/apm/webrtc/api/sequence_checker.h | 141 + ...set_local_description_observer_interface.h | 30 + ...et_remote_description_observer_interface.h | 31 + pkg/apm/webrtc/api/stats/attribute.h | 94 + pkg/apm/webrtc/api/stats/rtc_stats.h | 197 + .../api/stats/rtc_stats_collector_callback.h | 30 + pkg/apm/webrtc/api/stats/rtc_stats_report.h | 134 + pkg/apm/webrtc/api/stats/rtcstats_objects.h | 488 +++ .../task_queue/default_task_queue_factory.h | 25 + .../gcd/default_task_queue_factory_gcd.cc | 23 + pkg/apm/webrtc/api/task_queue/gcd/gcd.go | 9 + .../webrtc/api/task_queue/gcd/gcd_helpers.m | 22 + .../api/task_queue/gcd/task_queue_gcd.cc | 162 + .../api/task_queue/gcd/task_queue_gcd.h | 24 + .../task_queue/pending_task_safety_flag.cc | 72 + .../api/task_queue/pending_task_safety_flag.h | 177 + .../default_task_queue_factory_stdlib.cc | 23 + .../webrtc/api/task_queue/stdlib/stdlib.go | 9 + .../task_queue/stdlib/task_queue_stdlib.cc | 313 ++ .../api/task_queue/stdlib/task_queue_stdlib.h | 24 + pkg/apm/webrtc/api/task_queue/task_queue.go | 10 + .../webrtc/api/task_queue/task_queue_base.cc | 78 + .../webrtc/api/task_queue/task_queue_base.h | 197 + .../api/task_queue/task_queue_factory.h | 35 + .../transport/bandwidth_estimation_settings.h | 27 + .../webrtc/api/transport/bandwidth_usage.h | 25 + .../webrtc/api/transport/bitrate_settings.cc | 19 + .../webrtc/api/transport/bitrate_settings.h | 49 + .../data_channel_transport_interface.h | 136 + pkg/apm/webrtc/api/transport/ecn_marking.h | 42 + pkg/apm/webrtc/api/transport/enums.h | 49 + .../api/transport/field_trial_based_config.cc | 21 + .../api/transport/field_trial_based_config.h | 27 + .../webrtc/api/transport/goog_cc_factory.h | 46 + .../webrtc/api/transport/network_control.h | 140 + pkg/apm/webrtc/api/transport/network_types.cc | 107 + pkg/apm/webrtc/api/transport/network_types.h | 294 ++ .../rtp/corruption_detection_message.h | 153 + .../api/transport/rtp/dependency_descriptor.h | 150 + pkg/apm/webrtc/api/transport/rtp/rtp_source.h | 106 + .../sctp_transport_factory_interface.h | 41 + pkg/apm/webrtc/api/transport/stun.h | 907 ++++ pkg/apm/webrtc/api/transport/transport.go | 10 + pkg/apm/webrtc/api/turn_customizer.h | 42 + pkg/apm/webrtc/api/uma_metrics.h | 233 + pkg/apm/webrtc/api/units/data_rate.cc | 36 + pkg/apm/webrtc/api/units/data_rate.h | 150 + pkg/apm/webrtc/api/units/data_size.cc | 32 + pkg/apm/webrtc/api/units/data_size.h | 61 + pkg/apm/webrtc/api/units/frequency.cc | 32 + pkg/apm/webrtc/api/units/frequency.h | 97 + pkg/apm/webrtc/api/units/time_delta.cc | 38 + pkg/apm/webrtc/api/units/time_delta.h | 105 + pkg/apm/webrtc/api/units/timestamp.cc | 36 + pkg/apm/webrtc/api/units/timestamp.h | 134 + pkg/apm/webrtc/api/units/units.go | 10 + pkg/apm/webrtc/api/video/color_space.h | 6 + pkg/apm/webrtc/api/video/video_content_type.h | 6 + pkg/apm/webrtc/api/video/video_rotation.h | 6 + pkg/apm/webrtc/api/voip/voip_base.h | 116 + pkg/apm/webrtc/api/voip/voip_codec.h | 56 + pkg/apm/webrtc/api/voip/voip_dtmf.h | 76 + pkg/apm/webrtc/api/voip/voip_engine.h | 100 + pkg/apm/webrtc/api/voip/voip_engine_factory.h | 78 + pkg/apm/webrtc/api/voip/voip_network.h | 47 + pkg/apm/webrtc/api/voip/voip_statistics.h | 101 + pkg/apm/webrtc/api/voip/voip_volume_control.h | 64 + pkg/apm/webrtc/api/webrtc_key_value_config.h | 17 + .../webrtc/common_audio/audio_converter.cc | 219 + pkg/apm/webrtc/common_audio/audio_converter.h | 72 + pkg/apm/webrtc/common_audio/audio_util.cc | 54 + pkg/apm/webrtc/common_audio/avx2/avx2.go | 8 + .../common_audio/avx2/fir_filter_avx2.cc | 88 + pkg/apm/webrtc/common_audio/channel_buffer.cc | 80 + pkg/apm/webrtc/common_audio/channel_buffer.h | 251 ++ pkg/apm/webrtc/common_audio/common_audio.go | 21 + .../webrtc/common_audio/common_audio_amd64.go | 8 + pkg/apm/webrtc/common_audio/fir_filter.h | 30 + pkg/apm/webrtc/common_audio/fir_filter_avx2.h | 41 + pkg/apm/webrtc/common_audio/fir_filter_c.cc | 61 + pkg/apm/webrtc/common_audio/fir_filter_c.h | 38 + .../webrtc/common_audio/fir_filter_factory.cc | 58 + .../webrtc/common_audio/fir_filter_factory.h | 32 + pkg/apm/webrtc/common_audio/fir_filter_neon.h | 39 + .../common_audio/fir_filter_neon_arm64.cc | 73 + pkg/apm/webrtc/common_audio/fir_filter_sse.h | 41 + .../common_audio/fir_filter_sse_amd64.cc | 82 + .../webrtc/common_audio/include/audio_util.h | 204 + pkg/apm/webrtc/common_audio/real_fourier.cc | 51 + pkg/apm/webrtc/common_audio/real_fourier.h | 76 + .../webrtc/common_audio/real_fourier_ooura.cc | 91 + .../webrtc/common_audio/real_fourier_ooura.h | 45 + .../common_audio/resampler/avx2/avx2.go | 8 + .../resampler/avx2/sinc_resampler_avx2.cc | 66 + .../resampler/include/push_resampler.h | 58 + .../resampler/include/resampler.h | 99 + .../common_audio/resampler/push_resampler.cc | 127 + .../resampler/push_sinc_resampler.cc | 102 + .../resampler/push_sinc_resampler.h | 88 + .../common_audio/resampler/resampler.cc | 923 ++++ .../common_audio/resampler/resampler.go | 10 + .../common_audio/resampler/sinc_resampler.cc | 366 ++ .../common_audio/resampler/sinc_resampler.h | 181 + .../resampler/sinc_resampler_neon_arm64.cc | 48 + .../resampler/sinc_resampler_sse_amd64.cc | 63 + .../sinusoidal_linear_chirp_source.cc | 57 + .../sinusoidal_linear_chirp_source.h | 56 + pkg/apm/webrtc/common_audio/ring_buffer.c | 227 + pkg/apm/webrtc/common_audio/ring_buffer.h | 79 + .../auto_corr_to_refl_coef.c | 94 + .../signal_processing/auto_correlation.c | 64 + .../signal_processing/complex_bit_reverse.c | 106 + .../signal_processing/complex_fft.c | 267 ++ .../signal_processing/complex_fft_tables.h | 132 + .../signal_processing/copy_set_operations.c | 72 + .../signal_processing/cross_correlation.c | 30 + .../cross_correlation_neon_arm64.c | 85 + .../signal_processing/division_operations.c | 123 + .../dot_product_with_scale.cc | 34 + .../dot_product_with_scale.h | 40 + .../signal_processing/downsample_fast.c | 64 + .../downsample_fast_neon_arm64.c | 223 + .../common_audio/signal_processing/energy.c | 36 + .../signal_processing/filter_ar.c | 83 + .../signal_processing/filter_ar_fast_q12.c | 47 + .../signal_processing/filter_ma_fast_q12.c | 50 + .../signal_processing/get_hanning_window.c | 64 + .../signal_processing/get_scaling_square.c | 41 + .../signal_processing/include/real_fft.h | 96 + .../include/signal_processing_library.h | 1626 +++++++ .../signal_processing/include/spl_inl.h | 155 + .../signal_processing/include/spl_inl_armv7.h | 138 + .../signal_processing/include/spl_inl_mips.h | 204 + .../signal_processing/levinson_durbin.c | 243 ++ .../signal_processing/lpc_to_refl_coef.c | 52 + .../signal_processing/min_max_operations.c | 260 ++ .../min_max_operations_neon_arm64.c | 335 ++ .../randomization_functions.c | 106 + .../common_audio/signal_processing/real_fft.c | 102 + .../signal_processing/refl_coef_to_lpc.c | 54 + .../common_audio/signal_processing/resample.c | 511 +++ .../signal_processing/resample_48khz.c | 186 + .../signal_processing/resample_by_2.c | 185 + .../resample_by_2_internal.c | 673 +++ .../resample_by_2_internal.h | 60 + .../signal_processing/resample_fractional.c | 236 + .../common_audio/signal_processing/sp.go | 15 + .../common_audio/signal_processing/spl_init.c | 69 + .../common_audio/signal_processing/spl_inl.c | 24 + .../common_audio/signal_processing/spl_sqrt.c | 188 + .../signal_processing/splitting_filter.c | 217 + .../sqrt_of_one_minus_x_squared.c | 34 + .../signal_processing/vector_operations.c | 79 + .../vector_scaling_operations.c | 163 + .../webrtc/common_audio/smoothing_filter.cc | 147 + .../webrtc/common_audio/smoothing_filter.h | 75 + .../ooura/fft_size_128/fft_size_128.go | 10 + .../ooura/fft_size_128/ooura_fft.cc | 548 +++ .../ooura/fft_size_128/ooura_fft.h | 64 + .../fft_size_128/ooura_fft_neon_arm64.cc | 351 ++ .../fft_size_128/ooura_fft_sse2_amd64.cc | 439 ++ .../fft_size_128/ooura_fft_tables_common.h | 54 + .../fft_size_128/ooura_fft_tables_neon_sse2.h | 98 + .../third_party/ooura/fft_size_256/fft4g.cc | 866 ++++ .../third_party/ooura/fft_size_256/fft4g.h | 23 + .../ooura/fft_size_256/fft_size_256.go | 10 + .../spl_sqrt_floor/spl_sqrt_floor.c | 77 + .../spl_sqrt_floor/spl_sqrt_floor.go | 11 + .../spl_sqrt_floor/spl_sqrt_floor.h | 29 + pkg/apm/webrtc/common_audio/vad/include/vad.h | 50 + .../common_audio/vad/include/webrtc_vad.h | 87 + pkg/apm/webrtc/common_audio/vad/vad.cc | 66 + pkg/apm/webrtc/common_audio/vad/vad.go | 15 + pkg/apm/webrtc/common_audio/vad/vad_core.c | 677 +++ pkg/apm/webrtc/common_audio/vad/vad_core.h | 123 + .../webrtc/common_audio/vad/vad_filterbank.c | 339 ++ .../webrtc/common_audio/vad/vad_filterbank.h | 45 + pkg/apm/webrtc/common_audio/vad/vad_gmm.c | 82 + pkg/apm/webrtc/common_audio/vad/vad_gmm.h | 39 + pkg/apm/webrtc/common_audio/vad/vad_sp.c | 176 + pkg/apm/webrtc/common_audio/vad/vad_sp.h | 54 + pkg/apm/webrtc/common_audio/vad/webrtc_vad.c | 116 + pkg/apm/webrtc/common_audio/wav_file.cc | 290 ++ pkg/apm/webrtc/common_audio/wav_file.h | 115 + pkg/apm/webrtc/common_audio/wav_header.cc | 435 ++ pkg/apm/webrtc/common_audio/wav_header.h | 91 + .../webrtc/common_audio/window_generator.cc | 71 + .../webrtc/common_audio/window_generator.h | 31 + .../experiments/registered_field_trials.h | 8 + .../modules/audio_processing/aec_dump.h | 116 + .../audio_processing/audio_frame_proxies.h | 41 + .../audio_processing/audio_frame_view.h | 66 + .../audio_processing/audio_processing.h | 18 + .../audio_processing_statistics.h | 18 + .../audio_processing/mock_audio_processing.h | 191 + .../audio_coding/codecs/isac/bandwidth_info.h | 24 + .../isac/main/source/filter_functions.c | 191 + .../isac/main/source/filter_functions.h | 25 + .../codecs/isac/main/source/isac.go | 9 + .../codecs/isac/main/source/isac_vad.c | 409 ++ .../codecs/isac/main/source/isac_vad.h | 45 + .../isac/main/source/os_specific_inline.h | 42 + .../codecs/isac/main/source/pitch_estimator.c | 737 ++++ .../codecs/isac/main/source/pitch_estimator.h | 32 + .../codecs/isac/main/source/pitch_filter.c | 403 ++ .../codecs/isac/main/source/pitch_filter.h | 42 + .../codecs/isac/main/source/settings.h | 196 + .../codecs/isac/main/source/structs.h | 448 ++ .../aec3/adaptive_fir_filter.cc | 744 ++++ .../aec3/adaptive_fir_filter.h | 192 + .../aec3/adaptive_fir_filter_erl.cc | 102 + .../aec3/adaptive_fir_filter_erl.h | 54 + .../modules/audio_processing/aec3/aec3.go | 10 + .../audio_processing/aec3/aec3_common.cc | 58 + .../audio_processing/aec3/aec3_common.h | 114 + .../modules/audio_processing/aec3/aec3_fft.cc | 144 + .../modules/audio_processing/aec3/aec3_fft.h | 73 + .../audio_processing/aec3/aec_state.cc | 488 +++ .../modules/audio_processing/aec3/aec_state.h | 300 ++ .../audio_processing/aec3/alignment_mixer.cc | 163 + .../audio_processing/aec3/alignment_mixer.h | 57 + .../aec3/api_call_jitter_metrics.cc | 121 + .../aec3/api_call_jitter_metrics.h | 60 + .../aec3/avx2/adaptive_fir_filter_avx2.cc | 188 + .../aec3/avx2/adaptive_fir_filter_erl_avx2.cc | 37 + .../audio_processing/aec3/avx2/avx2.go | 8 + .../aec3/avx2/fft_data_avx2.cc | 32 + .../aec3/avx2/matched_filter_avx2.cc | 264 ++ .../aec3/avx2/vector_math_avx2.cc | 80 + .../modules/audio_processing/aec3/block.h | 91 + .../audio_processing/aec3/block_buffer.cc | 23 + .../audio_processing/aec3/block_buffer.h | 60 + .../aec3/block_delay_buffer.cc | 69 + .../aec3/block_delay_buffer.h | 43 + .../audio_processing/aec3/block_framer.cc | 83 + .../audio_processing/aec3/block_framer.h | 49 + .../audio_processing/aec3/block_processor.cc | 292 ++ .../audio_processing/aec3/block_processor.h | 85 + .../aec3/block_processor_metrics.cc | 104 + .../aec3/block_processor_metrics.h | 46 + .../aec3/clockdrift_detector.cc | 61 + .../aec3/clockdrift_detector.h | 40 + .../aec3/coarse_filter_update_gain.cc | 103 + .../aec3/coarse_filter_update_gain.h | 74 + .../aec3/comfort_noise_generator.cc | 185 + .../aec3/comfort_noise_generator.h | 76 + .../audio_processing/aec3/config_selector.cc | 71 + .../audio_processing/aec3/config_selector.h | 41 + .../audio_processing/aec3/decimator.cc | 90 + .../modules/audio_processing/aec3/decimator.h | 41 + .../audio_processing/aec3/delay_estimate.h | 33 + .../aec3/dominant_nearend_detector.cc | 74 + .../aec3/dominant_nearend_detector.h | 56 + .../aec3/downsampled_render_buffer.cc | 25 + .../aec3/downsampled_render_buffer.h | 58 + .../audio_processing/aec3/echo_audibility.cc | 119 + .../audio_processing/aec3/echo_audibility.h | 86 + .../audio_processing/aec3/echo_canceller3.cc | 1003 +++++ .../audio_processing/aec3/echo_canceller3.h | 231 + .../aec3/echo_path_delay_estimator.cc | 127 + .../aec3/echo_path_delay_estimator.h | 81 + .../aec3/echo_path_variability.cc | 22 + .../aec3/echo_path_variability.h | 33 + .../audio_processing/aec3/echo_remover.cc | 527 +++ .../audio_processing/aec3/echo_remover.h | 65 + .../aec3/echo_remover_metrics.cc | 157 + .../aec3/echo_remover_metrics.h | 78 + .../audio_processing/aec3/erl_estimator.cc | 145 + .../audio_processing/aec3/erl_estimator.h | 57 + .../audio_processing/aec3/erle_estimator.cc | 88 + .../audio_processing/aec3/erle_estimator.h | 109 + .../audio_processing/aec3/fft_buffer.cc | 27 + .../audio_processing/aec3/fft_buffer.h | 60 + .../modules/audio_processing/aec3/fft_data.h | 104 + .../audio_processing/aec3/filter_analyzer.cc | 288 ++ .../audio_processing/aec3/filter_analyzer.h | 149 + .../audio_processing/aec3/frame_blocker.cc | 80 + .../audio_processing/aec3/frame_blocker.h | 51 + .../aec3/fullband_erle_estimator.cc | 191 + .../aec3/fullband_erle_estimator.h | 117 + .../audio_processing/aec3/matched_filter.cc | 825 ++++ .../audio_processing/aec3/matched_filter.h | 175 + .../aec3/matched_filter_lag_aggregator.cc | 188 + .../aec3/matched_filter_lag_aggregator.h | 98 + .../audio_processing/aec3/moving_average.cc | 60 + .../audio_processing/aec3/moving_average.h | 45 + .../aec3/multi_channel_content_detector.cc | 148 + .../aec3/multi_channel_content_detector.h | 95 + .../audio_processing/aec3/nearend_detector.h | 41 + .../aec3/refined_filter_update_gain.cc | 173 + .../aec3/refined_filter_update_gain.h | 91 + .../audio_processing/aec3/render_buffer.cc | 81 + .../audio_processing/aec3/render_buffer.h | 115 + .../aec3/render_delay_buffer.cc | 505 +++ .../aec3/render_delay_buffer.h | 86 + .../aec3/render_delay_controller.cc | 185 + .../aec3/render_delay_controller.h | 52 + .../aec3/render_delay_controller_metrics.cc | 132 + .../aec3/render_delay_controller_metrics.h | 50 + .../aec3/render_signal_analyzer.cc | 156 + .../aec3/render_signal_analyzer.h | 62 + .../aec3/residual_echo_estimator.cc | 384 ++ .../aec3/residual_echo_estimator.h | 86 + .../aec3/reverb_decay_estimator.cc | 410 ++ .../aec3/reverb_decay_estimator.h | 120 + .../aec3/reverb_frequency_response.cc | 107 + .../aec3/reverb_frequency_response.h | 53 + .../audio_processing/aec3/reverb_model.cc | 58 + .../audio_processing/aec3/reverb_model.h | 55 + .../aec3/reverb_model_estimator.cc | 57 + .../aec3/reverb_model_estimator.h | 72 + .../aec3/signal_dependent_erle_estimator.cc | 416 ++ .../aec3/signal_dependent_erle_estimator.h | 104 + .../audio_processing/aec3/spectrum_buffer.cc | 30 + .../audio_processing/aec3/spectrum_buffer.h | 62 + .../aec3/stationarity_estimator.cc | 241 ++ .../aec3/stationarity_estimator.h | 123 + .../aec3/subband_erle_estimator.cc | 253 ++ .../aec3/subband_erle_estimator.h | 107 + .../aec3/subband_nearend_detector.cc | 69 + .../aec3/subband_nearend_detector.h | 52 + .../audio_processing/aec3/subtractor.cc | 367 ++ .../audio_processing/aec3/subtractor.h | 152 + .../aec3/subtractor_output.cc | 58 + .../audio_processing/aec3/subtractor_output.h | 52 + .../aec3/subtractor_output_analyzer.cc | 64 + .../aec3/subtractor_output_analyzer.h | 45 + .../aec3/suppression_filter.cc | 180 + .../aec3/suppression_filter.h | 51 + .../audio_processing/aec3/suppression_gain.cc | 460 ++ .../audio_processing/aec3/suppression_gain.h | 143 + .../audio_processing/aec3/transparent_mode.cc | 247 ++ .../audio_processing/aec3/transparent_mode.h | 49 + .../audio_processing/aec3/vector_math.h | 229 + .../audio_processing/aec_dump/aec_dump.go | 10 + .../aec_dump/aec_dump_factory.h | 48 + .../audio_processing/aec_dump/aec_dump_impl.h | 82 + .../aec_dump/capture_stream_info.h | 63 + .../aec_dump/null_aec_dump_factory.cc | 39 + .../modules/audio_processing/aecm/aecm.go | 10 + .../audio_processing/aecm/aecm_core.cc | 1124 +++++ .../modules/audio_processing/aecm/aecm_core.h | 441 ++ .../audio_processing/aecm/aecm_core_c.cc | 671 +++ .../aecm/aecm_core_neon_arm64.cc | 206 + .../audio_processing/aecm/aecm_defines.h | 87 + .../aecm/echo_control_mobile.cc | 599 +++ .../aecm/echo_control_mobile.h | 209 + .../modules/audio_processing/agc/agc.cc | 98 + .../modules/audio_processing/agc/agc.go | 14 + .../webrtc/modules/audio_processing/agc/agc.h | 52 + .../agc/agc_manager_direct.cc | 715 +++ .../audio_processing/agc/agc_manager_direct.h | 279 ++ .../audio_processing/agc/gain_control.h | 105 + .../audio_processing/agc/legacy/analog_agc.cc | 1238 ++++++ .../audio_processing/agc/legacy/analog_agc.h | 117 + .../agc/legacy/digital_agc.cc | 704 +++ .../audio_processing/agc/legacy/digital_agc.h | 75 + .../agc/legacy/gain_control.h | 256 ++ .../audio_processing/agc/legacy/legacy.go | 10 + .../agc/loudness_histogram.cc | 229 + .../audio_processing/agc/loudness_histogram.h | 90 + .../modules/audio_processing/agc/utility.cc | 39 + .../modules/audio_processing/agc/utility.h | 27 + .../agc2/adaptive_digital_gain_controller.cc | 216 + .../agc2/adaptive_digital_gain_controller.h | 66 + .../modules/audio_processing/agc2/agc2.go | 14 + .../audio_processing/agc2/agc2_common.h | 62 + .../agc2/agc2_testing_common.cc | 94 + .../agc2/agc2_testing_common.h | 82 + .../audio_processing/agc2/biquad_filter.cc | 59 + .../audio_processing/agc2/biquad_filter.h | 56 + .../agc2/clipping_predictor.cc | 384 ++ .../agc2/clipping_predictor.h | 62 + .../agc2/clipping_predictor_level_buffer.cc | 77 + .../agc2/clipping_predictor_level_buffer.h | 70 + .../agc2/compute_interpolated_gain_curve.cc | 229 + .../agc2/compute_interpolated_gain_curve.h | 48 + .../audio_processing/agc2/cpu_features.cc | 62 + .../audio_processing/agc2/cpu_features.h | 39 + .../agc2/fixed_digital_level_estimator.cc | 124 + .../agc2/fixed_digital_level_estimator.h | 70 + .../audio_processing/agc2/gain_applier.cc | 103 + .../audio_processing/agc2/gain_applier.h | 50 + .../audio_processing/agc2/gain_map_internal.h | 46 + .../agc2/input_volume_controller.cc | 580 +++ .../agc2/input_volume_controller.h | 282 ++ .../agc2/input_volume_stats_reporter.cc | 171 + .../agc2/input_volume_stats_reporter.h | 97 + .../agc2/interpolated_gain_curve.cc | 204 + .../agc2/interpolated_gain_curve.h | 152 + .../modules/audio_processing/agc2/limiter.cc | 146 + .../modules/audio_processing/agc2/limiter.h | 66 + .../agc2/limiter_db_gain_curve.cc | 138 + .../agc2/limiter_db_gain_curve.h | 76 + .../agc2/noise_level_estimator.cc | 173 + .../agc2/noise_level_estimator.h | 36 + .../agc2/rnn_vad/auto_correlation.cc | 91 + .../agc2/rnn_vad/auto_correlation.h | 48 + .../agc2/rnn_vad/avx2/avx2.go | 8 + .../agc2/rnn_vad/avx2/vector_math_avx2.cc | 53 + .../audio_processing/agc2/rnn_vad/common.h | 77 + .../agc2/rnn_vad/features_extraction.cc | 90 + .../agc2/rnn_vad/features_extraction.h | 61 + .../agc2/rnn_vad/lp_residual.cc | 138 + .../agc2/rnn_vad/lp_residual.h | 40 + .../agc2/rnn_vad/pitch_search.cc | 70 + .../agc2/rnn_vad/pitch_search.h | 54 + .../agc2/rnn_vad/pitch_search_internal.cc | 512 +++ .../agc2/rnn_vad/pitch_search_internal.h | 114 + .../agc2/rnn_vad/ring_buffer.h | 65 + .../audio_processing/agc2/rnn_vad/rnn.cc | 91 + .../audio_processing/agc2/rnn_vad/rnn.h | 53 + .../audio_processing/agc2/rnn_vad/rnn_fc.cc | 104 + .../audio_processing/agc2/rnn_vad/rnn_fc.h | 72 + .../audio_processing/agc2/rnn_vad/rnn_gru.cc | 198 + .../audio_processing/agc2/rnn_vad/rnn_gru.h | 70 + .../audio_processing/agc2/rnn_vad/rnn_vad.go | 10 + .../agc2/rnn_vad/sequence_buffer.h | 77 + .../agc2/rnn_vad/spectral_features.cc | 214 + .../agc2/rnn_vad/spectral_features.h | 79 + .../rnn_vad/spectral_features_internal.cc | 188 + .../agc2/rnn_vad/spectral_features_internal.h | 100 + .../agc2/rnn_vad/symmetric_matrix_buffer.h | 95 + .../agc2/rnn_vad/test_utils.h | 129 + .../agc2/rnn_vad/vector_math.h | 113 + .../agc2/saturation_protector.cc | 183 + .../agc2/saturation_protector.h | 46 + .../agc2/saturation_protector_buffer.cc | 77 + .../agc2/saturation_protector_buffer.h | 59 + .../agc2/speech_level_estimator.cc | 174 + .../agc2/speech_level_estimator.h | 81 + .../agc2/speech_probability_buffer.cc | 105 + .../agc2/speech_probability_buffer.h | 80 + .../audio_processing/agc2/vad_wrapper.cc | 106 + .../audio_processing/agc2/vad_wrapper.h | 78 + .../agc2/vector_float_frame.cc | 31 + .../agc2/vector_float_frame.h | 43 + .../webrtc/modules/audio_processing/apm.go | 24 + .../modules/audio_processing/apm_amd64.go | 8 + .../modules/audio_processing/audio_buffer.cc | 394 ++ .../modules/audio_processing/audio_buffer.h | 182 + .../audio_processing/audio_processing_impl.cc | 2303 ++++++++++ .../audio_processing/audio_processing_impl.h | 531 +++ .../audio_samples_scaler.cc | 92 + .../audio_samples_scaler.h | 46 + .../capture_levels_adjuster.cc | 96 + .../capture_levels_adjuster.go | 10 + .../capture_levels_adjuster.h | 88 + .../echo_control_mobile_impl.cc | 287 ++ .../echo_control_mobile_impl.h | 86 + .../echo_detector/circular_buffer.cc | 49 + .../echo_detector/circular_buffer.h | 43 + .../echo_detector/echo_detector.go | 10 + .../echo_detector/mean_variance_estimator.cc | 47 + .../echo_detector/mean_variance_estimator.h | 33 + .../echo_detector/moving_max.cc | 52 + .../echo_detector/moving_max.h | 36 + .../normalized_covariance_estimator.cc | 43 + .../normalized_covariance_estimator.h | 43 + .../audio_processing/gain_control_impl.cc | 369 ++ .../audio_processing/gain_control_impl.h | 91 + .../audio_processing/gain_controller2.cc | 289 ++ .../audio_processing/gain_controller2.h | 113 + .../audio_processing/high_pass_filter.cc | 118 + .../audio_processing/high_pass_filter.h | 45 + .../audio_processing/include/aec_dump.cc | 41 + .../audio_processing/include/aec_dump.h | 116 + .../include/audio_frame_proxies.cc | 66 + .../include/audio_frame_proxies.h | 41 + .../include/audio_frame_view.h | 66 + .../include/audio_processing.h | 18 + .../include/audio_processing_statistics.h | 18 + .../audio_processing/include/include.go | 10 + .../logging/apm_data_dumper.cc | 93 + .../logging/apm_data_dumper.h | 417 ++ .../audio_processing/logging/logging.go | 10 + .../modules/audio_processing/ns/fast_math.cc | 83 + .../modules/audio_processing/ns/fast_math.h | 37 + .../modules/audio_processing/ns/histograms.cc | 47 + .../modules/audio_processing/ns/histograms.h | 55 + .../audio_processing/ns/noise_estimator.cc | 201 + .../audio_processing/ns/noise_estimator.h | 75 + .../audio_processing/ns/noise_suppressor.cc | 556 +++ .../audio_processing/ns/noise_suppressor.h | 91 + .../webrtc/modules/audio_processing/ns/ns.go | 10 + .../modules/audio_processing/ns/ns_common.h | 34 + .../modules/audio_processing/ns/ns_config.h | 24 + .../modules/audio_processing/ns/ns_fft.cc | 64 + .../modules/audio_processing/ns/ns_fft.h | 45 + .../audio_processing/ns/prior_signal_model.cc | 18 + .../audio_processing/ns/prior_signal_model.h | 32 + .../ns/prior_signal_model_estimator.cc | 171 + .../ns/prior_signal_model_estimator.h | 39 + .../ns/quantile_noise_estimator.cc | 88 + .../ns/quantile_noise_estimator.h | 46 + .../audio_processing/ns/signal_model.cc | 24 + .../audio_processing/ns/signal_model.h | 34 + .../ns/signal_model_estimator.cc | 175 + .../ns/signal_model_estimator.h | 58 + .../ns/speech_probability_estimator.cc | 104 + .../ns/speech_probability_estimator.h | 51 + .../audio_processing/ns/suppression_params.cc | 49 + .../audio_processing/ns/suppression_params.h | 30 + .../audio_processing/ns/wiener_filter.cc | 121 + .../audio_processing/ns/wiener_filter.h | 57 + .../render_queue_item_verifier.h | 36 + .../residual_echo_detector.cc | 209 + .../audio_processing/residual_echo_detector.h | 91 + .../modules/audio_processing/rms_level.cc | 138 + .../modules/audio_processing/rms_level.h | 78 + .../audio_processing/splitting_filter.cc | 142 + .../audio_processing/splitting_filter.h | 72 + .../three_band_filter_bank.cc | 273 ++ .../audio_processing/three_band_filter_bank.h | 77 + .../utility/cascaded_biquad_filter.cc | 126 + .../utility/cascaded_biquad_filter.h | 80 + .../utility/delay_estimator.cc | 708 +++ .../utility/delay_estimator.h | 257 ++ .../utility/delay_estimator_internal.h | 51 + .../utility/delay_estimator_wrapper.cc | 489 +++ .../utility/delay_estimator_wrapper.h | 248 ++ .../audio_processing/utility/pffft_wrapper.cc | 135 + .../audio_processing/utility/pffft_wrapper.h | 94 + .../audio_processing/utility/utility.go | 10 + .../modules/audio_processing/vad/common.h | 29 + .../modules/audio_processing/vad/gmm.cc | 61 + .../webrtc/modules/audio_processing/vad/gmm.h | 45 + .../audio_processing/vad/noise_gmm_tables.h | 82 + .../audio_processing/vad/pitch_based_vad.cc | 120 + .../audio_processing/vad/pitch_based_vad.h | 57 + .../audio_processing/vad/pitch_internal.cc | 55 + .../audio_processing/vad/pitch_internal.h | 30 + .../audio_processing/vad/pole_zero_filter.cc | 107 + .../audio_processing/vad/pole_zero_filter.h | 51 + .../audio_processing/vad/standalone_vad.cc | 91 + .../audio_processing/vad/standalone_vad.h | 69 + .../modules/audio_processing/vad/vad.go | 10 + .../audio_processing/vad/vad_audio_proc.cc | 275 ++ .../audio_processing/vad/vad_audio_proc.h | 88 + .../vad/vad_audio_proc_internal.h | 81 + .../vad/vad_circular_buffer.cc | 135 + .../vad/vad_circular_buffer.h | 69 + .../vad/voice_activity_detector.cc | 85 + .../vad/voice_activity_detector.h | 74 + .../audio_processing/vad/voice_gmm_tables.h | 77 + pkg/apm/webrtc/modules/third_party/fft/fft.h | 58 + pkg/apm/webrtc/rtc_base/arraysize.h | 32 + pkg/apm/webrtc/rtc_base/async_dns_resolver.h | 63 + pkg/apm/webrtc/rtc_base/async_packet_socket.h | 217 + pkg/apm/webrtc/rtc_base/async_socket.h | 73 + pkg/apm/webrtc/rtc_base/async_tcp_socket.h | 136 + pkg/apm/webrtc/rtc_base/async_udp_socket.h | 88 + pkg/apm/webrtc/rtc_base/base64.h | 47 + pkg/apm/webrtc/rtc_base/bit_buffer.h | 107 + pkg/apm/webrtc/rtc_base/bitrate_tracker.h | 65 + pkg/apm/webrtc/rtc_base/bitstream_reader.h | 152 + .../webrtc/rtc_base/boringssl_certificate.h | 87 + pkg/apm/webrtc/rtc_base/boringssl_identity.h | 85 + .../webrtc/rtc_base/bounded_inline_vector.h | 155 + .../rtc_base/bounded_inline_vector_impl.h | 216 + pkg/apm/webrtc/rtc_base/buffer.h | 464 ++ pkg/apm/webrtc/rtc_base/buffer_queue.h | 78 + pkg/apm/webrtc/rtc_base/byte_buffer.h | 220 + pkg/apm/webrtc/rtc_base/byte_order.h | 240 ++ pkg/apm/webrtc/rtc_base/callback_list.cc | 120 + pkg/apm/webrtc/rtc_base/callback_list.h | 223 + pkg/apm/webrtc/rtc_base/checks.cc | 240 ++ pkg/apm/webrtc/rtc_base/checks.h | 537 +++ pkg/apm/webrtc/rtc_base/compile_assert_c.h | 25 + .../webrtc/rtc_base/containers/containers.go | 10 + pkg/apm/webrtc/rtc_base/containers/flat_map.h | 374 ++ pkg/apm/webrtc/rtc_base/containers/flat_set.h | 178 + .../webrtc/rtc_base/containers/flat_tree.cc | 19 + .../webrtc/rtc_base/containers/flat_tree.h | 1099 +++++ pkg/apm/webrtc/rtc_base/containers/identity.h | 36 + pkg/apm/webrtc/rtc_base/containers/invoke.h | 162 + .../rtc_base/containers/move_only_int.h | 74 + .../webrtc/rtc_base/copy_on_write_buffer.cc | 127 + .../webrtc/rtc_base/copy_on_write_buffer.h | 329 ++ pkg/apm/webrtc/rtc_base/cpu_time.h | 37 + pkg/apm/webrtc/rtc_base/crc32.h | 46 + pkg/apm/webrtc/rtc_base/crypto_random.h | 107 + pkg/apm/webrtc/rtc_base/data_rate_limiter.h | 66 + .../deprecated/recursive_critical_section.h | 116 + pkg/apm/webrtc/rtc_base/dscp.h | 76 + pkg/apm/webrtc/rtc_base/event.cc | 210 + pkg/apm/webrtc/rtc_base/event.h | 147 + pkg/apm/webrtc/rtc_base/event_tracer.h | 102 + .../rtc_base/experiments/alr_experiment.h | 49 + .../balanced_degradation_settings.h | 142 + .../experiments/encoder_info_settings.h | 103 + .../rtc_base/experiments/experiments.go | 10 + .../rtc_base/experiments/field_trial_list.h | 226 + .../experiments/field_trial_parser.cc | 260 ++ .../rtc_base/experiments/field_trial_parser.h | 290 ++ .../rtc_base/experiments/field_trial_units.cc | 115 + .../rtc_base/experiments/field_trial_units.h | 41 + .../experiments/keyframe_interval_settings.h | 38 + .../min_video_bitrate_experiment.h | 32 + .../normalize_simulcast_size_experiment.h | 28 + .../experiments/quality_scaler_settings.h | 45 + .../experiments/quality_scaling_experiment.h | 62 + .../experiments/rate_control_settings.h | 89 + .../stable_target_rate_experiment.h | 37 + .../experiments/struct_parameters_parser.h | 110 + .../webrtc/rtc_base/file_rotating_stream.h | 183 + .../webrtc/rtc_base/firewall_socket_server.h | 146 + pkg/apm/webrtc/rtc_base/frequency_tracker.h | 57 + pkg/apm/webrtc/rtc_base/gtest_prod_util.h | 7 + pkg/apm/webrtc/rtc_base/gunit.h | 51 + pkg/apm/webrtc/rtc_base/ifaddrs_android.h | 38 + pkg/apm/webrtc/rtc_base/ifaddrs_converter.h | 56 + pkg/apm/webrtc/rtc_base/ignore_wundef.h | 33 + .../rtc_base/internal/default_socket_server.h | 32 + pkg/apm/webrtc/rtc_base/ip_address.h | 235 + pkg/apm/webrtc/rtc_base/log_sinks.h | 89 + pkg/apm/webrtc/rtc_base/logging.cc | 587 +++ pkg/apm/webrtc/rtc_base/logging.h | 749 ++++ .../rtc_base/mdns_responder_interface.h | 51 + .../webrtc/rtc_base/memory/aligned_malloc.cc | 98 + .../webrtc/rtc_base/memory/aligned_malloc.h | 57 + .../rtc_base/memory/always_valid_pointer.h | 248 ++ pkg/apm/webrtc/rtc_base/memory/fifo_buffer.h | 132 + pkg/apm/webrtc/rtc_base/memory/memory.go | 10 + pkg/apm/webrtc/rtc_base/memory_stream.h | 70 + pkg/apm/webrtc/rtc_base/memory_usage.h | 32 + pkg/apm/webrtc/rtc_base/message_digest.h | 152 + pkg/apm/webrtc/rtc_base/net_helper.h | 48 + pkg/apm/webrtc/rtc_base/net_helpers.h | 42 + pkg/apm/webrtc/rtc_base/network.h | 600 +++ pkg/apm/webrtc/rtc_base/network/ecn_marking.h | 22 + .../webrtc/rtc_base/network/received_packet.h | 98 + pkg/apm/webrtc/rtc_base/network/sent_packet.h | 84 + pkg/apm/webrtc/rtc_base/network_constants.h | 104 + pkg/apm/webrtc/rtc_base/network_monitor.h | 150 + .../webrtc/rtc_base/network_monitor_factory.h | 50 + pkg/apm/webrtc/rtc_base/network_route.h | 105 + pkg/apm/webrtc/rtc_base/null_socket_server.h | 45 + .../webrtc/rtc_base/numerics/divide_round.h | 60 + .../event_based_exponential_moving_average.cc | 84 + .../event_based_exponential_moving_average.h | 78 + .../rtc_base/numerics/event_rate_counter.cc | 53 + .../rtc_base/numerics/event_rate_counter.h | 48 + .../webrtc/rtc_base/numerics/exp_filter.cc | 43 + pkg/apm/webrtc/rtc_base/numerics/exp_filter.h | 56 + .../numerics/histogram_percentile_counter.cc | 82 + .../numerics/histogram_percentile_counter.h | 52 + pkg/apm/webrtc/rtc_base/numerics/math_utils.h | 75 + pkg/apm/webrtc/rtc_base/numerics/mod_ops.h | 142 + .../rtc_base/numerics/moving_average.cc | 63 + .../webrtc/rtc_base/numerics/moving_average.h | 73 + .../rtc_base/numerics/moving_max_counter.h | 126 + .../numerics/moving_percentile_filter.h | 103 + pkg/apm/webrtc/rtc_base/numerics/numerics.go | 10 + .../rtc_base/numerics/percentile_filter.h | 124 + pkg/apm/webrtc/rtc_base/numerics/rational.h | 28 + .../rtc_base/numerics/running_statistics.h | 171 + .../webrtc/rtc_base/numerics/safe_compare.h | 188 + .../rtc_base/numerics/safe_conversions.h | 85 + .../rtc_base/numerics/safe_conversions_impl.h | 180 + .../webrtc/rtc_base/numerics/safe_minmax.h | 346 ++ .../rtc_base/numerics/sample_counter.cc | 120 + .../webrtc/rtc_base/numerics/sample_counter.h | 69 + .../webrtc/rtc_base/numerics/sample_stats.cc | 158 + .../webrtc/rtc_base/numerics/sample_stats.h | 76 + .../numerics/sequence_number_unwrapper.h | 81 + .../rtc_base/numerics/sequence_number_util.h | 83 + pkg/apm/webrtc/rtc_base/one_time_event.h | 60 + pkg/apm/webrtc/rtc_base/openssl.h | 25 + pkg/apm/webrtc/rtc_base/openssl_adapter.h | 248 ++ pkg/apm/webrtc/rtc_base/openssl_certificate.h | 81 + pkg/apm/webrtc/rtc_base/openssl_digest.h | 59 + pkg/apm/webrtc/rtc_base/openssl_identity.h | 83 + pkg/apm/webrtc/rtc_base/openssl_key_pair.h | 69 + .../webrtc/rtc_base/openssl_session_cache.h | 80 + .../webrtc/rtc_base/openssl_stream_adapter.h | 280 ++ pkg/apm/webrtc/rtc_base/openssl_utility.h | 82 + pkg/apm/webrtc/rtc_base/operations_chain.h | 209 + .../webrtc/rtc_base/physical_socket_server.h | 329 ++ pkg/apm/webrtc/rtc_base/platform_thread.cc | 219 + pkg/apm/webrtc/rtc_base/platform_thread.h | 131 + .../webrtc/rtc_base/platform_thread_types.cc | 128 + .../webrtc/rtc_base/platform_thread_types.h | 77 + pkg/apm/webrtc/rtc_base/protobuf_utils.h | 30 + pkg/apm/webrtc/rtc_base/proxy_server.h | 102 + pkg/apm/webrtc/rtc_base/race_checker.cc | 58 + pkg/apm/webrtc/rtc_base/race_checker.h | 91 + pkg/apm/webrtc/rtc_base/random.cc | 87 + pkg/apm/webrtc/rtc_base/random.h | 96 + pkg/apm/webrtc/rtc_base/rate_limiter.h | 60 + pkg/apm/webrtc/rtc_base/rate_statistics.h | 105 + pkg/apm/webrtc/rtc_base/rate_tracker.h | 80 + pkg/apm/webrtc/rtc_base/ref_count.h | 31 + pkg/apm/webrtc/rtc_base/ref_counted_object.h | 98 + pkg/apm/webrtc/rtc_base/ref_counter.h | 75 + pkg/apm/webrtc/rtc_base/rolling_accumulator.h | 153 + pkg/apm/webrtc/rtc_base/rtc_base.go | 19 + pkg/apm/webrtc/rtc_base/rtc_certificate.h | 103 + .../rtc_base/rtc_certificate_generator.h | 93 + pkg/apm/webrtc/rtc_base/sanitizer.h | 176 + .../webrtc/rtc_base/server_socket_adapters.h | 52 + pkg/apm/webrtc/rtc_base/socket.h | 186 + pkg/apm/webrtc/rtc_base/socket_adapters.h | 88 + pkg/apm/webrtc/rtc_base/socket_address.h | 209 + pkg/apm/webrtc/rtc_base/socket_address_pair.h | 51 + pkg/apm/webrtc/rtc_base/socket_factory.h | 36 + pkg/apm/webrtc/rtc_base/socket_server.h | 77 + pkg/apm/webrtc/rtc_base/ssl_adapter.h | 139 + pkg/apm/webrtc/rtc_base/ssl_certificate.h | 151 + pkg/apm/webrtc/rtc_base/ssl_fingerprint.h | 85 + pkg/apm/webrtc/rtc_base/ssl_identity.h | 197 + pkg/apm/webrtc/rtc_base/ssl_roots.h | 2485 +++++++++++ pkg/apm/webrtc/rtc_base/ssl_stream_adapter.h | 332 ++ pkg/apm/webrtc/rtc_base/stream.h | 174 + pkg/apm/webrtc/rtc_base/string_encode.cc | 202 + pkg/apm/webrtc/rtc_base/string_encode.h | 151 + pkg/apm/webrtc/rtc_base/string_to_number.cc | 104 + pkg/apm/webrtc/rtc_base/string_to_number.h | 113 + pkg/apm/webrtc/rtc_base/string_utils.cc | 42 + pkg/apm/webrtc/rtc_base/string_utils.h | 147 + pkg/apm/webrtc/rtc_base/strings/json.h | 123 + pkg/apm/webrtc/rtc_base/strings/str_join.h | 56 + .../webrtc/rtc_base/strings/string_builder.cc | 133 + .../webrtc/rtc_base/strings/string_builder.h | 175 + .../webrtc/rtc_base/strings/string_format.cc | 41 + .../webrtc/rtc_base/strings/string_format.h | 39 + pkg/apm/webrtc/rtc_base/strings/strings.go | 10 + pkg/apm/webrtc/rtc_base/strong_alias.h | 76 + pkg/apm/webrtc/rtc_base/swap_queue.h | 249 ++ .../webrtc/rtc_base/synchronization/mutex.h | 72 + .../rtc_base/synchronization/mutex_abseil.h | 43 + .../synchronization/mutex_critical_section.h | 56 + .../rtc_base/synchronization/mutex_pthread.h | 101 + .../sequence_checker_internal.cc | 89 + .../sequence_checker_internal.h | 90 + .../webrtc/rtc_base/synchronization/sync.go | 10 + .../webrtc/rtc_base/synchronization/yield.cc | 36 + .../webrtc/rtc_base/synchronization/yield.h | 20 + .../rtc_base/synchronization/yield_policy.cc | 82 + .../rtc_base/synchronization/yield_policy.h | 47 + pkg/apm/webrtc/rtc_base/system/arch.h | 100 + pkg/apm/webrtc/rtc_base/system/asm_defines.h | 72 + pkg/apm/webrtc/rtc_base/system/assume.h | 73 + .../webrtc/rtc_base/system/cocoa_threading.h | 24 + .../webrtc/rtc_base/system/file_wrapper.cc | 136 + pkg/apm/webrtc/rtc_base/system/file_wrapper.h | 114 + pkg/apm/webrtc/rtc_base/system/gcd_helpers.h | 29 + .../webrtc/rtc_base/system/ignore_warnings.h | 29 + pkg/apm/webrtc/rtc_base/system/inline.h | 31 + pkg/apm/webrtc/rtc_base/system/no_cfi_icall.h | 33 + .../rtc_base/system/no_unique_address.h | 37 + pkg/apm/webrtc/rtc_base/system/rtc_export.h | 43 + .../rtc_base/system/rtc_export_template.h | 197 + pkg/apm/webrtc/rtc_base/system/system.go | 10 + pkg/apm/webrtc/rtc_base/system/unused.h | 28 + .../warn_current_thread_is_deadlocked.h | 24 + pkg/apm/webrtc/rtc_base/system_time.cc | 102 + pkg/apm/webrtc/rtc_base/system_time.h | 35 + pkg/apm/webrtc/rtc_base/task_queue_gcd.h | 24 + pkg/apm/webrtc/rtc_base/task_queue_stdlib.h | 24 + pkg/apm/webrtc/rtc_base/task_queue_win.h | 24 + .../rtc_base/task_utils/repeating_task.h | 91 + .../rtc_base/third_party/base64/base64.h | 135 + .../rtc_base/third_party/sigslot/sigslot.h | 649 +++ pkg/apm/webrtc/rtc_base/thread.h | 580 +++ pkg/apm/webrtc/rtc_base/thread_annotations.h | 98 + pkg/apm/webrtc/rtc_base/time_utils.cc | 258 ++ pkg/apm/webrtc/rtc_base/time_utils.h | 171 + pkg/apm/webrtc/rtc_base/timestamp_aligner.h | 101 + pkg/apm/webrtc/rtc_base/trace_categories.h | 37 + pkg/apm/webrtc/rtc_base/trace_event.h | 15 + pkg/apm/webrtc/rtc_base/type_traits.h | 152 + pkg/apm/webrtc/rtc_base/unique_id_generator.h | 160 + pkg/apm/webrtc/rtc_base/units/unit_base.h | 311 ++ pkg/apm/webrtc/rtc_base/untyped_function.h | 325 ++ .../webrtc/rtc_base/virtual_socket_server.h | 491 +++ pkg/apm/webrtc/rtc_base/weak_ptr.h | 291 ++ .../rtc_base/win/create_direct3d_device.h | 34 + .../rtc_base/win/get_activation_factory.h | 53 + pkg/apm/webrtc/rtc_base/win/hstring.h | 30 + .../rtc_base/win/scoped_com_initializer.h | 56 + pkg/apm/webrtc/rtc_base/win/windows_version.h | 156 + pkg/apm/webrtc/rtc_base/win32.h | 57 + pkg/apm/webrtc/rtc_base/win32_socket_init.h | 41 + pkg/apm/webrtc/rtc_base/zero_memory.cc | 38 + pkg/apm/webrtc/rtc_base/zero_memory.h | 43 + .../webrtc/system_wrappers/include/clock.h | 101 + .../include/cpu_features_wrapper.h | 42 + .../webrtc/system_wrappers/include/cpu_info.h | 28 + .../include/denormal_disabler.h | 56 + .../system_wrappers/include/field_trial.h | 116 + .../webrtc/system_wrappers/include/metrics.h | 468 ++ .../webrtc/system_wrappers/include/ntp_time.h | 136 + .../include/rtp_to_ntp_estimator.h | 72 + .../webrtc/system_wrappers/include/sleep.h | 24 + .../webrtc/system_wrappers/source/clock.cc | 100 + .../system_wrappers/source/cpu_features.cc | 117 + .../source/cpu_features_android.cc | 19 + .../source/cpu_features_linux.cc | 97 + .../webrtc/system_wrappers/source/cpu_info.cc | 75 + .../source/denormal_disabler.cc | 111 + .../system_wrappers/source/field_trial.cc | 208 + .../webrtc/system_wrappers/source/metrics.cc | 338 ++ .../source/rtp_to_ntp_estimator.cc | 153 + .../webrtc/system_wrappers/source/sleep.cc | 36 + .../webrtc/system_wrappers/source/system.go | 10 + .../abseil-cpp/absl/algorithm/algorithm.h | 64 + .../abseil-cpp/absl/algorithm/container.h | 1830 ++++++++ .../abseil-cpp/absl/base/attributes.h | 1077 +++++ .../third_party/abseil-cpp/absl/base/base.go | 10 + .../abseil-cpp/absl/base/call_once.h | 227 + .../third_party/abseil-cpp/absl/base/casts.h | 180 + .../third_party/abseil-cpp/absl/base/config.h | 880 ++++ .../abseil-cpp/absl/base/const_init.h | 76 + .../abseil-cpp/absl/base/cycleclock.cc | 72 + .../absl/base/dynamic_annotations.h | 477 +++ .../abseil-cpp/absl/base/fast_type_id.h | 45 + .../absl/base/internal/atomic_hook.h | 200 + .../base/internal/atomic_hook_test_helper.h | 34 + .../absl/base/internal/cycleclock.h | 144 + .../absl/base/internal/cycleclock_config.h | 55 + .../absl/base/internal/direct_mmap.h | 170 + .../abseil-cpp/absl/base/internal/endian.h | 279 ++ .../absl/base/internal/errno_saver.h | 43 + .../abseil-cpp/absl/base/internal/hide_ptr.h | 51 + .../abseil-cpp/absl/base/internal/identity.h | 39 + .../absl/base/internal/iterator_traits.h | 71 + .../internal/iterator_traits_test_helper.h | 97 + .../absl/base/internal/low_level_alloc.h | 127 + .../absl/base/internal/low_level_scheduling.h | 134 + .../base/internal/nullability_deprecated.h | 106 + .../absl/base/internal/per_thread_tls.h | 52 + .../abseil-cpp/absl/base/internal/poison.h | 59 + .../absl/base/internal/pretty_function.h | 33 + .../absl/base/internal/raw_logging.h | 217 + .../absl/base/internal/scheduling_mode.h | 58 + .../absl/base/internal/scoped_set_env.h | 45 + .../abseil-cpp/absl/base/internal/spinlock.h | 265 ++ .../absl/base/internal/spinlock_akaros.inc | 35 + .../absl/base/internal/spinlock_linux.inc | 71 + .../absl/base/internal/spinlock_posix.inc | 46 + .../absl/base/internal/spinlock_wait.h | 95 + .../absl/base/internal/spinlock_win32.inc | 40 + .../abseil-cpp/absl/base/internal/strerror.h | 39 + .../abseil-cpp/absl/base/internal/sysinfo.h | 74 + .../absl/base/internal/thread_identity.h | 273 ++ .../absl/base/internal/throw_delegate.h | 75 + .../abseil-cpp/absl/base/internal/tracing.h | 81 + .../absl/base/internal/tsan_mutex_interface.h | 68 + .../absl/base/internal/unaligned_access.h | 89 + .../absl/base/internal/unscaledcycleclock.h | 113 + .../base/internal/unscaledcycleclock_config.h | 62 + .../abseil-cpp/absl/base/log_severity.cc | 56 + .../abseil-cpp/absl/base/log_severity.h | 185 + .../abseil-cpp/absl/base/low_level_alloc.cc | 632 +++ .../third_party/abseil-cpp/absl/base/macros.h | 220 + .../abseil-cpp/absl/base/no_destructor.h | 191 + .../abseil-cpp/absl/base/nullability.h | 318 ++ .../abseil-cpp/absl/base/optimization.h | 312 ++ .../abseil-cpp/absl/base/options.h | 184 + .../abseil-cpp/absl/base/poison.cc | 84 + .../abseil-cpp/absl/base/policy_checks.h | 115 + .../third_party/abseil-cpp/absl/base/port.h | 25 + .../abseil-cpp/absl/base/prefetch.h | 209 + .../abseil-cpp/absl/base/raw_logging.cc | 280 ++ .../abseil-cpp/absl/base/scoped_set_env.cc | 81 + .../abseil-cpp/absl/base/spinlock.cc | 223 + .../abseil-cpp/absl/base/spinlock_wait.cc | 81 + .../abseil-cpp/absl/base/strerror.cc | 88 + .../abseil-cpp/absl/base/sysinfo.cc | 503 +++ .../abseil-cpp/absl/base/thread_annotations.h | 333 ++ .../abseil-cpp/absl/base/thread_identity.cc | 163 + .../abseil-cpp/absl/base/throw_delegate.cc | 203 + .../abseil-cpp/absl/base/tracing.cc | 39 + .../absl/base/unscaledcycleclock.cc | 130 + .../abseil-cpp/absl/cleanup/cleanup.h | 138 + .../absl/cleanup/internal/cleanup.h | 99 + .../abseil-cpp/absl/container/btree_map.h | 891 ++++ .../abseil-cpp/absl/container/btree_set.h | 826 ++++ .../abseil-cpp/absl/container/container.go | 10 + .../abseil-cpp/absl/container/fixed_array.h | 550 +++ .../abseil-cpp/absl/container/flat_hash_map.h | 692 +++ .../abseil-cpp/absl/container/flat_hash_set.h | 580 +++ .../absl/container/hash_container_defaults.h | 45 + .../absl/container/hashtablez_sampler.cc | 316 ++ ...ashtablez_sampler_force_weak_definition.cc | 31 + .../absl/container/inlined_vector.h | 1019 +++++ .../absl/container/internal/btree.h | 3149 ++++++++++++++ .../absl/container/internal/btree_container.h | 867 ++++ .../absl/container/internal/common.h | 250 ++ .../container/internal/common_policy_traits.h | 151 + .../container/internal/compressed_tuple.h | 271 ++ .../container/internal/container_memory.h | 491 +++ .../internal/hash_function_defaults.h | 276 ++ .../container/internal/hash_policy_traits.h | 203 + .../internal/hashtable_control_bytes.h | 527 +++ .../absl/container/internal/hashtable_debug.h | 102 + .../internal/hashtable_debug_hooks.h | 85 + .../container/internal/hashtablez_sampler.h | 294 ++ .../absl/container/internal/inlined_vector.h | 1102 +++++ .../absl/container/internal/layout.h | 828 ++++ .../container/internal/node_slot_policy.h | 95 + .../absl/container/internal/raw_hash_map.h | 357 ++ .../absl/container/internal/raw_hash_set.h | 3816 +++++++++++++++++ .../internal/raw_hash_set_resize_impl.h | 80 + .../absl/container/internal/test_allocator.h | 387 ++ .../internal/test_instance_tracker.h | 274 ++ .../absl/container/internal/tracked.h | 83 + .../abseil-cpp/absl/container/node_hash_map.h | 687 +++ .../abseil-cpp/absl/container/node_hash_set.h | 578 +++ .../abseil-cpp/absl/container/raw_hash_set.cc | 1747 ++++++++ .../abseil-cpp/absl/crc/cpu_detect.cc | 342 ++ .../third_party/abseil-cpp/absl/crc/crc.cc | 437 ++ .../third_party/abseil-cpp/absl/crc/crc.go | 10 + .../third_party/abseil-cpp/absl/crc/crc32c.cc | 95 + .../third_party/abseil-cpp/absl/crc/crc32c.h | 192 + .../abseil-cpp/absl/crc/crc_cord_state.cc | 131 + .../absl/crc/crc_memcpy_fallback.cc | 78 + .../absl/crc/crc_memcpy_x86_arm_combined.cc | 454 ++ .../absl/crc/crc_non_temporal_memcpy.cc | 93 + .../absl/crc/crc_x86_arm_combined.cc | 708 +++ .../abseil-cpp/absl/crc/internal/cpu_detect.h | 63 + .../abseil-cpp/absl/crc/internal/crc.h | 83 + .../internal/crc32_x86_arm_combined_simd.h | 278 ++ .../abseil-cpp/absl/crc/internal/crc32c.h | 39 + .../absl/crc/internal/crc32c_inline.h | 72 + .../absl/crc/internal/crc_cord_state.h | 159 + .../absl/crc/internal/crc_internal.h | 177 + .../abseil-cpp/absl/crc/internal/crc_memcpy.h | 122 + .../internal/non_temporal_arm_intrinsics.h | 79 + .../absl/crc/internal/non_temporal_memcpy.h | 195 + .../absl/debugging/address_is_readable.cc | 98 + .../abseil-cpp/absl/debugging/debugging.go | 10 + .../absl/debugging/decode_rust_punycode.cc | 258 ++ .../abseil-cpp/absl/debugging/demangle.cc | 2988 +++++++++++++ .../absl/debugging/demangle_rust.cc | 925 ++++ .../absl/debugging/elf_mem_image.cc | 413 ++ .../absl/debugging/examine_stack.cc | 320 ++ .../absl/debugging/failure_signal_handler.cc | 434 ++ .../absl/debugging/failure_signal_handler.h | 121 + .../debugging/internal/address_is_readable.h | 32 + .../absl/debugging/internal/addresses.h | 57 + .../internal/bounded_utf8_length_sequence.h | 126 + .../debugging/internal/decode_rust_punycode.h | 55 + .../absl/debugging/internal/demangle.h | 76 + .../absl/debugging/internal/demangle_rust.h | 42 + .../absl/debugging/internal/elf_mem_image.h | 141 + .../absl/debugging/internal/examine_stack.h | 64 + .../debugging/internal/stack_consumption.h | 50 + .../internal/stacktrace_aarch64-inl.inc | 281 ++ .../debugging/internal/stacktrace_arm-inl.inc | 148 + .../debugging/internal/stacktrace_config.h | 95 + .../internal/stacktrace_emscripten-inl.inc | 119 + .../internal/stacktrace_generic-inl.inc | 103 + .../internal/stacktrace_powerpc-inl.inc | 269 ++ .../internal/stacktrace_riscv-inl.inc | 203 + .../internal/stacktrace_unimplemented-inl.inc | 25 + .../internal/stacktrace_win32-inl.inc | 81 + .../debugging/internal/stacktrace_x86-inl.inc | 404 ++ .../absl/debugging/internal/symbolize.h | 153 + .../debugging/internal/utf8_for_code_point.h | 47 + .../absl/debugging/internal/vdso_support.h | 158 + .../abseil-cpp/absl/debugging/leak_check.cc | 73 + .../abseil-cpp/absl/debugging/leak_check.h | 150 + .../absl/debugging/stack_consumption.cc | 206 + .../abseil-cpp/absl/debugging/stacktrace.cc | 259 ++ .../abseil-cpp/absl/debugging/stacktrace.h | 299 ++ .../abseil-cpp/absl/debugging/symbolize.cc | 44 + .../abseil-cpp/absl/debugging/symbolize.h | 99 + .../absl/debugging/symbolize_darwin.inc | 102 + .../absl/debugging/symbolize_elf.inc | 1774 ++++++++ .../absl/debugging/symbolize_emscripten.inc | 76 + .../debugging/symbolize_unimplemented.inc | 40 + .../absl/debugging/symbolize_win32.inc | 101 + .../absl/debugging/utf8_for_code_point.cc | 70 + .../abseil-cpp/absl/debugging/vdso_support.cc | 205 + .../abseil-cpp/absl/flags/commandlineflag.cc | 35 + .../abseil-cpp/absl/flags/commandlineflag.h | 218 + .../abseil-cpp/absl/flags/config.h | 68 + .../abseil-cpp/absl/flags/declare.h | 68 + .../third_party/abseil-cpp/absl/flags/flag.cc | 711 +++ .../third_party/abseil-cpp/absl/flags/flag.h | 306 ++ .../abseil-cpp/absl/flags/flags.go | 10 + .../absl/flags/internal/commandlineflag.h | 68 + .../abseil-cpp/absl/flags/internal/flag.h | 970 +++++ .../abseil-cpp/absl/flags/internal/parse.h | 70 + .../absl/flags/internal/path_util.h | 62 + .../flags/internal/private_handle_accessor.h | 64 + .../absl/flags/internal/program_name.h | 50 + .../abseil-cpp/absl/flags/internal/registry.h | 98 + .../absl/flags/internal/sequence_lock.h | 187 + .../abseil-cpp/absl/flags/internal/usage.h | 106 + .../absl/flags/internal_commandlineflag.cc | 26 + .../abseil-cpp/absl/flags/internal_usage.cc | 558 +++ .../abseil-cpp/absl/flags/marshalling.cc | 291 ++ .../abseil-cpp/absl/flags/marshalling.h | 361 ++ .../abseil-cpp/absl/flags/parse.cc | 950 ++++ .../third_party/abseil-cpp/absl/flags/parse.h | 130 + .../absl/flags/private_handle_accessor.cc | 69 + .../abseil-cpp/absl/flags/program_name.cc | 61 + .../abseil-cpp/absl/flags/reflection.cc | 364 ++ .../abseil-cpp/absl/flags/reflection.h | 90 + .../abseil-cpp/absl/flags/usage.cc | 66 + .../third_party/abseil-cpp/absl/flags/usage.h | 43 + .../abseil-cpp/absl/flags/usage_config.cc | 170 + .../abseil-cpp/absl/flags/usage_config.h | 135 + .../absl/functional/any_invocable.h | 332 ++ .../abseil-cpp/absl/functional/bind_front.h | 194 + .../abseil-cpp/absl/functional/function_ref.h | 144 + .../absl/functional/internal/any_invocable.h | 775 ++++ .../absl/functional/internal/front_binder.h | 93 + .../absl/functional/internal/function_ref.h | 113 + .../abseil-cpp/absl/functional/overload.h | 72 + .../third_party/abseil-cpp/absl/hash/city.cc | 349 ++ .../third_party/abseil-cpp/absl/hash/hash.cc | 65 + .../third_party/abseil-cpp/absl/hash/hash.go | 10 + .../third_party/abseil-cpp/absl/hash/hash.h | 466 ++ .../abseil-cpp/absl/hash/internal/city.h | 78 + .../abseil-cpp/absl/hash/internal/hash.h | 1516 +++++++ .../absl/hash/internal/low_level_hash.h | 50 + .../absl/hash/internal/spy_hash_state.h | 281 ++ .../absl/hash/internal/weakly_mixed_integer.h | 38 + .../abseil-cpp/absl/hash/low_level_hash.cc | 105 + .../abseil-cpp/absl/log/absl_check.h | 117 + .../abseil-cpp/absl/log/absl_log.h | 115 + .../abseil-cpp/absl/log/absl_vlog_is_on.h | 95 + .../third_party/abseil-cpp/absl/log/check.h | 209 + .../abseil-cpp/absl/log/check_op.cc | 143 + .../abseil-cpp/absl/log/conditions.cc | 85 + .../abseil-cpp/absl/log/die_if_null.cc | 32 + .../abseil-cpp/absl/log/die_if_null.h | 76 + .../third_party/abseil-cpp/absl/log/flags.cc | 143 + .../third_party/abseil-cpp/absl/log/flags.h | 43 + .../abseil-cpp/absl/log/fnmatch.cc | 73 + .../abseil-cpp/absl/log/globals.cc | 178 + .../third_party/abseil-cpp/absl/log/globals.h | 230 + .../abseil-cpp/absl/log/initialize.cc | 38 + .../abseil-cpp/absl/log/initialize.h | 45 + .../absl/log/internal/append_truncated.h | 47 + .../abseil-cpp/absl/log/internal/check_impl.h | 150 + .../abseil-cpp/absl/log/internal/check_op.h | 482 +++ .../abseil-cpp/absl/log/internal/conditions.h | 244 ++ .../abseil-cpp/absl/log/internal/config.h | 45 + .../abseil-cpp/absl/log/internal/flags.h | 59 + .../abseil-cpp/absl/log/internal/fnmatch.h | 35 + .../abseil-cpp/absl/log/internal/globals.h | 101 + .../abseil-cpp/absl/log/internal/log_format.h | 78 + .../abseil-cpp/absl/log/internal/log_impl.h | 282 ++ .../absl/log/internal/log_message.h | 410 ++ .../absl/log/internal/log_sink_set.h | 54 + .../abseil-cpp/absl/log/internal/nullguard.h | 88 + .../abseil-cpp/absl/log/internal/nullstream.h | 128 + .../abseil-cpp/absl/log/internal/proto.h | 298 ++ .../abseil-cpp/absl/log/internal/strip.h | 100 + .../abseil-cpp/absl/log/internal/structured.h | 160 + .../absl/log/internal/structured_proto.h | 107 + .../absl/log/internal/test_actions.h | 90 + .../absl/log/internal/test_helpers.h | 71 + .../absl/log/internal/test_matchers.h | 94 + .../absl/log/internal/vlog_config.h | 165 + .../abseil-cpp/absl/log/internal/voidify.h | 50 + .../abseil-cpp/absl/log/internal_globals.cc | 145 + .../third_party/abseil-cpp/absl/log/log.go | 10 + .../third_party/abseil-cpp/absl/log/log.h | 378 ++ .../abseil-cpp/absl/log/log_entry.h | 221 + .../abseil-cpp/absl/log/log_format.cc | 205 + .../abseil-cpp/absl/log/log_message.cc | 730 ++++ .../abseil-cpp/absl/log/log_sink.cc | 23 + .../abseil-cpp/absl/log/log_sink.h | 71 + .../abseil-cpp/absl/log/log_sink_registry.h | 64 + .../abseil-cpp/absl/log/log_sink_set.cc | 296 ++ .../abseil-cpp/absl/log/log_streamer.h | 181 + .../abseil-cpp/absl/log/nullguard.cc | 35 + .../third_party/abseil-cpp/absl/log/proto.cc | 218 + .../abseil-cpp/absl/log/structured.h | 75 + .../abseil-cpp/absl/log/structured_proto.cc | 115 + .../abseil-cpp/absl/log/vlog_config.cc | 347 ++ .../abseil-cpp/absl/log/vlog_is_on.h | 74 + .../abseil-cpp/absl/memory/memory.h | 278 ++ .../abseil-cpp/absl/meta/type_traits.h | 537 +++ .../abseil-cpp/absl/numeric/bits.h | 262 ++ .../abseil-cpp/absl/numeric/int128.cc | 344 ++ .../abseil-cpp/absl/numeric/int128.h | 1204 ++++++ .../absl/numeric/int128_have_intrinsic.inc | 309 ++ .../absl/numeric/int128_no_intrinsic.inc | 349 ++ .../abseil-cpp/absl/numeric/internal/bits.h | 366 ++ .../absl/numeric/internal/representation.h | 55 + .../abseil-cpp/absl/numeric/numeric.go | 10 + .../absl/profiling/exponential_biased.cc | 93 + .../profiling/internal/exponential_biased.h | 130 + .../profiling/internal/periodic_sampler.h | 211 + .../absl/profiling/internal/sample_recorder.h | 253 ++ .../absl/profiling/periodic_sampler.cc | 53 + .../abseil-cpp/absl/profiling/profiling.go | 10 + .../absl/random/bernoulli_distribution.h | 202 + .../absl/random/beta_distribution.h | 429 ++ .../abseil-cpp/absl/random/bit_gen_ref.h | 187 + .../absl/random/discrete_distribution.cc | 108 + .../absl/random/discrete_distribution.h | 249 ++ .../abseil-cpp/absl/random/distributions.h | 450 ++ .../abseil-cpp/absl/random/entropy_pool.cc | 185 + .../absl/random/exponential_distribution.h | 166 + .../absl/random/gaussian_distribution.cc | 104 + .../absl/random/gaussian_distribution.h | 276 ++ .../absl/random/internal/chi_square.h | 89 + .../random/internal/distribution_caller.h | 96 + .../random/internal/distribution_test_util.h | 113 + .../absl/random/internal/entropy_pool.h | 35 + .../absl/random/internal/explicit_seed_seq.h | 92 + .../absl/random/internal/fast_uniform_bits.h | 271 ++ .../absl/random/internal/fastmath.h | 57 + .../absl/random/internal/generate_real.h | 144 + .../random/internal/iostream_state_saver.h | 248 ++ .../absl/random/internal/nonsecure_base.h | 160 + .../absl/random/internal/pcg_engine.h | 287 ++ .../absl/random/internal/platform.h | 171 + .../abseil-cpp/absl/random/internal/randen.h | 96 + .../absl/random/internal/randen_detect.h | 33 + .../absl/random/internal/randen_engine.h | 265 ++ .../absl/random/internal/randen_hwaes.h | 50 + .../absl/random/internal/randen_slow.h | 40 + .../absl/random/internal/randen_traits.h | 88 + .../absl/random/internal/salted_seed_seq.h | 165 + .../absl/random/internal/seed_material.h | 104 + .../absl/random/internal/sequence_urbg.h | 60 + .../abseil-cpp/absl/random/internal/traits.h | 149 + .../absl/random/internal/uniform_helper.h | 244 ++ .../absl/random/internal/wide_multiply.h | 95 + .../random/log_uniform_int_distribution.h | 253 ++ .../abseil-cpp/absl/random/mocking_bit_gen.h | 237 + .../absl/random/poisson_distribution.h | 262 ++ .../abseil-cpp/absl/random/randen.cc | 91 + .../abseil-cpp/absl/random/randen_detect.cc | 280 ++ .../abseil-cpp/absl/random/randen_hwaes.cc | 526 +++ .../absl/random/randen_round_keys.cc | 462 ++ .../abseil-cpp/absl/random/randen_slow.cc | 471 ++ .../abseil-cpp/absl/random/random.go | 10 + .../abseil-cpp/absl/random/random.h | 224 + .../absl/random/seed_gen_exception.cc | 45 + .../absl/random/seed_gen_exception.h | 55 + .../abseil-cpp/absl/random/seed_material.cc | 275 ++ .../abseil-cpp/absl/random/seed_sequences.cc | 33 + .../abseil-cpp/absl/random/seed_sequences.h | 112 + .../absl/random/uniform_int_distribution.h | 276 ++ .../absl/random/uniform_real_distribution.h | 204 + .../absl/random/zipf_distribution.h | 273 ++ .../absl/status/internal/status_internal.h | 131 + .../absl/status/internal/statusor_internal.h | 494 +++ .../abseil-cpp/absl/status/status.cc | 417 ++ .../abseil-cpp/absl/status/status.go | 10 + .../abseil-cpp/absl/status/status.h | 943 ++++ .../abseil-cpp/absl/status/status_internal.cc | 251 ++ .../absl/status/status_payload_printer.cc | 36 + .../absl/status/status_payload_printer.h | 52 + .../abseil-cpp/absl/status/statusor.cc | 106 + .../abseil-cpp/absl/status/statusor.h | 796 ++++ .../abseil-cpp/absl/strings/ascii.cc | 296 ++ .../abseil-cpp/absl/strings/ascii.h | 284 ++ .../abseil-cpp/absl/strings/charconv.cc | 1442 +++++++ .../abseil-cpp/absl/strings/charconv.h | 123 + .../absl/strings/charconv_bigint.cc | 357 ++ .../abseil-cpp/absl/strings/charconv_parse.cc | 504 +++ .../abseil-cpp/absl/strings/charset.h | 163 + .../abseil-cpp/absl/strings/cord.cc | 1577 +++++++ .../abseil-cpp/absl/strings/cord.h | 1764 ++++++++ .../abseil-cpp/absl/strings/cord_analysis.cc | 196 + .../abseil-cpp/absl/strings/cord_analysis.h | 63 + .../abseil-cpp/absl/strings/cord_buffer.h | 572 +++ .../abseil-cpp/absl/strings/cord_internal.cc | 70 + .../abseil-cpp/absl/strings/cord_rep_btree.cc | 1237 ++++++ .../absl/strings/cord_rep_btree_navigator.cc | 187 + .../absl/strings/cord_rep_btree_reader.cc | 69 + .../absl/strings/cord_rep_consume.cc | 64 + .../abseil-cpp/absl/strings/cord_rep_crc.cc | 56 + .../absl/strings/cord_test_helpers.h | 122 + .../absl/strings/cordz_functions.cc | 102 + .../abseil-cpp/absl/strings/cordz_handle.cc | 165 + .../abseil-cpp/absl/strings/cordz_info.cc | 418 ++ .../absl/strings/cordz_sample_token.cc | 64 + .../absl/strings/cordz_test_helpers.h | 153 + .../strings/damerau_levenshtein_distance.cc | 93 + .../abseil-cpp/absl/strings/escaping.cc | 1003 +++++ .../abseil-cpp/absl/strings/escaping.h | 185 + .../absl/strings/has_absl_stringify.h | 64 + .../absl/strings/has_ostream_operator.h | 42 + .../absl/strings/internal/charconv_bigint.h | 433 ++ .../absl/strings/internal/charconv_parse.h | 99 + .../absl/strings/internal/cord_data_edge.h | 63 + .../absl/strings/internal/cord_internal.h | 942 ++++ .../absl/strings/internal/cord_rep_btree.h | 944 ++++ .../internal/cord_rep_btree_navigator.h | 267 ++ .../strings/internal/cord_rep_btree_reader.h | 212 + .../absl/strings/internal/cord_rep_consume.h | 47 + .../absl/strings/internal/cord_rep_crc.h | 103 + .../absl/strings/internal/cord_rep_flat.h | 195 + .../strings/internal/cord_rep_test_util.h | 205 + .../absl/strings/internal/cordz_functions.h | 87 + .../absl/strings/internal/cordz_handle.h | 98 + .../absl/strings/internal/cordz_info.h | 303 ++ .../strings/internal/cordz_sample_token.h | 97 + .../absl/strings/internal/cordz_statistics.h | 88 + .../strings/internal/cordz_update_scope.h | 71 + .../strings/internal/cordz_update_tracker.h | 123 + .../internal/damerau_levenshtein_distance.h | 34 + .../absl/strings/internal/escaping.h | 57 + .../strings/internal/escaping_test_common.h | 133 + .../absl/strings/internal/memutil.h | 40 + .../strings/internal/numbers_test_common.h | 184 + .../absl/strings/internal/ostringstream.h | 114 + .../absl/strings/internal/pow10_helper.h | 40 + .../strings/internal/resize_uninitialized.h | 119 + .../absl/strings/internal/stl_type_traits.h | 248 ++ .../absl/strings/internal/str_format/arg.h | 671 +++ .../absl/strings/internal/str_format/bind.h | 237 + .../strings/internal/str_format/checker.h | 100 + .../internal/str_format/constexpr_parser.h | 357 ++ .../strings/internal/str_format/extension.h | 456 ++ .../internal/str_format/float_conversion.h | 37 + .../absl/strings/internal/str_format/output.h | 97 + .../absl/strings/internal/str_format/parser.h | 271 ++ .../absl/strings/internal/str_join_internal.h | 338 ++ .../strings/internal/str_split_internal.h | 520 +++ .../absl/strings/internal/string_constant.h | 67 + .../absl/strings/internal/stringify_sink.h | 57 + .../abseil-cpp/absl/strings/internal/utf8.h | 50 + .../absl/strings/internal_escaping.cc | 209 + .../abseil-cpp/absl/strings/match.cc | 133 + .../abseil-cpp/absl/strings/match.h | 129 + .../abseil-cpp/absl/strings/memutil.cc | 48 + .../abseil-cpp/absl/strings/numbers.cc | 1167 +++++ .../abseil-cpp/absl/strings/numbers.h | 346 ++ .../abseil-cpp/absl/strings/ostringstream.cc | 43 + .../abseil-cpp/absl/strings/pow10_helper.cc | 122 + .../abseil-cpp/absl/strings/str_cat.cc | 242 ++ .../abseil-cpp/absl/strings/str_cat.h | 628 +++ .../abseil-cpp/absl/strings/str_format.h | 887 ++++ .../abseil-cpp/absl/strings/str_format_arg.cc | 671 +++ .../absl/strings/str_format_bind.cc | 275 ++ .../absl/strings/str_format_extension.cc | 53 + .../strings/str_format_float_conversion.cc | 1458 +++++++ .../absl/strings/str_format_output.cc | 74 + .../absl/strings/str_format_parser.cc | 140 + .../abseil-cpp/absl/strings/str_join.h | 301 ++ .../abseil-cpp/absl/strings/str_replace.cc | 91 + .../abseil-cpp/absl/strings/str_replace.h | 222 + .../abseil-cpp/absl/strings/str_split.cc | 144 + .../abseil-cpp/absl/strings/str_split.h | 582 +++ .../abseil-cpp/absl/strings/string_view.cc | 257 ++ .../abseil-cpp/absl/strings/string_view.h | 766 ++++ .../abseil-cpp/absl/strings/stringify_sink.cc | 28 + .../abseil-cpp/absl/strings/strings.go | 10 + .../abseil-cpp/absl/strings/strip.h | 99 + .../abseil-cpp/absl/strings/substitute.cc | 187 + .../abseil-cpp/absl/strings/substitute.h | 768 ++++ .../abseil-cpp/absl/strings/utf8.cc | 53 + .../absl/synchronization/barrier.cc | 52 + .../abseil-cpp/absl/synchronization/barrier.h | 79 + .../absl/synchronization/blocking_counter.cc | 73 + .../absl/synchronization/blocking_counter.h | 107 + .../synchronization/create_thread_identity.cc | 152 + .../absl/synchronization/futex_waiter.cc | 107 + .../absl/synchronization/graphcycles.cc | 717 ++++ .../internal/create_thread_identity.h | 56 + .../absl/synchronization/internal/futex.h | 177 + .../synchronization/internal/futex_waiter.h | 63 + .../synchronization/internal/graphcycles.h | 146 + .../synchronization/internal/kernel_timeout.h | 178 + .../synchronization/internal/per_thread_sem.h | 119 + .../synchronization/internal/pthread_waiter.h | 60 + .../synchronization/internal/sem_waiter.h | 65 + .../synchronization/internal/stdcpp_waiter.h | 56 + .../synchronization/internal/thread_pool.h | 96 + .../absl/synchronization/internal/waiter.h | 69 + .../synchronization/internal/waiter_base.h | 90 + .../synchronization/internal/win32_waiter.h | 72 + .../absl/synchronization/kernel_timeout.cc | 220 + .../abseil-cpp/absl/synchronization/mutex.cc | 2821 ++++++++++++ .../abseil-cpp/absl/synchronization/mutex.h | 1247 ++++++ .../absl/synchronization/notification.cc | 85 + .../absl/synchronization/notification.h | 133 + .../absl/synchronization/per_thread_sem.cc | 106 + .../absl/synchronization/pthread_waiter.cc | 163 + .../absl/synchronization/sem_waiter.cc | 118 + .../absl/synchronization/stdcpp_waiter.cc | 87 + .../abseil-cpp/absl/synchronization/sync.go | 10 + .../absl/synchronization/waiter_base.cc | 38 + .../absl/synchronization/win32_waiter.cc | 147 + .../abseil-cpp/absl/time/civil_time.h | 589 +++ .../third_party/abseil-cpp/absl/time/clock.h | 78 + .../internal/cctz/include/cctz/civil_time.h | 332 ++ .../cctz/include/cctz/civil_time_detail.h | 632 +++ .../internal/cctz/include/cctz/time_zone.h | 460 ++ .../cctz/include/cctz/zone_info_source.h | 102 + .../time/internal/cctz/src/time_zone_fixed.h | 52 + .../time/internal/cctz/src/time_zone_if.h | 80 + .../time/internal/cctz/src/time_zone_impl.h | 97 + .../time/internal/cctz/src/time_zone_info.h | 128 + .../time/internal/cctz/src/time_zone_libc.h | 60 + .../time/internal/cctz/src/time_zone_posix.h | 132 + .../absl/time/internal/cctz/src/tzfile.h | 120 + .../time/internal/get_current_time_chrono.inc | 31 + .../time/internal/get_current_time_posix.inc | 24 + .../abseil-cpp/absl/time/internal/test_util.h | 33 + .../abseil-cpp/absl/time/src/civil_time.cc | 200 + .../absl/time/src/civil_time_detail.cc | 94 + .../abseil-cpp/absl/time/src/clock.cc | 604 +++ .../abseil-cpp/absl/time/src/duration.cc | 918 ++++ .../abseil-cpp/absl/time/src/format.cc | 162 + .../abseil-cpp/absl/time/src/src.go | 10 + .../abseil-cpp/absl/time/src/time.cc | 507 +++ .../absl/time/src/time_zone_fixed.cc | 140 + .../absl/time/src/time_zone_format.cc | 1029 +++++ .../abseil-cpp/absl/time/src/time_zone_if.cc | 47 + .../absl/time/src/time_zone_impl.cc | 115 + .../absl/time/src/time_zone_info.cc | 1070 +++++ .../absl/time/src/time_zone_libc.cc | 333 ++ .../absl/time/src/time_zone_lookup.cc | 335 ++ .../absl/time/src/time_zone_posix.cc | 159 + .../absl/time/src/zone_info_source.cc | 116 + .../third_party/abseil-cpp/absl/time/time.h | 1922 +++++++++ .../third_party/abseil-cpp/absl/types/any.h | 44 + .../abseil-cpp/absl/types/compare.h | 505 +++ .../abseil-cpp/absl/types/internal/span.h | 140 + .../abseil-cpp/absl/types/optional.h | 41 + .../third_party/abseil-cpp/absl/types/span.h | 797 ++++ .../abseil-cpp/absl/types/variant.h | 82 + .../abseil-cpp/absl/utility/utility.h | 55 + .../jsoncpp/source/include/json/json.h | 4 + pkg/apm/webrtc/third_party/pffft/src/pffft.c | 1881 ++++++++ pkg/apm/webrtc/third_party/pffft/src/pffft.go | 6 + pkg/apm/webrtc/third_party/pffft/src/pffft.h | 198 + .../third_party/rnnoise/src/rnn_activations.h | 102 + .../rnnoise/src/rnn_vad_weights.cc | 401 ++ .../third_party/rnnoise/src/rnn_vad_weights.h | 37 + pkg/apm/webrtc/third_party/rnnoise/src/src.go | 10 + pkg/apm/webrtc/webrtc.go | 64 + pkg/apm/webrtc/webrtc_darwin.go | 5 + pkg/apm/webrtc/webrtc_linux.go | 5 + pkg/apm/webrtc/webrtc_windows.go | 5 + pkg/console/pipeline.go | 410 ++ pkg/console/ringbuffer.go | 77 + pkg/console/tcp.go | 146 + pkg/portaudio/pa_src | 1 + pkg/portaudio/portaudio.go | 302 ++ pkg/portaudio/portaudio_darwin.go | 16 + pkg/portaudio/portaudio_linux.go | 14 + pkg/portaudio/portaudio_windows.go | 17 + 1484 files changed, 266796 insertions(+), 95 deletions(-) create mode 100644 .github/workflows/build-binaries.yaml create mode 100644 .gitmodules delete mode 100644 .goreleaser.yaml create mode 100644 Formula/lk.rb create mode 100644 cmd/lk/console.go create mode 100644 cmd/lk/console_stub.go create mode 100644 cmd/lk/console_tui.go create mode 100644 pkg/apm/apm.go create mode 100644 pkg/apm/bridge.cpp create mode 100644 pkg/apm/bridge.go create mode 100644 pkg/apm/bridge.h create mode 100644 pkg/apm/webrtc/api/adaptation/resource.h create mode 100644 pkg/apm/webrtc/api/api.go create mode 100644 pkg/apm/webrtc/api/array_view.h create mode 100644 pkg/apm/webrtc/api/async_dns_resolver.h create mode 100644 pkg/apm/webrtc/api/audio/audio.go create mode 100644 pkg/apm/webrtc/api/audio/audio_device.h create mode 100644 pkg/apm/webrtc/api/audio/audio_device_defines.h create mode 100644 pkg/apm/webrtc/api/audio/audio_frame.cc create mode 100644 pkg/apm/webrtc/api/audio/audio_frame.h create mode 100644 pkg/apm/webrtc/api/audio/audio_frame_processor.h create mode 100644 pkg/apm/webrtc/api/audio/audio_mixer.h create mode 100644 pkg/apm/webrtc/api/audio/audio_processing.cc create mode 100644 pkg/apm/webrtc/api/audio/audio_processing.h create mode 100644 pkg/apm/webrtc/api/audio/audio_processing_statistics.cc create mode 100644 pkg/apm/webrtc/api/audio/audio_processing_statistics.h create mode 100644 pkg/apm/webrtc/api/audio/audio_view.h create mode 100644 pkg/apm/webrtc/api/audio/builtin_audio_processing_builder.cc create mode 100644 pkg/apm/webrtc/api/audio/builtin_audio_processing_builder.h create mode 100644 pkg/apm/webrtc/api/audio/channel_layout.cc create mode 100644 pkg/apm/webrtc/api/audio/channel_layout.h create mode 100644 pkg/apm/webrtc/api/audio/echo_canceller3_config.cc create mode 100644 pkg/apm/webrtc/api/audio/echo_canceller3_config.h create mode 100644 pkg/apm/webrtc/api/audio/echo_canceller3_factory.cc create mode 100644 pkg/apm/webrtc/api/audio/echo_canceller3_factory.h create mode 100644 pkg/apm/webrtc/api/audio/echo_control.h create mode 100644 pkg/apm/webrtc/api/audio/echo_detector_creator.cc create mode 100644 pkg/apm/webrtc/api/audio/echo_detector_creator.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/L16/audio_decoder_L16.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/L16/audio_encoder_L16.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/audio_codec_pair_id.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/audio_decoder.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/audio_decoder_factory.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/audio_decoder_factory_template.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/audio_encoder.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/audio_encoder_factory.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/audio_encoder_factory_template.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/audio_format.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/builtin_audio_decoder_factory.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/builtin_audio_encoder_factory.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/g711/audio_decoder_g711.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/g711/audio_encoder_g711.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/g722/audio_decoder_g722.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/g722/audio_encoder_g722.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/g722/audio_encoder_g722_config.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_opus.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_multi_channel_opus_config.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_opus.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_opus_config.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/opus_audio_decoder_factory.h create mode 100644 pkg/apm/webrtc/api/audio_codecs/opus_audio_encoder_factory.h create mode 100644 pkg/apm/webrtc/api/audio_options.h create mode 100644 pkg/apm/webrtc/api/call/audio_sink.h create mode 100644 pkg/apm/webrtc/api/call/bitrate_allocation.h create mode 100644 pkg/apm/webrtc/api/call/transport.h create mode 100644 pkg/apm/webrtc/api/candidate.h create mode 100644 pkg/apm/webrtc/api/create_peerconnection_factory.h create mode 100644 pkg/apm/webrtc/api/crypto/crypto_options.h create mode 100644 pkg/apm/webrtc/api/crypto/frame_crypto_transformer.h create mode 100644 pkg/apm/webrtc/api/crypto/frame_decryptor_interface.h create mode 100644 pkg/apm/webrtc/api/crypto/frame_encryptor_interface.h create mode 100644 pkg/apm/webrtc/api/data_channel_event_observer_interface.h create mode 100644 pkg/apm/webrtc/api/data_channel_interface.h create mode 100644 pkg/apm/webrtc/api/dtls_transport_interface.h create mode 100644 pkg/apm/webrtc/api/dtmf_sender_interface.h create mode 100644 pkg/apm/webrtc/api/environment/environment.go create mode 100644 pkg/apm/webrtc/api/environment/environment.h create mode 100644 pkg/apm/webrtc/api/environment/environment_factory.cc create mode 100644 pkg/apm/webrtc/api/environment/environment_factory.h create mode 100644 pkg/apm/webrtc/api/fec_controller.h create mode 100644 pkg/apm/webrtc/api/fec_controller_override.h create mode 100644 pkg/apm/webrtc/api/field_trials.h create mode 100644 pkg/apm/webrtc/api/field_trials_registry.cc create mode 100644 pkg/apm/webrtc/api/field_trials_registry.h create mode 100644 pkg/apm/webrtc/api/field_trials_view.h create mode 100644 pkg/apm/webrtc/api/frame_transformer_factory.h create mode 100644 pkg/apm/webrtc/api/frame_transformer_interface.h create mode 100644 pkg/apm/webrtc/api/function_view.h create mode 100644 pkg/apm/webrtc/api/ice_transport_factory.h create mode 100644 pkg/apm/webrtc/api/ice_transport_interface.h create mode 100644 pkg/apm/webrtc/api/jsep.h create mode 100644 pkg/apm/webrtc/api/jsep_ice_candidate.h create mode 100644 pkg/apm/webrtc/api/jsep_session_description.h create mode 100644 pkg/apm/webrtc/api/legacy_stats_types.h create mode 100644 pkg/apm/webrtc/api/location.h create mode 100644 pkg/apm/webrtc/api/make_ref_counted.h create mode 100644 pkg/apm/webrtc/api/metronome/metronome.h create mode 100644 pkg/apm/webrtc/api/neteq/custom_neteq_factory.h create mode 100644 pkg/apm/webrtc/api/neteq/default_neteq_controller_factory.h create mode 100644 pkg/apm/webrtc/api/neteq/default_neteq_factory.h create mode 100644 pkg/apm/webrtc/api/neteq/neteq.h create mode 100644 pkg/apm/webrtc/api/neteq/neteq_controller.h create mode 100644 pkg/apm/webrtc/api/neteq/neteq_controller_factory.h create mode 100644 pkg/apm/webrtc/api/neteq/neteq_factory.h create mode 100644 pkg/apm/webrtc/api/neteq/tick_timer.h create mode 100644 pkg/apm/webrtc/api/network_state_predictor.h create mode 100644 pkg/apm/webrtc/api/notifier.h create mode 100644 pkg/apm/webrtc/api/numerics/numerics.go create mode 100644 pkg/apm/webrtc/api/numerics/samples_stats_counter.cc create mode 100644 pkg/apm/webrtc/api/numerics/samples_stats_counter.h create mode 100644 pkg/apm/webrtc/api/packet_socket_factory.h create mode 100644 pkg/apm/webrtc/api/peer_connection_interface.h create mode 100644 pkg/apm/webrtc/api/priority.h create mode 100644 pkg/apm/webrtc/api/ref_count.h create mode 100644 pkg/apm/webrtc/api/ref_counted_base.h create mode 100644 pkg/apm/webrtc/api/rtc_error.h create mode 100644 pkg/apm/webrtc/api/rtc_event_log/rtc_event_log.h create mode 100644 pkg/apm/webrtc/api/rtp_headers.h create mode 100644 pkg/apm/webrtc/api/rtp_packet_info.h create mode 100644 pkg/apm/webrtc/api/rtp_packet_infos.h create mode 100644 pkg/apm/webrtc/api/rtp_packet_sender.h create mode 100644 pkg/apm/webrtc/api/rtp_parameters.h create mode 100644 pkg/apm/webrtc/api/rtp_receiver_interface.h create mode 100644 pkg/apm/webrtc/api/rtp_sender_interface.h create mode 100644 pkg/apm/webrtc/api/rtp_transceiver_direction.h create mode 100644 pkg/apm/webrtc/api/rtp_transceiver_interface.h create mode 100644 pkg/apm/webrtc/api/scoped_refptr.h create mode 100644 pkg/apm/webrtc/api/sctp_transport_interface.h create mode 100644 pkg/apm/webrtc/api/sequence_checker.h create mode 100644 pkg/apm/webrtc/api/set_local_description_observer_interface.h create mode 100644 pkg/apm/webrtc/api/set_remote_description_observer_interface.h create mode 100644 pkg/apm/webrtc/api/stats/attribute.h create mode 100644 pkg/apm/webrtc/api/stats/rtc_stats.h create mode 100644 pkg/apm/webrtc/api/stats/rtc_stats_collector_callback.h create mode 100644 pkg/apm/webrtc/api/stats/rtc_stats_report.h create mode 100644 pkg/apm/webrtc/api/stats/rtcstats_objects.h create mode 100644 pkg/apm/webrtc/api/task_queue/default_task_queue_factory.h create mode 100644 pkg/apm/webrtc/api/task_queue/gcd/default_task_queue_factory_gcd.cc create mode 100644 pkg/apm/webrtc/api/task_queue/gcd/gcd.go create mode 100644 pkg/apm/webrtc/api/task_queue/gcd/gcd_helpers.m create mode 100644 pkg/apm/webrtc/api/task_queue/gcd/task_queue_gcd.cc create mode 100644 pkg/apm/webrtc/api/task_queue/gcd/task_queue_gcd.h create mode 100644 pkg/apm/webrtc/api/task_queue/pending_task_safety_flag.cc create mode 100644 pkg/apm/webrtc/api/task_queue/pending_task_safety_flag.h create mode 100644 pkg/apm/webrtc/api/task_queue/stdlib/default_task_queue_factory_stdlib.cc create mode 100644 pkg/apm/webrtc/api/task_queue/stdlib/stdlib.go create mode 100644 pkg/apm/webrtc/api/task_queue/stdlib/task_queue_stdlib.cc create mode 100644 pkg/apm/webrtc/api/task_queue/stdlib/task_queue_stdlib.h create mode 100644 pkg/apm/webrtc/api/task_queue/task_queue.go create mode 100644 pkg/apm/webrtc/api/task_queue/task_queue_base.cc create mode 100644 pkg/apm/webrtc/api/task_queue/task_queue_base.h create mode 100644 pkg/apm/webrtc/api/task_queue/task_queue_factory.h create mode 100644 pkg/apm/webrtc/api/transport/bandwidth_estimation_settings.h create mode 100644 pkg/apm/webrtc/api/transport/bandwidth_usage.h create mode 100644 pkg/apm/webrtc/api/transport/bitrate_settings.cc create mode 100644 pkg/apm/webrtc/api/transport/bitrate_settings.h create mode 100644 pkg/apm/webrtc/api/transport/data_channel_transport_interface.h create mode 100644 pkg/apm/webrtc/api/transport/ecn_marking.h create mode 100644 pkg/apm/webrtc/api/transport/enums.h create mode 100644 pkg/apm/webrtc/api/transport/field_trial_based_config.cc create mode 100644 pkg/apm/webrtc/api/transport/field_trial_based_config.h create mode 100644 pkg/apm/webrtc/api/transport/goog_cc_factory.h create mode 100644 pkg/apm/webrtc/api/transport/network_control.h create mode 100644 pkg/apm/webrtc/api/transport/network_types.cc create mode 100644 pkg/apm/webrtc/api/transport/network_types.h create mode 100644 pkg/apm/webrtc/api/transport/rtp/corruption_detection_message.h create mode 100644 pkg/apm/webrtc/api/transport/rtp/dependency_descriptor.h create mode 100644 pkg/apm/webrtc/api/transport/rtp/rtp_source.h create mode 100644 pkg/apm/webrtc/api/transport/sctp_transport_factory_interface.h create mode 100644 pkg/apm/webrtc/api/transport/stun.h create mode 100644 pkg/apm/webrtc/api/transport/transport.go create mode 100644 pkg/apm/webrtc/api/turn_customizer.h create mode 100644 pkg/apm/webrtc/api/uma_metrics.h create mode 100644 pkg/apm/webrtc/api/units/data_rate.cc create mode 100644 pkg/apm/webrtc/api/units/data_rate.h create mode 100644 pkg/apm/webrtc/api/units/data_size.cc create mode 100644 pkg/apm/webrtc/api/units/data_size.h create mode 100644 pkg/apm/webrtc/api/units/frequency.cc create mode 100644 pkg/apm/webrtc/api/units/frequency.h create mode 100644 pkg/apm/webrtc/api/units/time_delta.cc create mode 100644 pkg/apm/webrtc/api/units/time_delta.h create mode 100644 pkg/apm/webrtc/api/units/timestamp.cc create mode 100644 pkg/apm/webrtc/api/units/timestamp.h create mode 100644 pkg/apm/webrtc/api/units/units.go create mode 100644 pkg/apm/webrtc/api/video/color_space.h create mode 100644 pkg/apm/webrtc/api/video/video_content_type.h create mode 100644 pkg/apm/webrtc/api/video/video_rotation.h create mode 100644 pkg/apm/webrtc/api/voip/voip_base.h create mode 100644 pkg/apm/webrtc/api/voip/voip_codec.h create mode 100644 pkg/apm/webrtc/api/voip/voip_dtmf.h create mode 100644 pkg/apm/webrtc/api/voip/voip_engine.h create mode 100644 pkg/apm/webrtc/api/voip/voip_engine_factory.h create mode 100644 pkg/apm/webrtc/api/voip/voip_network.h create mode 100644 pkg/apm/webrtc/api/voip/voip_statistics.h create mode 100644 pkg/apm/webrtc/api/voip/voip_volume_control.h create mode 100644 pkg/apm/webrtc/api/webrtc_key_value_config.h create mode 100644 pkg/apm/webrtc/common_audio/audio_converter.cc create mode 100644 pkg/apm/webrtc/common_audio/audio_converter.h create mode 100644 pkg/apm/webrtc/common_audio/audio_util.cc create mode 100644 pkg/apm/webrtc/common_audio/avx2/avx2.go create mode 100644 pkg/apm/webrtc/common_audio/avx2/fir_filter_avx2.cc create mode 100644 pkg/apm/webrtc/common_audio/channel_buffer.cc create mode 100644 pkg/apm/webrtc/common_audio/channel_buffer.h create mode 100644 pkg/apm/webrtc/common_audio/common_audio.go create mode 100644 pkg/apm/webrtc/common_audio/common_audio_amd64.go create mode 100644 pkg/apm/webrtc/common_audio/fir_filter.h create mode 100644 pkg/apm/webrtc/common_audio/fir_filter_avx2.h create mode 100644 pkg/apm/webrtc/common_audio/fir_filter_c.cc create mode 100644 pkg/apm/webrtc/common_audio/fir_filter_c.h create mode 100644 pkg/apm/webrtc/common_audio/fir_filter_factory.cc create mode 100644 pkg/apm/webrtc/common_audio/fir_filter_factory.h create mode 100644 pkg/apm/webrtc/common_audio/fir_filter_neon.h create mode 100644 pkg/apm/webrtc/common_audio/fir_filter_neon_arm64.cc create mode 100644 pkg/apm/webrtc/common_audio/fir_filter_sse.h create mode 100644 pkg/apm/webrtc/common_audio/fir_filter_sse_amd64.cc create mode 100644 pkg/apm/webrtc/common_audio/include/audio_util.h create mode 100644 pkg/apm/webrtc/common_audio/real_fourier.cc create mode 100644 pkg/apm/webrtc/common_audio/real_fourier.h create mode 100644 pkg/apm/webrtc/common_audio/real_fourier_ooura.cc create mode 100644 pkg/apm/webrtc/common_audio/real_fourier_ooura.h create mode 100644 pkg/apm/webrtc/common_audio/resampler/avx2/avx2.go create mode 100644 pkg/apm/webrtc/common_audio/resampler/avx2/sinc_resampler_avx2.cc create mode 100644 pkg/apm/webrtc/common_audio/resampler/include/push_resampler.h create mode 100644 pkg/apm/webrtc/common_audio/resampler/include/resampler.h create mode 100644 pkg/apm/webrtc/common_audio/resampler/push_resampler.cc create mode 100644 pkg/apm/webrtc/common_audio/resampler/push_sinc_resampler.cc create mode 100644 pkg/apm/webrtc/common_audio/resampler/push_sinc_resampler.h create mode 100644 pkg/apm/webrtc/common_audio/resampler/resampler.cc create mode 100644 pkg/apm/webrtc/common_audio/resampler/resampler.go create mode 100644 pkg/apm/webrtc/common_audio/resampler/sinc_resampler.cc create mode 100644 pkg/apm/webrtc/common_audio/resampler/sinc_resampler.h create mode 100644 pkg/apm/webrtc/common_audio/resampler/sinc_resampler_neon_arm64.cc create mode 100644 pkg/apm/webrtc/common_audio/resampler/sinc_resampler_sse_amd64.cc create mode 100644 pkg/apm/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc create mode 100644 pkg/apm/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h create mode 100644 pkg/apm/webrtc/common_audio/ring_buffer.c create mode 100644 pkg/apm/webrtc/common_audio/ring_buffer.h create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/auto_correlation.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/complex_bit_reverse.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/complex_fft.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/complex_fft_tables.h create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/copy_set_operations.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/cross_correlation.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/cross_correlation_neon_arm64.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/division_operations.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/dot_product_with_scale.cc create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/dot_product_with_scale.h create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/downsample_fast.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/downsample_fast_neon_arm64.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/energy.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/filter_ar.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/filter_ar_fast_q12.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/filter_ma_fast_q12.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/get_hanning_window.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/get_scaling_square.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/include/real_fft.h create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/include/signal_processing_library.h create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl.h create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl_mips.h create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/levinson_durbin.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/min_max_operations.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/min_max_operations_neon_arm64.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/randomization_functions.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/real_fft.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/resample.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/resample_48khz.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/resample_by_2.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/resample_by_2_internal.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/resample_by_2_internal.h create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/resample_fractional.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/sp.go create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/spl_init.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/spl_inl.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/spl_sqrt.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/splitting_filter.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/vector_operations.c create mode 100644 pkg/apm/webrtc/common_audio/signal_processing/vector_scaling_operations.c create mode 100644 pkg/apm/webrtc/common_audio/smoothing_filter.cc create mode 100644 pkg/apm/webrtc/common_audio/smoothing_filter.h create mode 100644 pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/fft_size_128.go create mode 100644 pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.cc create mode 100644 pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.h create mode 100644 pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_neon_arm64.cc create mode 100644 pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_sse2_amd64.cc create mode 100644 pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h create mode 100644 pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h create mode 100644 pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.cc create mode 100644 pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.h create mode 100644 pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft_size_256.go create mode 100644 pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c create mode 100644 pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.go create mode 100644 pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h create mode 100644 pkg/apm/webrtc/common_audio/vad/include/vad.h create mode 100644 pkg/apm/webrtc/common_audio/vad/include/webrtc_vad.h create mode 100644 pkg/apm/webrtc/common_audio/vad/vad.cc create mode 100644 pkg/apm/webrtc/common_audio/vad/vad.go create mode 100644 pkg/apm/webrtc/common_audio/vad/vad_core.c create mode 100644 pkg/apm/webrtc/common_audio/vad/vad_core.h create mode 100644 pkg/apm/webrtc/common_audio/vad/vad_filterbank.c create mode 100644 pkg/apm/webrtc/common_audio/vad/vad_filterbank.h create mode 100644 pkg/apm/webrtc/common_audio/vad/vad_gmm.c create mode 100644 pkg/apm/webrtc/common_audio/vad/vad_gmm.h create mode 100644 pkg/apm/webrtc/common_audio/vad/vad_sp.c create mode 100644 pkg/apm/webrtc/common_audio/vad/vad_sp.h create mode 100644 pkg/apm/webrtc/common_audio/vad/webrtc_vad.c create mode 100644 pkg/apm/webrtc/common_audio/wav_file.cc create mode 100644 pkg/apm/webrtc/common_audio/wav_file.h create mode 100644 pkg/apm/webrtc/common_audio/wav_header.cc create mode 100644 pkg/apm/webrtc/common_audio/wav_header.h create mode 100644 pkg/apm/webrtc/common_audio/window_generator.cc create mode 100644 pkg/apm/webrtc/common_audio/window_generator.h create mode 100644 pkg/apm/webrtc/experiments/registered_field_trials.h create mode 100644 pkg/apm/webrtc/include/modules/audio_processing/aec_dump.h create mode 100644 pkg/apm/webrtc/include/modules/audio_processing/audio_frame_proxies.h create mode 100644 pkg/apm/webrtc/include/modules/audio_processing/audio_frame_view.h create mode 100644 pkg/apm/webrtc/include/modules/audio_processing/audio_processing.h create mode 100644 pkg/apm/webrtc/include/modules/audio_processing/audio_processing_statistics.h create mode 100644 pkg/apm/webrtc/include/modules/audio_processing/mock_audio_processing.h create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/bandwidth_info.h create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.h create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac.go create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.c create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.h create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.c create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.h create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/settings.h create mode 100644 pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/structs.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/aec3.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/aec3_common.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/aec3_common.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/aec3_fft.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/aec3_fft.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/aec_state.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/aec_state.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/alignment_mixer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/alignment_mixer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/avx2/adaptive_fir_filter_avx2.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/avx2/adaptive_fir_filter_erl_avx2.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/avx2/avx2.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/avx2/fft_data_avx2.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/avx2/matched_filter_avx2.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/avx2/vector_math_avx2.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/block.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/block_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/block_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/block_delay_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/block_delay_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/block_framer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/block_framer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/block_processor.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/block_processor.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/block_processor_metrics.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/block_processor_metrics.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/clockdrift_detector.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/clockdrift_detector.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/comfort_noise_generator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/config_selector.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/config_selector.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/decimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/decimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/delay_estimate.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_audibility.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_audibility.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_canceller3.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_canceller3.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_variability.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_variability.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover_metrics.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover_metrics.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/erl_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/erl_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/erle_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/erle_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/fft_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/fft_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/fft_data.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/filter_analyzer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/filter_analyzer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/frame_blocker.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/frame_blocker.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/moving_average.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/moving_average.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/multi_channel_content_detector.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/multi_channel_content_detector.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/nearend_detector.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/render_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/render_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/render_signal_analyzer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/render_signal_analyzer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/reverb_frequency_response.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/reverb_frequency_response.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/spectrum_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/spectrum_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/stationarity_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/stationarity_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/subband_erle_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/subband_erle_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/subband_nearend_detector.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/subband_nearend_detector.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/subtractor.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/subtractor.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/suppression_filter.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/suppression_filter.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/suppression_gain.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/suppression_gain.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/transparent_mode.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/transparent_mode.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec3/vector_math.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump_factory.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump_impl.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec_dump/capture_stream_info.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aec_dump/null_aec_dump_factory.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aecm/aecm.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core_c.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core_neon_arm64.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aecm/aecm_defines.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/aecm/echo_control_mobile.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/aecm/echo_control_mobile.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/agc.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/agc.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/agc.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/agc_manager_direct.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/agc_manager_direct.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/gain_control.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/legacy/analog_agc.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/legacy/analog_agc.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/legacy/digital_agc.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/legacy/digital_agc.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/legacy/gain_control.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/legacy/legacy.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/loudness_histogram.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/loudness_histogram.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/utility.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc/utility.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/agc2.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/agc2_common.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/agc2_testing_common.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/agc2_testing_common.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/biquad_filter.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/biquad_filter.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor_level_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor_level_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/cpu_features.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/cpu_features.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/gain_applier.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/gain_applier.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/gain_map_internal.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_controller.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_controller.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_stats_reporter.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_stats_reporter.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/limiter.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/limiter.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/noise_level_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/noise_level_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/avx2/avx2.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/avx2/vector_math_avx2.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/common.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/ring_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_fc.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_fc.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_gru.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_gru.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/vector_math.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/speech_level_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/speech_level_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/speech_probability_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/speech_probability_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/vad_wrapper.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/vad_wrapper.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/vector_float_frame.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/agc2/vector_float_frame.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/apm.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/apm_amd64.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/audio_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/audio_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/audio_processing_impl.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/audio_processing_impl.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/echo_control_mobile_impl.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/echo_control_mobile_impl.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/echo_detector/circular_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/echo_detector/circular_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/echo_detector/echo_detector.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/echo_detector/moving_max.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/echo_detector/moving_max.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/gain_control_impl.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/gain_control_impl.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/gain_controller2.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/gain_controller2.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/high_pass_filter.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/high_pass_filter.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/include/aec_dump.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/include/aec_dump.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/include/audio_frame_proxies.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/include/audio_frame_proxies.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/include/audio_frame_view.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/include/audio_processing.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/include/audio_processing_statistics.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/include/include.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/logging/apm_data_dumper.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/logging/apm_data_dumper.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/logging/logging.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/fast_math.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/fast_math.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/histograms.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/histograms.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/noise_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/noise_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/noise_suppressor.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/noise_suppressor.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/ns.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/ns_common.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/ns_config.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/ns_fft.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/ns_fft.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/quantile_noise_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/quantile_noise_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/signal_model.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/signal_model.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/signal_model_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/signal_model_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/speech_probability_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/speech_probability_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/suppression_params.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/suppression_params.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/wiener_filter.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/ns/wiener_filter.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/render_queue_item_verifier.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/residual_echo_detector.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/residual_echo_detector.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/rms_level.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/rms_level.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/splitting_filter.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/splitting_filter.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/three_band_filter_bank.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/three_band_filter_bank.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_internal.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/utility/pffft_wrapper.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/utility/pffft_wrapper.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/utility/utility.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/common.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/gmm.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/gmm.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/noise_gmm_tables.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/pitch_based_vad.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/pitch_based_vad.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/pitch_internal.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/pitch_internal.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/pole_zero_filter.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/pole_zero_filter.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/standalone_vad.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/standalone_vad.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/vad.go create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc_internal.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/vad_circular_buffer.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/vad_circular_buffer.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/voice_activity_detector.cc create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/voice_activity_detector.h create mode 100644 pkg/apm/webrtc/modules/audio_processing/vad/voice_gmm_tables.h create mode 100644 pkg/apm/webrtc/modules/third_party/fft/fft.h create mode 100644 pkg/apm/webrtc/rtc_base/arraysize.h create mode 100644 pkg/apm/webrtc/rtc_base/async_dns_resolver.h create mode 100644 pkg/apm/webrtc/rtc_base/async_packet_socket.h create mode 100644 pkg/apm/webrtc/rtc_base/async_socket.h create mode 100644 pkg/apm/webrtc/rtc_base/async_tcp_socket.h create mode 100644 pkg/apm/webrtc/rtc_base/async_udp_socket.h create mode 100644 pkg/apm/webrtc/rtc_base/base64.h create mode 100644 pkg/apm/webrtc/rtc_base/bit_buffer.h create mode 100644 pkg/apm/webrtc/rtc_base/bitrate_tracker.h create mode 100644 pkg/apm/webrtc/rtc_base/bitstream_reader.h create mode 100644 pkg/apm/webrtc/rtc_base/boringssl_certificate.h create mode 100644 pkg/apm/webrtc/rtc_base/boringssl_identity.h create mode 100644 pkg/apm/webrtc/rtc_base/bounded_inline_vector.h create mode 100644 pkg/apm/webrtc/rtc_base/bounded_inline_vector_impl.h create mode 100644 pkg/apm/webrtc/rtc_base/buffer.h create mode 100644 pkg/apm/webrtc/rtc_base/buffer_queue.h create mode 100644 pkg/apm/webrtc/rtc_base/byte_buffer.h create mode 100644 pkg/apm/webrtc/rtc_base/byte_order.h create mode 100644 pkg/apm/webrtc/rtc_base/callback_list.cc create mode 100644 pkg/apm/webrtc/rtc_base/callback_list.h create mode 100644 pkg/apm/webrtc/rtc_base/checks.cc create mode 100644 pkg/apm/webrtc/rtc_base/checks.h create mode 100644 pkg/apm/webrtc/rtc_base/compile_assert_c.h create mode 100644 pkg/apm/webrtc/rtc_base/containers/containers.go create mode 100644 pkg/apm/webrtc/rtc_base/containers/flat_map.h create mode 100644 pkg/apm/webrtc/rtc_base/containers/flat_set.h create mode 100644 pkg/apm/webrtc/rtc_base/containers/flat_tree.cc create mode 100644 pkg/apm/webrtc/rtc_base/containers/flat_tree.h create mode 100644 pkg/apm/webrtc/rtc_base/containers/identity.h create mode 100644 pkg/apm/webrtc/rtc_base/containers/invoke.h create mode 100644 pkg/apm/webrtc/rtc_base/containers/move_only_int.h create mode 100644 pkg/apm/webrtc/rtc_base/copy_on_write_buffer.cc create mode 100644 pkg/apm/webrtc/rtc_base/copy_on_write_buffer.h create mode 100644 pkg/apm/webrtc/rtc_base/cpu_time.h create mode 100644 pkg/apm/webrtc/rtc_base/crc32.h create mode 100644 pkg/apm/webrtc/rtc_base/crypto_random.h create mode 100644 pkg/apm/webrtc/rtc_base/data_rate_limiter.h create mode 100644 pkg/apm/webrtc/rtc_base/deprecated/recursive_critical_section.h create mode 100644 pkg/apm/webrtc/rtc_base/dscp.h create mode 100644 pkg/apm/webrtc/rtc_base/event.cc create mode 100644 pkg/apm/webrtc/rtc_base/event.h create mode 100644 pkg/apm/webrtc/rtc_base/event_tracer.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/alr_experiment.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/balanced_degradation_settings.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/encoder_info_settings.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/experiments.go create mode 100644 pkg/apm/webrtc/rtc_base/experiments/field_trial_list.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/field_trial_parser.cc create mode 100644 pkg/apm/webrtc/rtc_base/experiments/field_trial_parser.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/field_trial_units.cc create mode 100644 pkg/apm/webrtc/rtc_base/experiments/field_trial_units.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/keyframe_interval_settings.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/min_video_bitrate_experiment.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/normalize_simulcast_size_experiment.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/quality_scaler_settings.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/quality_scaling_experiment.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/rate_control_settings.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/stable_target_rate_experiment.h create mode 100644 pkg/apm/webrtc/rtc_base/experiments/struct_parameters_parser.h create mode 100644 pkg/apm/webrtc/rtc_base/file_rotating_stream.h create mode 100644 pkg/apm/webrtc/rtc_base/firewall_socket_server.h create mode 100644 pkg/apm/webrtc/rtc_base/frequency_tracker.h create mode 100644 pkg/apm/webrtc/rtc_base/gtest_prod_util.h create mode 100644 pkg/apm/webrtc/rtc_base/gunit.h create mode 100644 pkg/apm/webrtc/rtc_base/ifaddrs_android.h create mode 100644 pkg/apm/webrtc/rtc_base/ifaddrs_converter.h create mode 100644 pkg/apm/webrtc/rtc_base/ignore_wundef.h create mode 100644 pkg/apm/webrtc/rtc_base/internal/default_socket_server.h create mode 100644 pkg/apm/webrtc/rtc_base/ip_address.h create mode 100644 pkg/apm/webrtc/rtc_base/log_sinks.h create mode 100644 pkg/apm/webrtc/rtc_base/logging.cc create mode 100644 pkg/apm/webrtc/rtc_base/logging.h create mode 100644 pkg/apm/webrtc/rtc_base/mdns_responder_interface.h create mode 100644 pkg/apm/webrtc/rtc_base/memory/aligned_malloc.cc create mode 100644 pkg/apm/webrtc/rtc_base/memory/aligned_malloc.h create mode 100644 pkg/apm/webrtc/rtc_base/memory/always_valid_pointer.h create mode 100644 pkg/apm/webrtc/rtc_base/memory/fifo_buffer.h create mode 100644 pkg/apm/webrtc/rtc_base/memory/memory.go create mode 100644 pkg/apm/webrtc/rtc_base/memory_stream.h create mode 100644 pkg/apm/webrtc/rtc_base/memory_usage.h create mode 100644 pkg/apm/webrtc/rtc_base/message_digest.h create mode 100644 pkg/apm/webrtc/rtc_base/net_helper.h create mode 100644 pkg/apm/webrtc/rtc_base/net_helpers.h create mode 100644 pkg/apm/webrtc/rtc_base/network.h create mode 100644 pkg/apm/webrtc/rtc_base/network/ecn_marking.h create mode 100644 pkg/apm/webrtc/rtc_base/network/received_packet.h create mode 100644 pkg/apm/webrtc/rtc_base/network/sent_packet.h create mode 100644 pkg/apm/webrtc/rtc_base/network_constants.h create mode 100644 pkg/apm/webrtc/rtc_base/network_monitor.h create mode 100644 pkg/apm/webrtc/rtc_base/network_monitor_factory.h create mode 100644 pkg/apm/webrtc/rtc_base/network_route.h create mode 100644 pkg/apm/webrtc/rtc_base/null_socket_server.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/divide_round.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/event_based_exponential_moving_average.cc create mode 100644 pkg/apm/webrtc/rtc_base/numerics/event_based_exponential_moving_average.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/event_rate_counter.cc create mode 100644 pkg/apm/webrtc/rtc_base/numerics/event_rate_counter.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/exp_filter.cc create mode 100644 pkg/apm/webrtc/rtc_base/numerics/exp_filter.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/histogram_percentile_counter.cc create mode 100644 pkg/apm/webrtc/rtc_base/numerics/histogram_percentile_counter.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/math_utils.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/mod_ops.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/moving_average.cc create mode 100644 pkg/apm/webrtc/rtc_base/numerics/moving_average.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/moving_max_counter.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/moving_percentile_filter.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/numerics.go create mode 100644 pkg/apm/webrtc/rtc_base/numerics/percentile_filter.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/rational.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/running_statistics.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/safe_compare.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/safe_conversions.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/safe_conversions_impl.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/safe_minmax.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/sample_counter.cc create mode 100644 pkg/apm/webrtc/rtc_base/numerics/sample_counter.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/sample_stats.cc create mode 100644 pkg/apm/webrtc/rtc_base/numerics/sample_stats.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/sequence_number_unwrapper.h create mode 100644 pkg/apm/webrtc/rtc_base/numerics/sequence_number_util.h create mode 100644 pkg/apm/webrtc/rtc_base/one_time_event.h create mode 100644 pkg/apm/webrtc/rtc_base/openssl.h create mode 100644 pkg/apm/webrtc/rtc_base/openssl_adapter.h create mode 100644 pkg/apm/webrtc/rtc_base/openssl_certificate.h create mode 100644 pkg/apm/webrtc/rtc_base/openssl_digest.h create mode 100644 pkg/apm/webrtc/rtc_base/openssl_identity.h create mode 100644 pkg/apm/webrtc/rtc_base/openssl_key_pair.h create mode 100644 pkg/apm/webrtc/rtc_base/openssl_session_cache.h create mode 100644 pkg/apm/webrtc/rtc_base/openssl_stream_adapter.h create mode 100644 pkg/apm/webrtc/rtc_base/openssl_utility.h create mode 100644 pkg/apm/webrtc/rtc_base/operations_chain.h create mode 100644 pkg/apm/webrtc/rtc_base/physical_socket_server.h create mode 100644 pkg/apm/webrtc/rtc_base/platform_thread.cc create mode 100644 pkg/apm/webrtc/rtc_base/platform_thread.h create mode 100644 pkg/apm/webrtc/rtc_base/platform_thread_types.cc create mode 100644 pkg/apm/webrtc/rtc_base/platform_thread_types.h create mode 100644 pkg/apm/webrtc/rtc_base/protobuf_utils.h create mode 100644 pkg/apm/webrtc/rtc_base/proxy_server.h create mode 100644 pkg/apm/webrtc/rtc_base/race_checker.cc create mode 100644 pkg/apm/webrtc/rtc_base/race_checker.h create mode 100644 pkg/apm/webrtc/rtc_base/random.cc create mode 100644 pkg/apm/webrtc/rtc_base/random.h create mode 100644 pkg/apm/webrtc/rtc_base/rate_limiter.h create mode 100644 pkg/apm/webrtc/rtc_base/rate_statistics.h create mode 100644 pkg/apm/webrtc/rtc_base/rate_tracker.h create mode 100644 pkg/apm/webrtc/rtc_base/ref_count.h create mode 100644 pkg/apm/webrtc/rtc_base/ref_counted_object.h create mode 100644 pkg/apm/webrtc/rtc_base/ref_counter.h create mode 100644 pkg/apm/webrtc/rtc_base/rolling_accumulator.h create mode 100644 pkg/apm/webrtc/rtc_base/rtc_base.go create mode 100644 pkg/apm/webrtc/rtc_base/rtc_certificate.h create mode 100644 pkg/apm/webrtc/rtc_base/rtc_certificate_generator.h create mode 100644 pkg/apm/webrtc/rtc_base/sanitizer.h create mode 100644 pkg/apm/webrtc/rtc_base/server_socket_adapters.h create mode 100644 pkg/apm/webrtc/rtc_base/socket.h create mode 100644 pkg/apm/webrtc/rtc_base/socket_adapters.h create mode 100644 pkg/apm/webrtc/rtc_base/socket_address.h create mode 100644 pkg/apm/webrtc/rtc_base/socket_address_pair.h create mode 100644 pkg/apm/webrtc/rtc_base/socket_factory.h create mode 100644 pkg/apm/webrtc/rtc_base/socket_server.h create mode 100644 pkg/apm/webrtc/rtc_base/ssl_adapter.h create mode 100644 pkg/apm/webrtc/rtc_base/ssl_certificate.h create mode 100644 pkg/apm/webrtc/rtc_base/ssl_fingerprint.h create mode 100644 pkg/apm/webrtc/rtc_base/ssl_identity.h create mode 100644 pkg/apm/webrtc/rtc_base/ssl_roots.h create mode 100644 pkg/apm/webrtc/rtc_base/ssl_stream_adapter.h create mode 100644 pkg/apm/webrtc/rtc_base/stream.h create mode 100644 pkg/apm/webrtc/rtc_base/string_encode.cc create mode 100644 pkg/apm/webrtc/rtc_base/string_encode.h create mode 100644 pkg/apm/webrtc/rtc_base/string_to_number.cc create mode 100644 pkg/apm/webrtc/rtc_base/string_to_number.h create mode 100644 pkg/apm/webrtc/rtc_base/string_utils.cc create mode 100644 pkg/apm/webrtc/rtc_base/string_utils.h create mode 100644 pkg/apm/webrtc/rtc_base/strings/json.h create mode 100644 pkg/apm/webrtc/rtc_base/strings/str_join.h create mode 100644 pkg/apm/webrtc/rtc_base/strings/string_builder.cc create mode 100644 pkg/apm/webrtc/rtc_base/strings/string_builder.h create mode 100644 pkg/apm/webrtc/rtc_base/strings/string_format.cc create mode 100644 pkg/apm/webrtc/rtc_base/strings/string_format.h create mode 100644 pkg/apm/webrtc/rtc_base/strings/strings.go create mode 100644 pkg/apm/webrtc/rtc_base/strong_alias.h create mode 100644 pkg/apm/webrtc/rtc_base/swap_queue.h create mode 100644 pkg/apm/webrtc/rtc_base/synchronization/mutex.h create mode 100644 pkg/apm/webrtc/rtc_base/synchronization/mutex_abseil.h create mode 100644 pkg/apm/webrtc/rtc_base/synchronization/mutex_critical_section.h create mode 100644 pkg/apm/webrtc/rtc_base/synchronization/mutex_pthread.h create mode 100644 pkg/apm/webrtc/rtc_base/synchronization/sequence_checker_internal.cc create mode 100644 pkg/apm/webrtc/rtc_base/synchronization/sequence_checker_internal.h create mode 100644 pkg/apm/webrtc/rtc_base/synchronization/sync.go create mode 100644 pkg/apm/webrtc/rtc_base/synchronization/yield.cc create mode 100644 pkg/apm/webrtc/rtc_base/synchronization/yield.h create mode 100644 pkg/apm/webrtc/rtc_base/synchronization/yield_policy.cc create mode 100644 pkg/apm/webrtc/rtc_base/synchronization/yield_policy.h create mode 100644 pkg/apm/webrtc/rtc_base/system/arch.h create mode 100644 pkg/apm/webrtc/rtc_base/system/asm_defines.h create mode 100644 pkg/apm/webrtc/rtc_base/system/assume.h create mode 100644 pkg/apm/webrtc/rtc_base/system/cocoa_threading.h create mode 100644 pkg/apm/webrtc/rtc_base/system/file_wrapper.cc create mode 100644 pkg/apm/webrtc/rtc_base/system/file_wrapper.h create mode 100644 pkg/apm/webrtc/rtc_base/system/gcd_helpers.h create mode 100644 pkg/apm/webrtc/rtc_base/system/ignore_warnings.h create mode 100644 pkg/apm/webrtc/rtc_base/system/inline.h create mode 100644 pkg/apm/webrtc/rtc_base/system/no_cfi_icall.h create mode 100644 pkg/apm/webrtc/rtc_base/system/no_unique_address.h create mode 100644 pkg/apm/webrtc/rtc_base/system/rtc_export.h create mode 100644 pkg/apm/webrtc/rtc_base/system/rtc_export_template.h create mode 100644 pkg/apm/webrtc/rtc_base/system/system.go create mode 100644 pkg/apm/webrtc/rtc_base/system/unused.h create mode 100644 pkg/apm/webrtc/rtc_base/system/warn_current_thread_is_deadlocked.h create mode 100644 pkg/apm/webrtc/rtc_base/system_time.cc create mode 100644 pkg/apm/webrtc/rtc_base/system_time.h create mode 100644 pkg/apm/webrtc/rtc_base/task_queue_gcd.h create mode 100644 pkg/apm/webrtc/rtc_base/task_queue_stdlib.h create mode 100644 pkg/apm/webrtc/rtc_base/task_queue_win.h create mode 100644 pkg/apm/webrtc/rtc_base/task_utils/repeating_task.h create mode 100644 pkg/apm/webrtc/rtc_base/third_party/base64/base64.h create mode 100644 pkg/apm/webrtc/rtc_base/third_party/sigslot/sigslot.h create mode 100644 pkg/apm/webrtc/rtc_base/thread.h create mode 100644 pkg/apm/webrtc/rtc_base/thread_annotations.h create mode 100644 pkg/apm/webrtc/rtc_base/time_utils.cc create mode 100644 pkg/apm/webrtc/rtc_base/time_utils.h create mode 100644 pkg/apm/webrtc/rtc_base/timestamp_aligner.h create mode 100644 pkg/apm/webrtc/rtc_base/trace_categories.h create mode 100644 pkg/apm/webrtc/rtc_base/trace_event.h create mode 100644 pkg/apm/webrtc/rtc_base/type_traits.h create mode 100644 pkg/apm/webrtc/rtc_base/unique_id_generator.h create mode 100644 pkg/apm/webrtc/rtc_base/units/unit_base.h create mode 100644 pkg/apm/webrtc/rtc_base/untyped_function.h create mode 100644 pkg/apm/webrtc/rtc_base/virtual_socket_server.h create mode 100644 pkg/apm/webrtc/rtc_base/weak_ptr.h create mode 100644 pkg/apm/webrtc/rtc_base/win/create_direct3d_device.h create mode 100644 pkg/apm/webrtc/rtc_base/win/get_activation_factory.h create mode 100644 pkg/apm/webrtc/rtc_base/win/hstring.h create mode 100644 pkg/apm/webrtc/rtc_base/win/scoped_com_initializer.h create mode 100644 pkg/apm/webrtc/rtc_base/win/windows_version.h create mode 100644 pkg/apm/webrtc/rtc_base/win32.h create mode 100644 pkg/apm/webrtc/rtc_base/win32_socket_init.h create mode 100644 pkg/apm/webrtc/rtc_base/zero_memory.cc create mode 100644 pkg/apm/webrtc/rtc_base/zero_memory.h create mode 100644 pkg/apm/webrtc/system_wrappers/include/clock.h create mode 100644 pkg/apm/webrtc/system_wrappers/include/cpu_features_wrapper.h create mode 100644 pkg/apm/webrtc/system_wrappers/include/cpu_info.h create mode 100644 pkg/apm/webrtc/system_wrappers/include/denormal_disabler.h create mode 100644 pkg/apm/webrtc/system_wrappers/include/field_trial.h create mode 100644 pkg/apm/webrtc/system_wrappers/include/metrics.h create mode 100644 pkg/apm/webrtc/system_wrappers/include/ntp_time.h create mode 100644 pkg/apm/webrtc/system_wrappers/include/rtp_to_ntp_estimator.h create mode 100644 pkg/apm/webrtc/system_wrappers/include/sleep.h create mode 100644 pkg/apm/webrtc/system_wrappers/source/clock.cc create mode 100644 pkg/apm/webrtc/system_wrappers/source/cpu_features.cc create mode 100644 pkg/apm/webrtc/system_wrappers/source/cpu_features_android.cc create mode 100644 pkg/apm/webrtc/system_wrappers/source/cpu_features_linux.cc create mode 100644 pkg/apm/webrtc/system_wrappers/source/cpu_info.cc create mode 100644 pkg/apm/webrtc/system_wrappers/source/denormal_disabler.cc create mode 100644 pkg/apm/webrtc/system_wrappers/source/field_trial.cc create mode 100644 pkg/apm/webrtc/system_wrappers/source/metrics.cc create mode 100644 pkg/apm/webrtc/system_wrappers/source/rtp_to_ntp_estimator.cc create mode 100644 pkg/apm/webrtc/system_wrappers/source/sleep.cc create mode 100644 pkg/apm/webrtc/system_wrappers/source/system.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/algorithm/algorithm.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/algorithm/container.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/attributes.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/base.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/call_once.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/casts.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/config.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/const_init.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/cycleclock.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/dynamic_annotations.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/fast_type_id.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/atomic_hook.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/atomic_hook_test_helper.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/cycleclock.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/cycleclock_config.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/direct_mmap.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/endian.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/errno_saver.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/hide_ptr.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/identity.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/iterator_traits.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/iterator_traits_test_helper.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/low_level_alloc.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/low_level_scheduling.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/nullability_deprecated.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/per_thread_tls.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/poison.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/pretty_function.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/raw_logging.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/scheduling_mode.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/scoped_set_env.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/spinlock.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/spinlock_akaros.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/spinlock_linux.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/spinlock_posix.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/spinlock_wait.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/spinlock_win32.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/strerror.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/sysinfo.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/thread_identity.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/throw_delegate.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/tracing.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/tsan_mutex_interface.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/unaligned_access.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock_config.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/log_severity.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/log_severity.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/low_level_alloc.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/macros.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/no_destructor.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/nullability.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/optimization.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/options.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/poison.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/policy_checks.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/port.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/prefetch.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/raw_logging.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/scoped_set_env.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/spinlock.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/spinlock_wait.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/strerror.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/sysinfo.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/thread_annotations.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/thread_identity.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/throw_delegate.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/tracing.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/base/unscaledcycleclock.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/cleanup/cleanup.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/cleanup/internal/cleanup.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/btree_map.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/btree_set.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/container.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/fixed_array.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/flat_hash_map.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/flat_hash_set.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hash_container_defaults.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hashtablez_sampler.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hashtablez_sampler_force_weak_definition.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/inlined_vector.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/btree.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/btree_container.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/common.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/common_policy_traits.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/compressed_tuple.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/container_memory.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hash_policy_traits.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_control_bytes.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_debug.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_debug_hooks.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/inlined_vector.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/layout.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/node_slot_policy.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_map.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_set_resize_impl.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/test_allocator.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/test_instance_tracker.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/tracked.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/node_hash_map.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/node_hash_set.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/container/raw_hash_set.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/cpu_detect.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/crc.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/crc.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/crc32c.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/crc32c.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/crc_cord_state.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/crc_memcpy_fallback.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/crc_memcpy_x86_arm_combined.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/crc_non_temporal_memcpy.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/crc_x86_arm_combined.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/internal/cpu_detect.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/internal/crc.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/internal/crc32_x86_arm_combined_simd.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/internal/crc32c.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/internal/crc32c_inline.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/internal/crc_cord_state.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/internal/crc_internal.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/internal/crc_memcpy.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/internal/non_temporal_arm_intrinsics.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/crc/internal/non_temporal_memcpy.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/address_is_readable.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/debugging.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/decode_rust_punycode.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/demangle.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/demangle_rust.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/elf_mem_image.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/examine_stack.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/failure_signal_handler.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/address_is_readable.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/addresses.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/bounded_utf8_length_sequence.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/decode_rust_punycode.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/demangle.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/demangle_rust.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/examine_stack.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/stack_consumption.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/stacktrace_arm-inl.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/stacktrace_config.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/stacktrace_emscripten-inl.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/stacktrace_generic-inl.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/stacktrace_riscv-inl.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/stacktrace_unimplemented-inl.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/stacktrace_win32-inl.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/stacktrace_x86-inl.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/symbolize.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/utf8_for_code_point.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/internal/vdso_support.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/leak_check.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/leak_check.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/stack_consumption.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/stacktrace.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/stacktrace.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/symbolize.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/symbolize.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/symbolize_darwin.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/symbolize_elf.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/symbolize_emscripten.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/symbolize_unimplemented.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/symbolize_win32.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/utf8_for_code_point.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/debugging/vdso_support.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/commandlineflag.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/commandlineflag.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/config.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/declare.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/flag.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/flag.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/flags.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/internal/commandlineflag.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/internal/flag.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/internal/parse.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/internal/path_util.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/internal/private_handle_accessor.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/internal/program_name.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/internal/registry.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/internal/sequence_lock.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/internal/usage.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/internal_commandlineflag.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/internal_usage.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/marshalling.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/marshalling.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/parse.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/parse.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/private_handle_accessor.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/program_name.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/reflection.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/reflection.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/usage.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/usage.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/usage_config.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/flags/usage_config.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/functional/any_invocable.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/functional/bind_front.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/functional/function_ref.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/functional/internal/any_invocable.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/functional/internal/front_binder.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/functional/internal/function_ref.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/functional/overload.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/hash/city.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/hash/hash.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/hash/hash.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/hash/hash.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/hash/internal/city.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/hash/internal/hash.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/hash/internal/low_level_hash.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/hash/internal/spy_hash_state.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/hash/internal/weakly_mixed_integer.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/hash/low_level_hash.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/absl_check.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/absl_log.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/absl_vlog_is_on.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/check.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/check_op.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/conditions.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/die_if_null.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/die_if_null.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/flags.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/flags.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/fnmatch.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/globals.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/globals.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/initialize.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/initialize.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/append_truncated.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/check_impl.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/check_op.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/conditions.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/config.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/flags.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/fnmatch.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/globals.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/log_format.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/log_impl.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/log_message.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/log_sink_set.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/nullguard.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/nullstream.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/proto.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/strip.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/structured.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/structured_proto.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/test_actions.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/test_helpers.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/test_matchers.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/vlog_config.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal/voidify.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/internal_globals.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/log.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/log.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/log_entry.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/log_format.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/log_message.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/log_sink.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/log_sink.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/log_sink_registry.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/log_sink_set.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/log_streamer.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/nullguard.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/proto.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/structured.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/structured_proto.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/vlog_config.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/log/vlog_is_on.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/memory/memory.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/meta/type_traits.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/numeric/bits.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/numeric/int128.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/numeric/int128.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/numeric/int128_have_intrinsic.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/numeric/int128_no_intrinsic.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/numeric/internal/bits.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/numeric/internal/representation.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/numeric/numeric.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/profiling/exponential_biased.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/profiling/internal/exponential_biased.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/profiling/internal/periodic_sampler.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/profiling/internal/sample_recorder.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/profiling/periodic_sampler.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/profiling/profiling.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/bernoulli_distribution.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/beta_distribution.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/bit_gen_ref.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/discrete_distribution.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/discrete_distribution.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/distributions.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/entropy_pool.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/exponential_distribution.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/gaussian_distribution.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/gaussian_distribution.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/chi_square.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/distribution_caller.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/distribution_test_util.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/entropy_pool.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/explicit_seed_seq.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/fast_uniform_bits.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/fastmath.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/generate_real.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/iostream_state_saver.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/nonsecure_base.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/pcg_engine.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/platform.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/randen.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/randen_detect.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/randen_engine.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/randen_hwaes.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/randen_slow.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/randen_traits.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/salted_seed_seq.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/seed_material.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/sequence_urbg.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/traits.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/uniform_helper.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/internal/wide_multiply.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/log_uniform_int_distribution.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/mocking_bit_gen.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/poisson_distribution.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/randen.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/randen_detect.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/randen_hwaes.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/randen_round_keys.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/randen_slow.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/random.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/random.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/seed_gen_exception.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/seed_gen_exception.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/seed_material.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/seed_sequences.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/seed_sequences.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/uniform_int_distribution.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/uniform_real_distribution.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/random/zipf_distribution.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/status/internal/status_internal.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/status/internal/statusor_internal.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/status/status.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/status/status.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/status/status.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/status/status_internal.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/status/status_payload_printer.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/status/status_payload_printer.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/status/statusor.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/status/statusor.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/ascii.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/ascii.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/charconv.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/charconv.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/charconv_bigint.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/charconv_parse.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/charset.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord_analysis.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord_analysis.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord_buffer.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord_internal.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord_rep_btree.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord_rep_btree_navigator.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord_rep_btree_reader.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord_rep_consume.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord_rep_crc.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cord_test_helpers.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cordz_functions.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cordz_handle.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cordz_info.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cordz_sample_token.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/damerau_levenshtein_distance.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/escaping.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/escaping.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/has_absl_stringify.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/has_ostream_operator.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/charconv_bigint.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/charconv_parse.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cord_data_edge.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cord_internal.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cord_rep_crc.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cord_rep_flat.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cord_rep_test_util.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cordz_functions.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cordz_handle.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cordz_info.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cordz_statistics.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cordz_update_scope.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/damerau_levenshtein_distance.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/escaping.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/escaping_test_common.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/memutil.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/numbers_test_common.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/ostringstream.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/pow10_helper.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/resize_uninitialized.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/stl_type_traits.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/str_format/bind.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/str_format/checker.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/str_format/constexpr_parser.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/str_format/extension.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/str_format/output.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/str_format/parser.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/str_join_internal.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/str_split_internal.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/string_constant.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/stringify_sink.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal/utf8.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/internal_escaping.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/match.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/match.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/memutil.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/numbers.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/numbers.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/ostringstream.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/pow10_helper.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_cat.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_cat.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_format.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_format_arg.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_format_bind.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_format_extension.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_format_float_conversion.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_format_output.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_format_parser.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_join.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_replace.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_replace.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_split.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/str_split.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/string_view.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/string_view.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/stringify_sink.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/strings.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/strip.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/substitute.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/substitute.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/strings/utf8.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/barrier.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/barrier.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/blocking_counter.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/blocking_counter.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/create_thread_identity.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/futex_waiter.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/graphcycles.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/create_thread_identity.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/futex.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/futex_waiter.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/graphcycles.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/kernel_timeout.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/pthread_waiter.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/sem_waiter.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/stdcpp_waiter.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/thread_pool.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/waiter.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/waiter_base.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/internal/win32_waiter.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/kernel_timeout.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/mutex.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/mutex.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/notification.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/notification.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/per_thread_sem.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/pthread_waiter.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/sem_waiter.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/stdcpp_waiter.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/sync.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/waiter_base.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/synchronization/win32_waiter.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/civil_time.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/clock.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/civil_time.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/time_zone.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_posix.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/get_current_time_chrono.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/get_current_time_posix.inc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/internal/test_util.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/civil_time.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/civil_time_detail.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/clock.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/duration.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/format.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/src.go create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/time.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/time_zone_fixed.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/time_zone_format.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/time_zone_if.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/time_zone_impl.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/time_zone_info.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/time_zone_libc.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/time_zone_lookup.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/time_zone_posix.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/src/zone_info_source.cc create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/time/time.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/types/any.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/types/compare.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/types/internal/span.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/types/optional.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/types/span.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/types/variant.h create mode 100644 pkg/apm/webrtc/third_party/abseil-cpp/absl/utility/utility.h create mode 100644 pkg/apm/webrtc/third_party/jsoncpp/source/include/json/json.h create mode 100644 pkg/apm/webrtc/third_party/pffft/src/pffft.c create mode 100644 pkg/apm/webrtc/third_party/pffft/src/pffft.go create mode 100644 pkg/apm/webrtc/third_party/pffft/src/pffft.h create mode 100644 pkg/apm/webrtc/third_party/rnnoise/src/rnn_activations.h create mode 100644 pkg/apm/webrtc/third_party/rnnoise/src/rnn_vad_weights.cc create mode 100644 pkg/apm/webrtc/third_party/rnnoise/src/rnn_vad_weights.h create mode 100644 pkg/apm/webrtc/third_party/rnnoise/src/src.go create mode 100644 pkg/apm/webrtc/webrtc.go create mode 100644 pkg/apm/webrtc/webrtc_darwin.go create mode 100644 pkg/apm/webrtc/webrtc_linux.go create mode 100644 pkg/apm/webrtc/webrtc_windows.go create mode 100644 pkg/console/pipeline.go create mode 100644 pkg/console/ringbuffer.go create mode 100644 pkg/console/tcp.go create mode 160000 pkg/portaudio/pa_src create mode 100644 pkg/portaudio/portaudio.go create mode 100644 pkg/portaudio/portaudio_darwin.go create mode 100644 pkg/portaudio/portaudio_linux.go create mode 100644 pkg/portaudio/portaudio_windows.go diff --git a/.gitattributes b/.gitattributes index 7f09569d..14a2225a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,11 @@ pkg/provider/resources/*.h264 filter=lfs diff=lfs merge=lfs -text pkg/provider/resources/*.ivf filter=lfs diff=lfs merge=lfs -text pkg/provider/resources/*.ogg filter=lfs diff=lfs merge=lfs -text + +# Vendored C/C++ source (WebRTC APM, PortAudio, abseil, pffft, rnnoise) +pkg/apm/webrtc/**/*.cc linguist-vendored +pkg/apm/webrtc/**/*.c linguist-vendored +pkg/apm/webrtc/**/*.h linguist-vendored +pkg/apm/webrtc/**/*.m linguist-vendored +pkg/apm/webrtc/**/*.inc linguist-vendored +pkg/portaudio/pa_src/** linguist-vendored diff --git a/.github/workflows/build-binaries.yaml b/.github/workflows/build-binaries.yaml new file mode 100644 index 00000000..acefc9a6 --- /dev/null +++ b/.github/workflows/build-binaries.yaml @@ -0,0 +1,170 @@ +# Copyright 2023 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Build binaries + +on: + workflow_call: + inputs: + output_dir: + type: string + default: bin + upload_release: + type: boolean + default: false + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + suffix: darwin_arm64 + - os: ubuntu-latest + suffix: linux_amd64 + zig_target: x86_64-linux-gnu.2.28 + alsa_arch: amd64 + alsa_triple: x86_64-linux-gnu + - os: ubuntu-latest + suffix: linux_arm64 + zig_target: aarch64-linux-gnu.2.28 + alsa_arch: arm64 + alsa_triple: aarch64-linux-gnu + goarch: arm64 + - os: ubuntu-latest + suffix: linux_arm + zig_target: arm-linux-gnueabihf.2.28 + alsa_arch: armhf + alsa_triple: arm-linux-gnueabihf + goarch: arm + goarm: "7" + - os: ubuntu-latest + suffix: windows_amd64 + zig_target: x86_64-windows-gnu + goos: windows + goarch: amd64 + - os: ubuntu-latest + suffix: windows_arm64 + zig_target: aarch64-windows-gnu + goos: windows + goarch: arm64 + - os: ubuntu-latest + suffix: windows_arm + goos: windows + goarch: arm + goarm: "7" + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v6 + with: + lfs: 'true' + submodules: true + + - run: git lfs pull + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: "1.25" + + - name: Install Zig + if: matrix.zig_target + uses: mlugg/setup-zig@v2 + with: + version: 0.14.1 + + - name: Install ALSA headers + if: matrix.alsa_arch + run: | + sudo dpkg --add-architecture ${{ matrix.alsa_arch }} + if [ "${{ matrix.alsa_arch }}" != "amd64" ]; then + CODENAME=$(lsb_release -cs) + # Restrict existing sources to amd64 to avoid 404s for foreign arch + for f in /etc/apt/sources.list.d/*.sources; do + grep -q '^Architectures:' "$f" || sudo sed -i '/^Types:/a Architectures: amd64 i386' "$f" + done + # Add ports.ubuntu.com for the foreign architecture + printf 'Types: deb\nURIs: http://ports.ubuntu.com/ubuntu-ports\nSuites: %s %s-updates\nComponents: main universe\nArchitectures: %s\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\n' \ + "$CODENAME" "$CODENAME" "${{ matrix.alsa_arch }}" | sudo tee /etc/apt/sources.list.d/ports.sources + fi + sudo apt-get update + sudo apt-get install -y libasound2-dev:${{ matrix.alsa_arch }} + + - name: Generate Windows import libraries + if: matrix.goos == 'windows' && matrix.zig_target + run: | + ZIG_LIB=$(zig env | jq -r '.lib_dir') + echo "ZIG_LIB=${ZIG_LIB}" >> "$GITHUB_ENV" + LIB_DIR="${ZIG_LIB}/libc/mingw/lib-common" + # Zig bundles MinGW .def files but lld needs .a import libraries. + # Go's compiled objects embed COFF /DEFAULTLIB directives (e.g. dbghelp, + # bcrypt) that lld resolves directly, bypassing Zig's lazy .def→.a + # generation. Pre-generate all import libraries so lld can find them. + MACHINE=${{ matrix.goarch == 'amd64' && 'i386:x86-64' || 'arm64' }} + for def in "${LIB_DIR}"/*.def; do + lib=$(basename "$def" .def) + [ -f "${LIB_DIR}/lib${lib}.a" ] && continue + zig dlltool -d "$def" -l "${LIB_DIR}/lib${lib}.a" -m "$MACHINE" 2>/dev/null || true + done + + - name: Build + env: + CGO_ENABLED: ${{ (matrix.goos && !matrix.zig_target) && '0' || '1' }} + CC: ${{ matrix.zig_target && format('zig cc -target {0}', matrix.zig_target) || '' }} + CXX: ${{ matrix.zig_target && format('zig c++ -target {0}', matrix.zig_target) || '' }} + # Zig uses its own sysroot; point it at the system ALSA headers and libraries + CGO_CFLAGS: ${{ matrix.alsa_triple && format('-isystem /usr/include -isystem /usr/include/{0}', matrix.alsa_triple) || '' }} + CGO_LDFLAGS: ${{ matrix.alsa_triple && format('-L/usr/lib/{0}', matrix.alsa_triple) || '' }} + # -fms-extensions: enable __try/__except (SEH) used by WebRTC + # -DNTDDI_VERSION: target Windows 10 base to skip WinRT includes absent from MinGW + CGO_CXXFLAGS: ${{ matrix.goos == 'windows' && '-fms-extensions -DNTDDI_VERSION=0x0A000000' || '' }} + GOOS: ${{ matrix.goos || '' }} + GOARCH: ${{ matrix.goarch || '' }} + GOARM: ${{ matrix.goarm || '' }} + shell: bash + run: | + EXT=""; if [ "${GOOS:-}" = "windows" ]; then EXT=".exe"; fi + TAGS="" + if [ "$CGO_ENABLED" = "1" ]; then TAGS="-tags console"; fi + # Force external linking for Windows so Go uses zig cc (CC) as the linker, + # and add Zig's MinGW lib path so lld can find the generated import libraries. + EXTLD="" + if [ "${GOOS:-}" = "windows" ] && [ "$CGO_ENABLED" = "1" ]; then + EXTLD="-linkmode=external -extldflags '-L${ZIG_LIB}/libc/mingw/lib-common'" + fi + go build $TAGS -ldflags "-w -s $EXTLD" -o "${{ inputs.output_dir }}/lk${EXT}" ./cmd/lk + + - name: Verify binary + if: "!matrix.goos && !matrix.goarch" + run: ${{ inputs.output_dir }}/lk --help > /dev/null + + - name: Package and upload + if: inputs.upload_release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + TAG="${GITHUB_REF#refs/tags/}" + VERSION="${TAG#v}" + NAME="lk_${VERSION}_${{ matrix.suffix }}" + cp LICENSE ${{ inputs.output_dir }}/ + cp -r autocomplete ${{ inputs.output_dir }}/ + if [[ "${{ matrix.suffix }}" == windows_* ]]; then + cd ${{ inputs.output_dir }} && zip -r "../${NAME}.zip" lk.exe LICENSE autocomplete && cd .. + gh release upload "$TAG" "${NAME}.zip" --clobber + else + tar -czf "${NAME}.tar.gz" -C ${{ inputs.output_dir }} lk LICENSE autocomplete + gh release upload "$TAG" "${NAME}.tar.gz" --clobber + fi diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b87a6552..99b8baae 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -20,32 +20,149 @@ on: pull_request: branches: [ main ] +permissions: + contents: read + jobs: - build: + lint-and-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - uses: actions/cache@v5 with: - path: | - ~/go/pkg/mod - ~/go/bin - ~/.cache - key: livekit-cli + submodules: true - name: Set up Go uses: actions/setup-go@v6 with: go-version: "1.25" - - name: Download Go modules - run: go mod download - - name: Static Check uses: dominikh/staticcheck-action@v1.4.0 with: version: "latest" install-go: false - - name: Run Go tests + - name: Test run: go test -v ./... + + build: + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + suffix: darwin_arm64 + - os: ubuntu-latest + suffix: linux_amd64 + zig_target: x86_64-linux-gnu.2.28 + alsa_arch: amd64 + alsa_triple: x86_64-linux-gnu + - os: ubuntu-latest + suffix: linux_arm64 + zig_target: aarch64-linux-gnu.2.28 + alsa_arch: arm64 + alsa_triple: aarch64-linux-gnu + goarch: arm64 + - os: ubuntu-latest + suffix: linux_arm + zig_target: arm-linux-gnueabihf.2.28 + alsa_arch: armhf + alsa_triple: arm-linux-gnueabihf + goarch: arm + goarm: "7" + - os: ubuntu-latest + suffix: windows_amd64 + zig_target: x86_64-windows-gnu + goos: windows + goarch: amd64 + - os: ubuntu-latest + suffix: windows_arm64 + zig_target: aarch64-windows-gnu + goos: windows + goarch: arm64 + - os: ubuntu-latest + suffix: windows_arm + goos: windows + goarch: arm + goarm: "7" + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v6 + with: + submodules: true + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: "1.25" + + - name: Install Zig + if: matrix.zig_target + uses: mlugg/setup-zig@v2 + with: + version: 0.14.1 + + - name: Install ALSA headers + if: matrix.alsa_arch + run: | + sudo dpkg --add-architecture ${{ matrix.alsa_arch }} + if [ "${{ matrix.alsa_arch }}" != "amd64" ]; then + CODENAME=$(lsb_release -cs) + # Restrict existing sources to amd64 to avoid 404s for foreign arch + for f in /etc/apt/sources.list.d/*.sources; do + grep -q '^Architectures:' "$f" || sudo sed -i '/^Types:/a Architectures: amd64 i386' "$f" + done + # Add ports.ubuntu.com for the foreign architecture + printf 'Types: deb\nURIs: http://ports.ubuntu.com/ubuntu-ports\nSuites: %s %s-updates\nComponents: main universe\nArchitectures: %s\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\n' \ + "$CODENAME" "$CODENAME" "${{ matrix.alsa_arch }}" | sudo tee /etc/apt/sources.list.d/ports.sources + fi + sudo apt-get update + sudo apt-get install -y libasound2-dev:${{ matrix.alsa_arch }} + + - name: Generate Windows import libraries + if: matrix.goos == 'windows' && matrix.zig_target + run: | + ZIG_LIB=$(zig env | jq -r '.lib_dir') + echo "ZIG_LIB=${ZIG_LIB}" >> "$GITHUB_ENV" + LIB_DIR="${ZIG_LIB}/libc/mingw/lib-common" + # Zig bundles MinGW .def files but lld needs .a import libraries. + # Go's compiled objects embed COFF /DEFAULTLIB directives (e.g. dbghelp, + # bcrypt) that lld resolves directly, bypassing Zig's lazy .def→.a + # generation. Pre-generate all import libraries so lld can find them. + MACHINE=${{ matrix.goarch == 'amd64' && 'i386:x86-64' || 'arm64' }} + for def in "${LIB_DIR}"/*.def; do + lib=$(basename "$def" .def) + [ -f "${LIB_DIR}/lib${lib}.a" ] && continue + zig dlltool -d "$def" -l "${LIB_DIR}/lib${lib}.a" -m "$MACHINE" 2>/dev/null || true + done + + - name: Build + env: + CGO_ENABLED: ${{ (matrix.goos && !matrix.zig_target) && '0' || '1' }} + CC: ${{ matrix.zig_target && format('zig cc -target {0}', matrix.zig_target) || '' }} + CXX: ${{ matrix.zig_target && format('zig c++ -target {0}', matrix.zig_target) || '' }} + # Zig uses its own sysroot; point it at the system ALSA headers and libraries + CGO_CFLAGS: ${{ matrix.alsa_triple && format('-isystem /usr/include -isystem /usr/include/{0}', matrix.alsa_triple) || '' }} + CGO_LDFLAGS: ${{ matrix.alsa_triple && format('-L/usr/lib/{0}', matrix.alsa_triple) || '' }} + # -fms-extensions: enable __try/__except (SEH) used by WebRTC + # -DNTDDI_VERSION: target Windows 10 base to skip WinRT includes absent from MinGW + CGO_CXXFLAGS: ${{ matrix.goos == 'windows' && '-fms-extensions -DNTDDI_VERSION=0x0A000000' || '' }} + GOOS: ${{ matrix.goos || '' }} + GOARCH: ${{ matrix.goarch || '' }} + GOARM: ${{ matrix.goarm || '' }} + shell: bash + run: | + EXT=""; if [ "${GOOS:-}" = "windows" ]; then EXT=".exe"; fi + TAGS="" + if [ "$CGO_ENABLED" = "1" ]; then TAGS="-tags console"; fi + # Force external linking for Windows so Go uses zig cc (CC) as the linker, + # and add Zig's MinGW lib path so lld can find the generated import libraries. + EXTLD="" + if [ "${GOOS:-}" = "windows" ] && [ "$CGO_ENABLED" = "1" ]; then + EXTLD="-linkmode=external -extldflags '-L${ZIG_LIB}/libc/mingw/lib-common'" + fi + go build $TAGS -ldflags "-w -s $EXTLD" -o "bin/lk${EXT}" ./cmd/lk + + - name: Verify binary + if: "!matrix.goos && !matrix.goarch" + run: bin/lk --help > /dev/null diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 723730bc..e400ba80 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -14,7 +14,6 @@ name: Release -# on events on: push: tags: @@ -23,17 +22,14 @@ on: permissions: contents: write -# workflow tasks jobs: - release: - name: Release + create-release: runs-on: ubuntu-latest steps: - - name: Checkout Git LFS - uses: actions/checkout@v6 + - uses: actions/checkout@v6 with: lfs: 'true' - + - name: Verify version.go matches tag run: | TAG_VERSION=${GITHUB_REF#refs/tags/v} @@ -47,7 +43,7 @@ jobs: exit 1 fi echo "✓ Versions match: $CODE_VERSION" - + - name: Verify server-sdk-go version run: | livekit_cli_ver=${GITHUB_REF#refs/tags/} @@ -65,29 +61,202 @@ jobs: fi echo "versions match ($livekit_cli_major_minor)" - - run: git lfs pull - - - name: Fetch all tags - run: git fetch --force --tags + - name: Create release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="${GITHUB_REF#refs/tags/}" + gh release create "$TAG" --draft --generate-notes --title "$TAG" - - uses: actions/cache@v5 + build: + needs: create-release + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + suffix: darwin_arm64 + - os: ubuntu-latest + suffix: linux_amd64 + zig_target: x86_64-linux-gnu.2.28 + alsa_arch: amd64 + alsa_triple: x86_64-linux-gnu + - os: ubuntu-latest + suffix: linux_arm64 + zig_target: aarch64-linux-gnu.2.28 + alsa_arch: arm64 + alsa_triple: aarch64-linux-gnu + goarch: arm64 + - os: ubuntu-latest + suffix: linux_arm + zig_target: arm-linux-gnueabihf.2.28 + alsa_arch: armhf + alsa_triple: arm-linux-gnueabihf + goarch: arm + goarm: "7" + - os: ubuntu-latest + suffix: windows_amd64 + zig_target: x86_64-windows-gnu + goos: windows + goarch: amd64 + - os: ubuntu-latest + suffix: windows_arm64 + zig_target: aarch64-windows-gnu + goos: windows + goarch: arm64 + - os: ubuntu-latest + suffix: windows_arm + goos: windows + goarch: arm + goarm: "7" + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v6 with: - path: | - ~/go/pkg/mod - ~/go/bin - ~/.cache - key: livekit-cli + lfs: 'true' + submodules: true + + - run: git lfs pull - name: Set up Go uses: actions/setup-go@v6 with: go-version: "1.25" - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v6 + - name: Install Zig + if: matrix.zig_target + uses: mlugg/setup-zig@v2 with: - distribution: goreleaser - version: latest - args: release --clean + version: 0.14.1 + + - name: Install ALSA headers + if: matrix.alsa_arch + run: | + sudo dpkg --add-architecture ${{ matrix.alsa_arch }} + if [ "${{ matrix.alsa_arch }}" != "amd64" ]; then + CODENAME=$(lsb_release -cs) + # Restrict existing sources to amd64 to avoid 404s for foreign arch + for f in /etc/apt/sources.list.d/*.sources; do + grep -q '^Architectures:' "$f" || sudo sed -i '/^Types:/a Architectures: amd64 i386' "$f" + done + # Add ports.ubuntu.com for the foreign architecture + printf 'Types: deb\nURIs: http://ports.ubuntu.com/ubuntu-ports\nSuites: %s %s-updates\nComponents: main universe\nArchitectures: %s\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\n' \ + "$CODENAME" "$CODENAME" "${{ matrix.alsa_arch }}" | sudo tee /etc/apt/sources.list.d/ports.sources + fi + sudo apt-get update + sudo apt-get install -y libasound2-dev:${{ matrix.alsa_arch }} + + - name: Generate Windows import libraries + if: matrix.goos == 'windows' && matrix.zig_target + run: | + ZIG_LIB=$(zig env | jq -r '.lib_dir') + echo "ZIG_LIB=${ZIG_LIB}" >> "$GITHUB_ENV" + LIB_DIR="${ZIG_LIB}/libc/mingw/lib-common" + # Zig bundles MinGW .def files but lld needs .a import libraries. + # Go's compiled objects embed COFF /DEFAULTLIB directives (e.g. dbghelp, + # bcrypt) that lld resolves directly, bypassing Zig's lazy .def→.a + # generation. Pre-generate all import libraries so lld can find them. + MACHINE=${{ matrix.goarch == 'amd64' && 'i386:x86-64' || 'arm64' }} + for def in "${LIB_DIR}"/*.def; do + lib=$(basename "$def" .def) + [ -f "${LIB_DIR}/lib${lib}.a" ] && continue + zig dlltool -d "$def" -l "${LIB_DIR}/lib${lib}.a" -m "$MACHINE" 2>/dev/null || true + done + + - name: Build + env: + CGO_ENABLED: ${{ (matrix.goos && !matrix.zig_target) && '0' || '1' }} + CC: ${{ matrix.zig_target && format('zig cc -target {0}', matrix.zig_target) || '' }} + CXX: ${{ matrix.zig_target && format('zig c++ -target {0}', matrix.zig_target) || '' }} + # Zig uses its own sysroot; point it at the system ALSA headers and libraries + CGO_CFLAGS: ${{ matrix.alsa_triple && format('-isystem /usr/include -isystem /usr/include/{0}', matrix.alsa_triple) || '' }} + CGO_LDFLAGS: ${{ matrix.alsa_triple && format('-L/usr/lib/{0}', matrix.alsa_triple) || '' }} + # -fms-extensions: enable __try/__except (SEH) used by WebRTC + # -DNTDDI_VERSION: target Windows 10 base to skip WinRT includes absent from MinGW + CGO_CXXFLAGS: ${{ matrix.goos == 'windows' && '-fms-extensions -DNTDDI_VERSION=0x0A000000' || '' }} + GOOS: ${{ matrix.goos || '' }} + GOARCH: ${{ matrix.goarch || '' }} + GOARM: ${{ matrix.goarm || '' }} + shell: bash + run: | + EXT=""; if [ "${GOOS:-}" = "windows" ]; then EXT=".exe"; fi + TAGS="" + if [ "$CGO_ENABLED" = "1" ]; then TAGS="-tags console"; fi + # Force external linking for Windows so Go uses zig cc (CC) as the linker, + # and add Zig's MinGW lib path so lld can find the generated import libraries. + EXTLD="" + if [ "${GOOS:-}" = "windows" ] && [ "$CGO_ENABLED" = "1" ]; then + EXTLD="-linkmode=external -extldflags '-L${ZIG_LIB}/libc/mingw/lib-common'" + fi + go build $TAGS -ldflags "-w -s $EXTLD" -o "dist/lk${EXT}" ./cmd/lk + + - name: Package and upload + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + TAG="${GITHUB_REF#refs/tags/}" + VERSION="${TAG#v}" + NAME="lk_${VERSION}_${{ matrix.suffix }}" + cp LICENSE dist/ + cp -r autocomplete dist/ + if [[ "${{ matrix.suffix }}" == windows_* ]]; then + cd dist && zip -r "../${NAME}.zip" lk.exe LICENSE autocomplete && cd .. + gh release upload "$TAG" "${NAME}.zip" --clobber + else + tar -czf "${NAME}.tar.gz" -C dist lk LICENSE autocomplete + gh release upload "$TAG" "${NAME}.tar.gz" --clobber + fi + + checksums: + needs: build + runs-on: ubuntu-latest + steps: + - name: Generate checksums + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="${GITHUB_REF#refs/tags/}" + gh release download "$TAG" -R livekit/livekit-cli -D artifacts + cd artifacts + sha256sum * > ../checksums.txt + cd .. + gh release upload "$TAG" checksums.txt --clobber -R livekit/livekit-cli + + update-homebrew: + needs: checksums + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Update Homebrew formula env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + TAG="${GITHUB_REF#refs/tags/}" + VERSION="${TAG#v}" + + # Download checksums + gh release download "$TAG" -R livekit/livekit-cli -p checksums.txt -D . + get_sha() { grep "$1" checksums.txt | awk '{print $1}'; } + + # Fill in the formula template + sed \ + -e "s/VERSION/${VERSION}/g" \ + -e "s/SHA256_DARWIN_ARM64/$(get_sha darwin_arm64)/g" \ + -e "s/SHA256_LINUX_AMD64/$(get_sha linux_amd64)/g" \ + -e "s/SHA256_LINUX_ARM64/$(get_sha linux_arm64)/g" \ + -e "s/SHA256_LINUX_ARM/$(get_sha linux_arm.tar)/g" \ + Formula/lk.rb > lk.rb + + # Push to homebrew tap + git clone "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/livekit/homebrew-livekit.git" tap + mkdir -p tap/Formula + cp lk.rb tap/Formula/lk.rb + cd tap + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Formula/lk.rb + git commit -m "lk ${VERSION}" + git push diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 58485dc4..db3106cd 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -20,6 +20,8 @@ jobs: steps: - uses: actions/checkout@v6 + with: + submodules: true - name: Set up Go uses: actions/setup-go@v6 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..9f72418a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "pkg/portaudio/pa_src"] + path = pkg/portaudio/pa_src + url = https://github.com/PortAudio/portaudio.git diff --git a/.goreleaser.yaml b/.goreleaser.yaml deleted file mode 100644 index cccda6f1..00000000 --- a/.goreleaser.yaml +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2023 LiveKit, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -before: - hooks: - - go mod tidy -builds: - - id: lk - env: - - CGO_ENABLED=0 - main: ./cmd/lk - binary: lk - goarm: - - "7" - goarch: - - amd64 - - arm64 - - arm - goos: - - linux - - windows - ldflags: - - -s -w -archives: - - name_template: "lk_{{ .Version }}_{{ .Os }}_{{ .Arch }}" - format_overrides: - - goos: windows - format: zip - files: - - LICENSE - - 'autocomplete/*' -release: - github: - owner: livekit - name: livekit-cli - draft: true - prerelease: auto -changelog: - sort: asc - filters: - exclude: - - '^docs:' - - '^test:' -gomod: - proxy: false -checksum: - name_template: 'checksums.txt' -snapshot: - name_template: "{{ incpatch .Version }}-next" diff --git a/Formula/lk.rb b/Formula/lk.rb new file mode 100644 index 00000000..841854fe --- /dev/null +++ b/Formula/lk.rb @@ -0,0 +1,47 @@ +# typed: false +# frozen_string_literal: true + +# This formula is meant for a custom Homebrew tap (e.g. livekit/homebrew-livekit). +# It installs a prebuilt binary with console support (PortAudio + WebRTC AEC). +# Usage: brew install livekit/livekit/lk +class Lk < Formula + desc "Command-line interface to LiveKit (with console support)" + homepage "https://livekit.io" + license "Apache-2.0" + version "VERSION" + + on_macos do + if Hardware::CPU.arm? + url "https://github.com/livekit/livekit-cli/releases/download/vVERSION/lk_VERSION_darwin_arm64.tar.gz" + sha256 "SHA256_DARWIN_ARM64" + end + end + + on_linux do + if Hardware::CPU.arm? && Hardware::CPU.is_64_bit? + url "https://github.com/livekit/livekit-cli/releases/download/vVERSION/lk_VERSION_linux_arm64.tar.gz" + sha256 "SHA256_LINUX_ARM64" + elsif Hardware::CPU.arm? + url "https://github.com/livekit/livekit-cli/releases/download/vVERSION/lk_VERSION_linux_arm.tar.gz" + sha256 "SHA256_LINUX_ARM" + else + url "https://github.com/livekit/livekit-cli/releases/download/vVERSION/lk_VERSION_linux_amd64.tar.gz" + sha256 "SHA256_LINUX_AMD64" + end + end + + def install + bin.install "lk" + bin.install_symlink "lk" => "livekit-cli" + + bash_completion.install "autocomplete/bash_autocomplete" => "lk" + fish_completion.install "autocomplete/fish_autocomplete" => "lk.fish" + zsh_completion.install "autocomplete/zsh_autocomplete" => "_lk" + end + + test do + output = shell_output("#{bin}/lk token create --list --api-key key --api-secret secret") + assert_match "valid for (mins): 5", output + assert_match "lk version #{version}", shell_output("#{bin}/lk --version") + end +end diff --git a/Makefile b/Makefile index 0b08d5aa..573fea25 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,9 @@ cli: check_lfs GOOS=windows GOARCH=amd64 go build -ldflags "-w -s" -o bin/lk.exe ./cmd/lk +console: + CGO_ENABLED=1 go build -tags console -ldflags "-w -s" -o bin/lk ./cmd/lk + install: cli ifeq ($(DETECTED_OS),Windows) cp bin/lk.exe $(GOBIN)/lk.exe diff --git a/cmd/lk/console.go b/cmd/lk/console.go new file mode 100644 index 00000000..2dab4130 --- /dev/null +++ b/cmd/lk/console.go @@ -0,0 +1,186 @@ +//go:build console + +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "fmt" + "os" + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/urfave/cli/v3" + + "github.com/livekit/livekit-cli/v2/pkg/console" + "github.com/livekit/livekit-cli/v2/pkg/portaudio" +) + +var ConsoleCommands = []*cli.Command{ + { + Name: "console", + Usage: "Voice chat with an agent via mic/speakers", + Category: "Core", + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "port", + Aliases: []string{"p"}, + Usage: "TCP port for agent communication", + Value: 0, + }, + &cli.StringFlag{ + Name: "input-device", + Usage: "Input device index or name substring", + }, + &cli.StringFlag{ + Name: "output-device", + Usage: "Output device index or name substring", + }, + &cli.BoolFlag{ + Name: "list-devices", + Usage: "List available audio devices and exit", + }, + &cli.BoolFlag{ + Name: "no-aec", + Usage: "Disable acoustic echo cancellation", + }, + }, + Action: runConsole, + }, +} + +func runConsole(ctx context.Context, cmd *cli.Command) error { + if err := portaudio.Initialize(); err != nil { + return fmt.Errorf("failed to initialize PortAudio: %w", err) + } + defer portaudio.Terminate() + + if cmd.Bool("list-devices") { + return listDevices() + } + + var inputDev *portaudio.DeviceInfo + var err error + if q := cmd.String("input-device"); q != "" { + inputDev, err = portaudio.FindDevice(q, true) + } else { + inputDev, err = portaudio.DefaultInputDevice() + } + if err != nil { + return fmt.Errorf("input device: %w", err) + } + + var outputDev *portaudio.DeviceInfo + if q := cmd.String("output-device"); q != "" { + outputDev, err = portaudio.FindDevice(q, false) + } else { + outputDev, err = portaudio.DefaultOutputDevice() + } + if err != nil { + return fmt.Errorf("output device: %w", err) + } + + port := cmd.Int("port") + addr := fmt.Sprintf("127.0.0.1:%d", port) + server, err := console.NewTCPServer(addr) + if err != nil { + return err + } + defer server.Close() + + actualAddr := server.Addr().String() + fmt.Fprintf(os.Stderr, "Listening on %s\n", actualAddr) + fmt.Fprintf(os.Stderr, "Input: %s\n", inputDev.Name) + fmt.Fprintf(os.Stderr, "Output: %s\n", outputDev.Name) + fmt.Fprintf(os.Stderr, "Waiting for agent connection...\n") + + conn, err := server.Accept() + if err != nil { + return fmt.Errorf("agent connection: %w", err) + } + defer conn.Close() + + fmt.Fprintf(os.Stderr, "Agent connected from %s\n", conn.RemoteAddr()) + + pipeline, err := console.NewPipeline(console.PipelineConfig{ + InputDevice: inputDev, + OutputDevice: outputDev, + NoAEC: cmd.Bool("no-aec"), + Conn: conn, + }) + if err != nil { + return fmt.Errorf("pipeline: %w", err) + } + + pipelineCtx, pipelineCancel := context.WithCancel(ctx) + defer pipelineCancel() + + go func() { + pipeline.Start(pipelineCtx) + }() + + model := newConsoleModel(pipeline, actualAddr, inputDev, outputDev) + p := tea.NewProgram(model, tea.WithAltScreen()) + + if _, err := p.Run(); err != nil { + return err + } + + pipelineCancel() + pipeline.Stop() + + return nil +} + +func listDevices() error { + devices, err := portaudio.ListDevices() + if err != nil { + return err + } + + headerStyle := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("6")) + defaultStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("2")) + + fmt.Println(headerStyle.Render(fmt.Sprintf(" %-4s %-8s %-45s %s", "#", "Type", "Name", "Default"))) + fmt.Println(strings.Repeat("─", 70)) + + for _, d := range devices { + devType := "" + if d.MaxInputChannels > 0 && d.MaxOutputChannels > 0 { + devType = "Both" + } else if d.MaxInputChannels > 0 { + devType = "Input" + } else { + devType = "Output" + } + + defStr := "" + if d.IsDefaultInput { + defStr += defaultStyle.Render("✓ input") + } + if d.IsDefaultOutput { + if defStr != "" { + defStr += " " + } + defStr += defaultStyle.Render("✓ output") + } + + fmt.Printf(" %-4d %-8s %-45s %s\n", d.Index, devType, d.Name, defStr) + } + + return nil +} diff --git a/cmd/lk/console_stub.go b/cmd/lk/console_stub.go new file mode 100644 index 00000000..6cc487ba --- /dev/null +++ b/cmd/lk/console_stub.go @@ -0,0 +1,9 @@ +//go:build !console + +package main + +import "github.com/urfave/cli/v3" + +// ConsoleCommands is nil when built without the console tag. +// This ensures the default build (CGO_ENABLED=0) is unaffected. +var ConsoleCommands []*cli.Command diff --git a/cmd/lk/console_tui.go b/cmd/lk/console_tui.go new file mode 100644 index 00000000..1c468bbd --- /dev/null +++ b/cmd/lk/console_tui.go @@ -0,0 +1,169 @@ +//go:build console + +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "strings" + "time" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + + "github.com/livekit/livekit-cli/v2/pkg/console" + "github.com/livekit/livekit-cli/v2/pkg/portaudio" +) + +var ( + consoleTitleStyle = lipgloss.NewStyle().Background(lipgloss.Color("#1fd5f9")).Foreground(lipgloss.Color("#000000")).Bold(true).Padding(0, 1) + consoleGreenStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("2")) + consoleRedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("1")) + consoleDimStyle = lipgloss.NewStyle().Faint(true) + consoleBoldStyle = lipgloss.NewStyle().Bold(true) + consoleCyanStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("6")) + consoleYellowStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("3")) +) + +// Unicode block characters for frequency visualizer +var blocks = []rune{' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'} + +type consoleTickMsg struct{} + +type consoleModel struct { + pipeline *console.AudioPipeline + addr string + inputDev *portaudio.DeviceInfo + outputDev *portaudio.DeviceInfo + + width int + height int +} + +func newConsoleModel(pipeline *console.AudioPipeline, addr string, inputDev, outputDev *portaudio.DeviceInfo) consoleModel { + return consoleModel{ + pipeline: pipeline, + addr: addr, + inputDev: inputDev, + outputDev: outputDev, + } +} + +func (m consoleModel) Init() tea.Cmd { + return tea.Batch( + consoleTickCmd(), + ) +} + +func consoleTickCmd() tea.Cmd { + return tea.Tick(50*time.Millisecond, func(t time.Time) tea.Msg { + return consoleTickMsg{} + }) +} + +func (m consoleModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "q", "ctrl+c": + return m, tea.Quit + case "m": + m.pipeline.SetMuted(!m.pipeline.Muted()) + } + + case tea.WindowSizeMsg: + m.width = msg.Width + m.height = msg.Height + + case consoleTickMsg: + return m, consoleTickCmd() + } + + return m, nil +} + +func (m consoleModel) View() string { + var b strings.Builder + + b.WriteString(consoleTitleStyle.Render(" lk console ")) + b.WriteString("\n\n") + + b.WriteString(consoleBoldStyle.Render("Status: ")) + b.WriteString(consoleGreenStyle.Render("● Connected")) + b.WriteString(" ") + b.WriteString(consoleDimStyle.Render(m.addr)) + b.WriteString("\n") + + b.WriteString(consoleBoldStyle.Render("Input: ")) + b.WriteString(m.inputDev.Name) + b.WriteString("\n") + b.WriteString(consoleBoldStyle.Render("Output: ")) + b.WriteString(m.outputDev.Name) + b.WriteString("\n\n") + + bands := m.pipeline.FFTBands() + b.WriteString(consoleBoldStyle.Render("Audio ")) + for _, band := range bands { + idx := int(band * float64(len(blocks)-1)) + if idx >= len(blocks) { + idx = len(blocks) - 1 + } + if idx < 0 { + idx = 0 + } + if band > 0.5 { + b.WriteString(consoleCyanStyle.Render(string(blocks[idx]))) + } else if band > 0.2 { + b.WriteString(consoleGreenStyle.Render(string(blocks[idx]))) + } else { + b.WriteString(consoleDimStyle.Render(string(blocks[idx]))) + } + } + b.WriteString("\n\n") + + level := m.pipeline.Level() + b.WriteString(consoleBoldStyle.Render("Mic: ")) + + if m.pipeline.Muted() { + b.WriteString(consoleRedStyle.Render("MUTED")) + } else { + // Level bar: -60dB to 0dB + normalized := (level + 60) / 60 + if normalized < 0 { + normalized = 0 + } + if normalized > 1 { + normalized = 1 + } + barWidth := 30 + filled := int(normalized * float64(barWidth)) + bar := strings.Repeat("█", filled) + strings.Repeat("░", barWidth-filled) + if normalized > 0.8 { + b.WriteString(consoleRedStyle.Render(bar)) + } else if normalized > 0.5 { + b.WriteString(consoleYellowStyle.Render(bar)) + } else { + b.WriteString(consoleGreenStyle.Render(bar)) + } + b.WriteString(fmt.Sprintf(" %.0f dB", level)) + } + b.WriteString("\n\n") + + b.WriteString(consoleDimStyle.Render("m: mute/unmute q: quit")) + b.WriteString("\n") + + return b.String() +} diff --git a/cmd/lk/main.go b/cmd/lk/main.go index faf899ec..d12796f1 100644 --- a/cmd/lk/main.go +++ b/cmd/lk/main.go @@ -70,6 +70,7 @@ func main() { app.Commands = append(app.Commands, SIPCommands...) app.Commands = append(app.Commands, PhoneNumberCommands...) app.Commands = append(app.Commands, ReplayCommands...) + app.Commands = append(app.Commands, ConsoleCommands...) app.Commands = append(app.Commands, PerfCommands...) // Register cleanup hook for SIGINT, SIGTERM, SIGQUIT diff --git a/pkg/apm/apm.go b/pkg/apm/apm.go new file mode 100644 index 00000000..a125a1e4 --- /dev/null +++ b/pkg/apm/apm.go @@ -0,0 +1,147 @@ +//go:build console + +// Package apm provides Go bindings for the WebRTC Audio Processing Module (APM). +// It supports echo cancellation (AEC3), noise suppression, automatic gain control, +// and high-pass filtering. Audio must be 48kHz int16 PCM in 10ms frames (480 samples/channel). +package apm + +// #include "bridge.h" +import "C" + +import ( + "errors" + "runtime" + "unsafe" +) + +type APMConfig struct { + EchoCanceller bool + GainController bool + HighPassFilter bool + NoiseSuppressor bool + CaptureChannels int + RenderChannels int +} + +func DefaultConfig() APMConfig { + return APMConfig{ + EchoCanceller: true, + GainController: true, + HighPassFilter: true, + NoiseSuppressor: true, + CaptureChannels: 1, + RenderChannels: 1, + } +} + +type APM struct { + handle C.ApmHandle +} + +func NewAPM(config APMConfig) (*APM, error) { + capCh := config.CaptureChannels + if capCh == 0 { + capCh = 1 + } + renCh := config.RenderChannels + if renCh == 0 { + renCh = 1 + } + + var cerr C.int + handle := C.apm_create( + boolToInt(config.EchoCanceller), + boolToInt(config.GainController), + boolToInt(config.HighPassFilter), + boolToInt(config.NoiseSuppressor), + C.int(capCh), + C.int(renCh), + &cerr, + ) + if handle == nil { + return nil, errors.New("apm: failed to create audio processing module") + } + + a := &APM{handle: handle} + runtime.SetFinalizer(a, func(a *APM) { a.Close() }) + return a, nil +} + +// ProcessCapture processes a 10ms capture (microphone) frame in-place. +// samples must contain exactly 480 * numChannels int16 values. +func (a *APM) ProcessCapture(samples []int16) error { + if a.handle == nil { + return errors.New("apm: closed") + } + if len(samples) == 0 { + return nil + } + numChannels := len(samples) / 480 + if numChannels == 0 { + numChannels = 1 + } + ret := C.apm_process_capture( + a.handle, + (*C.int16_t)(unsafe.Pointer(&samples[0])), + C.int(numChannels), + ) + if ret != 0 { + return errors.New("apm: ProcessCapture failed") + } + return nil +} + +// ProcessRender processes a 10ms render (speaker/far-end) frame in-place. +// This feeds the echo canceller with the signal being played back. +// samples must contain exactly 480 * numChannels int16 values. +func (a *APM) ProcessRender(samples []int16) error { + if a.handle == nil { + return errors.New("apm: closed") + } + if len(samples) == 0 { + return nil + } + numChannels := len(samples) / 480 + if numChannels == 0 { + numChannels = 1 + } + ret := C.apm_process_render( + a.handle, + (*C.int16_t)(unsafe.Pointer(&samples[0])), + C.int(numChannels), + ) + if ret != 0 { + return errors.New("apm: ProcessRender failed") + } + return nil +} + +// SetStreamDelayMs sets the delay in milliseconds between the far-end signal +// being rendered and arriving at the near-end microphone. +func (a *APM) SetStreamDelayMs(ms int) { + if a.handle == nil { + return + } + C.apm_set_stream_delay_ms(a.handle, C.int(ms)) +} + +func (a *APM) StreamDelayMs() int { + if a.handle == nil { + return 0 + } + return int(C.apm_stream_delay_ms(a.handle)) +} + +func (a *APM) Close() { + if a.handle != nil { + C.apm_destroy(a.handle) + a.handle = nil + } +} + +func boolToInt(b bool) C.int { + if b { + return 1 + } + return 0 +} diff --git a/pkg/apm/bridge.cpp b/pkg/apm/bridge.cpp new file mode 100644 index 00000000..41f6de3e --- /dev/null +++ b/pkg/apm/bridge.cpp @@ -0,0 +1,76 @@ +#include "bridge.h" + +#include "api/audio/builtin_audio_processing_builder.h" +#include "api/environment/environment_factory.h" +#include "api/scoped_refptr.h" +#include "modules/audio_processing/include/audio_processing.h" + +#include + +struct ApmInstance { + webrtc::scoped_refptr apm; +}; + +extern "C" { + +ApmHandle apm_create(int echo, int gain, int hpf, int ns, + int capture_ch, int render_ch, int* err) { + (void)capture_ch; + (void)render_ch; + + auto apm = webrtc::BuiltinAudioProcessingBuilder().Build( + webrtc::CreateEnvironment()); + if (!apm) { + if (err) *err = -1; + return nullptr; + } + + webrtc::AudioProcessing::Config config; + config.echo_canceller.enabled = (echo != 0); + config.gain_controller1.enabled = false; + config.gain_controller2.enabled = (gain != 0); + config.high_pass_filter.enabled = (hpf != 0); + config.noise_suppression.enabled = (ns != 0); + if (ns) { + config.noise_suppression.level = + webrtc::AudioProcessing::Config::NoiseSuppression::kHigh; + } + + apm->ApplyConfig(config); + apm->Initialize(); + + auto* inst = new ApmInstance{std::move(apm)}; + if (err) *err = 0; + return static_cast(inst); +} + +void apm_destroy(ApmHandle h) { + if (h) { + delete static_cast(h); + } +} + +int apm_process_capture(ApmHandle h, int16_t* samples, int num_channels) { + auto* inst = static_cast(h); + // 10ms at 48kHz = 480 samples per channel + webrtc::StreamConfig stream_cfg(48000, num_channels); + return inst->apm->ProcessStream(samples, stream_cfg, stream_cfg, samples); +} + +int apm_process_render(ApmHandle h, int16_t* samples, int num_channels) { + auto* inst = static_cast(h); + webrtc::StreamConfig stream_cfg(48000, num_channels); + return inst->apm->ProcessReverseStream(samples, stream_cfg, stream_cfg, samples); +} + +void apm_set_stream_delay_ms(ApmHandle h, int delay_ms) { + auto* inst = static_cast(h); + inst->apm->set_stream_delay_ms(delay_ms); +} + +int apm_stream_delay_ms(ApmHandle h) { + auto* inst = static_cast(h); + return inst->apm->stream_delay_ms(); +} + +} // extern "C" diff --git a/pkg/apm/bridge.go b/pkg/apm/bridge.go new file mode 100644 index 00000000..c21f33f3 --- /dev/null +++ b/pkg/apm/bridge.go @@ -0,0 +1,16 @@ +//go:build console + +package apm + +// #cgo CXXFLAGS: -I${SRCDIR}/webrtc -I${SRCDIR}/webrtc/third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +// #cgo darwin LDFLAGS: -lc++ +// #cgo linux LDFLAGS: -lc++ -lm -lpthread +// #cgo windows LDFLAGS: -lc++ +// #include "bridge.h" +import "C" + +import _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc" diff --git a/pkg/apm/bridge.h b/pkg/apm/bridge.h new file mode 100644 index 00000000..90355dcd --- /dev/null +++ b/pkg/apm/bridge.h @@ -0,0 +1,35 @@ +#ifndef LK_APM_BRIDGE_H +#define LK_APM_BRIDGE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* ApmHandle; + +// Create an APM instance. Returns NULL on error, sets *err to non-zero. +ApmHandle apm_create(int echo, int gain, int hpf, int ns, + int capture_ch, int render_ch, int* err); + +// Destroy an APM instance. +void apm_destroy(ApmHandle h); + +// Process a 10ms capture frame in-place. Returns 0 on success. +int apm_process_capture(ApmHandle h, int16_t* samples, int num_channels); + +// Process a 10ms render (far-end/playback) frame in-place. Returns 0 on success. +int apm_process_render(ApmHandle h, int16_t* samples, int num_channels); + +// Set the stream delay in milliseconds for echo cancellation. +void apm_set_stream_delay_ms(ApmHandle h, int delay_ms); + +// Get the current stream delay in milliseconds. +int apm_stream_delay_ms(ApmHandle h); + +#ifdef __cplusplus +} +#endif + +#endif // LK_APM_BRIDGE_H diff --git a/pkg/apm/webrtc/api/adaptation/resource.h b/pkg/apm/webrtc/api/adaptation/resource.h new file mode 100644 index 00000000..bd71b742 --- /dev/null +++ b/pkg/apm/webrtc/api/adaptation/resource.h @@ -0,0 +1,66 @@ +/* + * 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_ADAPTATION_RESOURCE_H_ +#define API_ADAPTATION_RESOURCE_H_ + +#include + +#include "api/ref_count.h" +#include "api/scoped_refptr.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +class Resource; + +enum class ResourceUsageState { + // Action is needed to minimze the load on this resource. + kOveruse, + // Increasing the load on this resource is desired, if possible. + kUnderuse, +}; + +RTC_EXPORT const char* ResourceUsageStateToString( + ResourceUsageState usage_state); + +class RTC_EXPORT ResourceListener { + public: + virtual ~ResourceListener(); + + virtual void OnResourceUsageStateMeasured(scoped_refptr resource, + ResourceUsageState usage_state) = 0; +}; + +// A Resource monitors an implementation-specific resource. It may report +// kOveruse or kUnderuse when resource usage is high or low enough that we +// should perform some sort of mitigation to fulfil the resource's constraints. +// +// The methods on this interface are invoked on the adaptation task queue. +// Resource usage measurements may be performed on an any task queue. +// +// The Resource is reference counted to prevent use-after-free when posting +// between task queues. As such, the implementation MUST NOT make any +// assumptions about which task queue Resource is destructed on. +class RTC_EXPORT Resource : public RefCountInterface { + public: + Resource(); + // Destruction may happen on any task queue. + ~Resource() override; + + virtual std::string Name() const = 0; + // The `listener` may be informed of resource usage measurements on any task + // queue, but not after this method is invoked with the null argument. + virtual void SetResourceListener(ResourceListener* listener) = 0; +}; + +} // namespace webrtc + +#endif // API_ADAPTATION_RESOURCE_H_ diff --git a/pkg/apm/webrtc/api/api.go b/pkg/apm/webrtc/api/api.go new file mode 100644 index 00000000..99ce769c --- /dev/null +++ b/pkg/apm/webrtc/api/api.go @@ -0,0 +1,10 @@ +//go:build console + +package api + +// #cgo CXXFLAGS: -I${SRCDIR}/.. -I${SRCDIR}/../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/api/array_view.h b/pkg/apm/webrtc/api/array_view.h new file mode 100644 index 00000000..db85b479 --- /dev/null +++ b/pkg/apm/webrtc/api/array_view.h @@ -0,0 +1,347 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_ARRAY_VIEW_H_ +#define API_ARRAY_VIEW_H_ + +#include +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/type_traits.h" + +namespace webrtc { + +// tl;dr: webrtc::ArrayView is the same thing as gsl::span from the Guideline +// Support Library. +// +// Many functions read from or write to arrays. The obvious way to do this is +// to use two arguments, a pointer to the first element and an element count: +// +// bool Contains17(const int* arr, size_t size) { +// for (size_t i = 0; i < size; ++i) { +// if (arr[i] == 17) +// return true; +// } +// return false; +// } +// +// This is flexible, since it doesn't matter how the array is stored (C array, +// std::vector, webrtc::Buffer, ...), but it's error-prone because the caller +// has to correctly specify the array length: +// +// Contains17(arr, arraysize(arr)); // C array +// Contains17(arr.data(), arr.size()); // std::vector +// Contains17(arr, size); // pointer + size +// ... +// +// It's also kind of messy to have two separate arguments for what is +// conceptually a single thing. +// +// Enter webrtc::ArrayView. It contains a T pointer (to an array it doesn't +// own) and a count, and supports the basic things you'd expect, such as +// indexing and iteration. It allows us to write our function like this: +// +// bool Contains17(webrtc::ArrayView arr) { +// for (auto e : arr) { +// if (e == 17) +// return true; +// } +// return false; +// } +// +// And even better, because a bunch of things will implicitly convert to +// ArrayView, we can call it like this: +// +// Contains17(arr); // C array +// Contains17(arr); // std::vector +// Contains17(webrtc::ArrayView(arr, size)); // pointer + size +// Contains17(nullptr); // nullptr -> empty ArrayView +// ... +// +// ArrayView stores both a pointer and a size, but you may also use +// ArrayView, which has a size that's fixed at compile time (which means +// it only has to store the pointer). +// +// One important point is that ArrayView and ArrayView are +// different types, which allow and don't allow mutation of the array elements, +// respectively. The implicit conversions work just like you'd hope, so that +// e.g. vector will convert to either ArrayView or ArrayView, but const vector will convert only to ArrayView. +// (ArrayView itself can be the source type in such conversions, so +// ArrayView will convert to ArrayView.) +// +// Note: ArrayView is tiny (just a pointer and a count if variable-sized, just +// a pointer if fix-sized) and trivially copyable, so it's probably cheaper to +// pass it by value than by const reference. + +namespace array_view_internal { + +// Magic constant for indicating that the size of an ArrayView is variable +// instead of fixed. +enum : std::ptrdiff_t { kArrayViewVarSize = -4711 }; + +// Base class for ArrayViews of fixed nonzero size. +template +class ArrayViewBase { + static_assert(Size > 0, "ArrayView size must be variable or non-negative"); + + public: + ArrayViewBase(T* data, size_t /* size */) : data_(data) {} + + static constexpr size_t size() { return Size; } + static constexpr bool empty() { return false; } + T* data() const { return data_; } + + protected: + static constexpr bool fixed_size() { return true; } + + private: + T* data_; +}; + +// Specialized base class for ArrayViews of fixed zero size. +template +class ArrayViewBase { + public: + explicit ArrayViewBase(T* /* data */, size_t /* size */) {} + + static constexpr size_t size() { return 0; } + static constexpr bool empty() { return true; } + T* data() const { return nullptr; } + + protected: + static constexpr bool fixed_size() { return true; } +}; + +// Specialized base class for ArrayViews of variable size. +template +class ArrayViewBase { + public: + ArrayViewBase(T* data, size_t size) + : data_(size == 0 ? nullptr : data), size_(size) {} + + size_t size() const { return size_; } + bool empty() const { return size_ == 0; } + T* data() const { return data_; } + + protected: + static constexpr bool fixed_size() { return false; } + + private: + T* data_; + size_t size_; +}; + +} // namespace array_view_internal + +template +class ArrayView final : public array_view_internal::ArrayViewBase { + public: + using value_type = T; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using const_iterator = const T*; + + // Construct an ArrayView from a pointer and a length. + template + ArrayView(U* data, size_t size) + : array_view_internal::ArrayViewBase::ArrayViewBase(data, size) { + RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data()); + RTC_DCHECK_EQ(size, this->size()); + RTC_DCHECK_EQ(!this->data(), + this->size() == 0); // data is null iff size == 0. + } + + // Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0 + // cannot be empty. + ArrayView() : ArrayView(nullptr, 0) {} + ArrayView(std::nullptr_t) // NOLINT + : ArrayView() {} + ArrayView(std::nullptr_t, size_t size) + : ArrayView(static_cast(nullptr), size) { + static_assert(Size == 0 || Size == array_view_internal::kArrayViewVarSize, + ""); + RTC_DCHECK_EQ(0, size); + } + + // Construct an ArrayView from a C-style array. + template + ArrayView(U (&array)[N]) // NOLINT + : ArrayView(array, N) { + static_assert(Size == N || Size == array_view_internal::kArrayViewVarSize, + "Array size must match ArrayView size"); + } + + // (Only if size is fixed.) Construct a fixed size ArrayView from a + // non-const std::array instance. For an ArrayView with variable size, the + // used ctor is ArrayView(U& u) instead. + template (N)>::type* = nullptr> + ArrayView(std::array& u) // NOLINT + : ArrayView(u.data(), u.size()) {} + + // (Only if size is fixed.) Construct a fixed size ArrayView where T is + // const from a const(expr) std::array instance. For an ArrayView with + // variable size, the used ctor is ArrayView(U& u) instead. + template (N)>::type* = nullptr> + ArrayView(const std::array& u) // NOLINT + : ArrayView(u.data(), u.size()) {} + + // (Only if size is fixed.) Construct an ArrayView from any type U that has a + // static constexpr size() method whose return value is equal to Size, and a + // data() method whose return value converts implicitly to T*. In particular, + // this means we allow conversion from ArrayView to ArrayView, but not the other way around. We also don't allow conversion from + // ArrayView to ArrayView, or from ArrayView to ArrayView when M != N. + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + ArrayView(U& u) // NOLINT + : ArrayView(u.data(), u.size()) { + static_assert(U::size() == Size, "Sizes must match exactly"); + } + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + ArrayView(const U& u) // NOLINT(runtime/explicit) + : ArrayView(u.data(), u.size()) { + static_assert(U::size() == Size, "Sizes must match exactly"); + } + + // (Only if size is variable.) Construct an ArrayView from any type U that + // has a size() method whose return value converts implicitly to size_t, and + // a data() method whose return value converts implicitly to T*. In + // particular, this means we allow conversion from ArrayView to + // ArrayView, but not the other way around. Other allowed + // conversions include + // ArrayView to ArrayView or ArrayView, + // std::vector to ArrayView or ArrayView, + // const std::vector to ArrayView, + // webrtc::Buffer to ArrayView or ArrayView, and + // const webrtc::Buffer to ArrayView. + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + ArrayView(U& u) // NOLINT + : ArrayView(u.data(), u.size()) {} + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + ArrayView(const U& u) // NOLINT(runtime/explicit) + : ArrayView(u.data(), u.size()) {} + + // Indexing and iteration. These allow mutation even if the ArrayView is + // const, because the ArrayView doesn't own the array. (To prevent mutation, + // use a const element type.) + T& operator[](size_t idx) const { + RTC_DCHECK_LT(idx, this->size()); + RTC_DCHECK(this->data()); + return this->data()[idx]; + } + T* begin() const { return this->data(); } + T* end() const { return this->data() + this->size(); } + const T* cbegin() const { return this->data(); } + const T* cend() const { return this->data() + this->size(); } + std::reverse_iterator rbegin() const { + return std::make_reverse_iterator(end()); + } + std::reverse_iterator rend() const { + return std::make_reverse_iterator(begin()); + } + std::reverse_iterator crbegin() const { + return std::make_reverse_iterator(cend()); + } + std::reverse_iterator crend() const { + return std::make_reverse_iterator(cbegin()); + } + + ArrayView subview(size_t offset, size_t size) const { + return offset < this->size() + ? ArrayView(this->data() + offset, + std::min(size, this->size() - offset)) + : ArrayView(); + } + ArrayView subview(size_t offset) const { + return subview(offset, this->size()); + } +}; + +// Comparing two ArrayViews compares their (pointer,size) pairs; it does *not* +// dereference the pointers. +template +bool operator==(const ArrayView& a, const ArrayView& b) { + return a.data() == b.data() && a.size() == b.size(); +} +template +bool operator!=(const ArrayView& a, const ArrayView& b) { + return !(a == b); +} + +// Variable-size ArrayViews are the size of two pointers; fixed-size ArrayViews +// are the size of one pointer. (And as a special case, fixed-size ArrayViews +// of size 0 require no storage.) +static_assert(sizeof(ArrayView) == 2 * sizeof(int*), ""); +static_assert(sizeof(ArrayView) == sizeof(int*), ""); +static_assert(std::is_empty>::value, ""); + +template +inline ArrayView MakeArrayView(T* data, size_t size) { + return ArrayView(data, size); +} + +// Only for primitive types that have the same size and aligment. +// Allow reinterpret cast of the array view to another primitive type of the +// same size. +// Template arguments order is (U, T, Size) to allow deduction of the template +// arguments in client calls: reinterpret_array_view(array_view). +template +inline ArrayView reinterpret_array_view(ArrayView view) { + static_assert(sizeof(U) == sizeof(T) && alignof(U) == alignof(T), + "ArrayView reinterpret_cast is only supported for casting " + "between views that represent the same chunk of memory."); + static_assert( + std::is_fundamental::value && std::is_fundamental::value, + "ArrayView reinterpret_cast is only supported for casting between " + "fundamental types."); + return ArrayView(reinterpret_cast(view.data()), view.size()); +} + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +template +using ArrayView = ::webrtc::ArrayView; +using ::webrtc::MakeArrayView; +using ::webrtc::reinterpret_array_view; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // API_ARRAY_VIEW_H_ diff --git a/pkg/apm/webrtc/api/async_dns_resolver.h b/pkg/apm/webrtc/api/async_dns_resolver.h new file mode 100644 index 00000000..11fdf67d --- /dev/null +++ b/pkg/apm/webrtc/api/async_dns_resolver.h @@ -0,0 +1,102 @@ +/* + * Copyright 2021 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_ASYNC_DNS_RESOLVER_H_ +#define API_ASYNC_DNS_RESOLVER_H_ + +#include + +#include "absl/functional/any_invocable.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// This interface defines the methods to resolve a hostname asynchronously. +// The AsyncDnsResolverInterface class encapsulates a single name query. +// +// Usage: +// std::unique_ptr resolver = +// factory->Create(address-to-be-resolved, [r = resolver.get()]() { +// if (r->result.GetResolvedAddress(AF_INET, &addr) { +// // success +// } else { +// // failure +// error = r->result().GetError(); +// } +// // Release resolver. +// resolver_list.erase(std::remove_if(resolver_list.begin(), +// resolver_list.end(), +// [](refptr) { refptr.get() == r; }); +// }); +// resolver_list.push_back(std::move(resolver)); + +class AsyncDnsResolverResult { + public: + virtual ~AsyncDnsResolverResult() = default; + // Returns true iff the address from `Start` was successfully resolved. + // If the address was successfully resolved, sets `addr` to a copy of the + // address from `Start` with the IP address set to the top most resolved + // address of `family` (`addr` will have both hostname and the resolved ip). + virtual bool GetResolvedAddress(int family, SocketAddress* addr) const = 0; + // Returns error from resolver. + virtual int GetError() const = 0; +}; + +// The API for a single name query. +// The constructor, destructor and all functions must be called from +// the same sequence, and the callback will also be called on that sequence. +// The class guarantees that the callback will not be called if the +// resolver's destructor has been called. +class RTC_EXPORT AsyncDnsResolverInterface { + public: + virtual ~AsyncDnsResolverInterface() = default; + + // Start address resolution of the hostname in `addr`. + virtual void Start(const SocketAddress& addr, + absl::AnyInvocable callback) = 0; + // Start address resolution of the hostname in `addr` matching `family`. + virtual void Start(const SocketAddress& addr, + int family, + absl::AnyInvocable callback) = 0; + virtual const AsyncDnsResolverResult& result() const = 0; +}; + +// An abstract factory for creating AsyncDnsResolverInterfaces. This allows +// client applications to provide WebRTC with their own mechanism for +// performing DNS resolution. +class AsyncDnsResolverFactoryInterface { + public: + virtual ~AsyncDnsResolverFactoryInterface() = default; + + // Creates an AsyncDnsResolver and starts resolving the name. The callback + // will be called when resolution is finished. + // The callback will be called on the sequence that the caller runs on. + virtual std::unique_ptr CreateAndResolve( + const SocketAddress& addr, + absl::AnyInvocable callback) = 0; + // Creates an AsyncDnsResolver and starts resolving the name to an address + // matching the specified family. The callback will be called when resolution + // is finished. The callback will be called on the sequence that the caller + // runs on. + virtual std::unique_ptr CreateAndResolve( + const SocketAddress& addr, + int family, + absl::AnyInvocable callback) = 0; + // Creates an AsyncDnsResolver and does not start it. + // For backwards compatibility, will be deprecated and removed. + // One has to do a separate Start() call on the + // resolver to start name resolution. + virtual std::unique_ptr Create() = 0; +}; + +} // namespace webrtc + +#endif // API_ASYNC_DNS_RESOLVER_H_ diff --git a/pkg/apm/webrtc/api/audio/audio.go b/pkg/apm/webrtc/api/audio/audio.go new file mode 100644 index 00000000..8b3f44b8 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio.go @@ -0,0 +1,10 @@ +//go:build console + +package audio + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/api/audio/audio_device.h b/pkg/apm/webrtc/api/audio/audio_device.h new file mode 100644 index 00000000..5670c2be --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio_device.h @@ -0,0 +1,265 @@ +/* + * 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_DEVICE_H_ +#define API_AUDIO_AUDIO_DEVICE_H_ + +#include +#include +#include "api/audio/audio_device_defines.h" +#include "api/ref_count.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_factory.h" +#include "sdk/objc/base/RTCMacros.h" + +RTC_FWD_DECL_OBJC_CLASS(AVAudioEngine); +RTC_FWD_DECL_OBJC_CLASS(AVAudioFormat); +RTC_FWD_DECL_OBJC_CLASS(AVAudioNode); +RTC_FWD_DECL_OBJC_CLASS(AVAudioSourceNode); +RTC_FWD_DECL_OBJC_CLASS(AVAudioMixerNode); +RTC_FWD_DECL_OBJC_CLASS(NSDictionary); + +namespace webrtc { + +class AudioDeviceModuleForTest; +class AudioDeviceObserver; + +class AudioDeviceModule : public webrtc::RefCountInterface { + public: + enum AudioLayer { + kPlatformDefaultAudio = 0, + kWindowsCoreAudio, + kWindowsCoreAudio2, + kLinuxAlsaAudio, + kLinuxPulseAudio, + kAndroidJavaAudio, + kAndroidOpenSLESAudio, + kAndroidJavaInputAndOpenSLESOutputAudio, + kAndroidAAudioAudio, + kAndroidJavaInputAndAAudioOutputAudio, + kDummyAudio, + }; + + enum WindowsDeviceType { kDefaultCommunicationDevice = -1, kDefaultDevice = -2 }; + + // Only supported on iOS. +#if defined(WEBRTC_IOS) + enum MutedSpeechEvent { kMutedSpeechStarted, kMutedSpeechEnded }; + typedef void (^MutedSpeechEventHandler)(MutedSpeechEvent event); +#endif // WEBRTC_IOS + + enum SpeechActivityEvent { + kStarted = 0, + kEnded, + }; + + struct Stats { + // The fields below correspond to similarly-named fields in the WebRTC stats + // spec. https://w3c.github.io/webrtc-stats/#playoutstats-dict* + double synthesized_samples_duration_s = 0; + uint64_t synthesized_samples_events = 0; + double total_samples_duration_s = 0; + double total_playout_delay_s = 0; + uint64_t total_samples_count = 0; + }; + + public: + // Creates a default ADM for usage in production code. + static scoped_refptr Create(AudioLayer audio_layer, + TaskQueueFactory* task_queue_factory, + bool bypass_voice_processing = false); + // Creates an ADM with support for extra test methods. Don't use this factory + // in production code. + static scoped_refptr CreateForTest( + AudioLayer audio_layer, TaskQueueFactory* task_queue_factory, + bool bypass_voice_processing = false); + + // Retrieve the currently utilized audio layer + virtual int32_t ActiveAudioLayer(AudioLayer* audioLayer) const = 0; + + // Full-duplex transportation of PCM audio + virtual int32_t RegisterAudioCallback(AudioTransport* audioCallback) = 0; + + // Main initialization and termination + virtual int32_t Init() = 0; + virtual int32_t Terminate() = 0; + virtual bool Initialized() const = 0; + + // Device enumeration + virtual int16_t PlayoutDevices() = 0; + virtual int16_t RecordingDevices() = 0; + virtual int32_t PlayoutDeviceName(uint16_t index, char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) = 0; + virtual int32_t RecordingDeviceName(uint16_t index, char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) = 0; + + // Device selection + virtual int32_t SetPlayoutDevice(uint16_t index) = 0; + virtual int32_t SetPlayoutDevice(WindowsDeviceType device) = 0; + virtual int32_t SetRecordingDevice(uint16_t index) = 0; + virtual int32_t SetRecordingDevice(WindowsDeviceType device) = 0; + + // Audio transport initialization + virtual int32_t PlayoutIsAvailable(bool* available) = 0; + virtual int32_t InitPlayout() = 0; + virtual bool PlayoutIsInitialized() const = 0; + virtual int32_t RecordingIsAvailable(bool* available) = 0; + virtual int32_t InitRecording() = 0; + virtual bool RecordingIsInitialized() const = 0; + + // Audio transport control + virtual int32_t StartPlayout() = 0; + virtual int32_t StopPlayout() = 0; + virtual bool Playing() const = 0; + virtual int32_t StartRecording() = 0; + virtual int32_t StopRecording() = 0; + virtual bool Recording() const = 0; + + // Audio mixer initialization + virtual int32_t InitSpeaker() = 0; + virtual bool SpeakerIsInitialized() const = 0; + virtual int32_t InitMicrophone() = 0; + virtual bool MicrophoneIsInitialized() const = 0; + + // Speaker volume controls + virtual int32_t SpeakerVolumeIsAvailable(bool* available) = 0; + virtual int32_t SetSpeakerVolume(uint32_t volume) = 0; + virtual int32_t SpeakerVolume(uint32_t* volume) const = 0; + virtual int32_t MaxSpeakerVolume(uint32_t* maxVolume) const = 0; + virtual int32_t MinSpeakerVolume(uint32_t* minVolume) const = 0; + + // Microphone volume controls + virtual int32_t MicrophoneVolumeIsAvailable(bool* available) = 0; + virtual int32_t SetMicrophoneVolume(uint32_t volume) = 0; + virtual int32_t MicrophoneVolume(uint32_t* volume) const = 0; + virtual int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const = 0; + virtual int32_t MinMicrophoneVolume(uint32_t* minVolume) const = 0; + + // Speaker mute control + virtual int32_t SpeakerMuteIsAvailable(bool* available) = 0; + virtual int32_t SetSpeakerMute(bool enable) = 0; + virtual int32_t SpeakerMute(bool* enabled) const = 0; + + // Microphone mute control + virtual int32_t MicrophoneMuteIsAvailable(bool* available) = 0; + virtual int32_t SetMicrophoneMute(bool enable) = 0; + virtual int32_t MicrophoneMute(bool* enabled) const = 0; + + // Stereo support + virtual int32_t StereoPlayoutIsAvailable(bool* available) const = 0; + virtual int32_t SetStereoPlayout(bool enable) = 0; + virtual int32_t StereoPlayout(bool* enabled) const = 0; + virtual int32_t StereoRecordingIsAvailable(bool* available) const = 0; + virtual int32_t SetStereoRecording(bool enable) = 0; + virtual int32_t StereoRecording(bool* enabled) const = 0; + + // Playout delay + virtual int32_t PlayoutDelay(uint16_t* delayMS) const = 0; + + // Only supported on Android. + virtual bool BuiltInAECIsAvailable() const = 0; + virtual bool BuiltInAGCIsAvailable() const = 0; + virtual bool BuiltInNSIsAvailable() const = 0; + + // Enables the built-in audio effects. Only supported on Android. + virtual int32_t EnableBuiltInAEC(bool enable) = 0; + virtual int32_t EnableBuiltInAGC(bool enable) = 0; + virtual int32_t EnableBuiltInNS(bool enable) = 0; + + // Play underrun count. Only supported on Android. + // TODO(alexnarest): Make it abstract after upstream projects support it. + virtual int32_t GetPlayoutUnderrunCount() const { return -1; } + + // Used to generate RTC stats. If not implemented, RTCAudioPlayoutStats will + // not be present in the stats. + virtual std::optional GetStats() const { return std::nullopt; } + + // Whether to stop recording when all streams are muted. + virtual bool IsStopOnMuteModeEnabled() const { return true; } + +// Only supported on iOS. +#if defined(WEBRTC_IOS) + virtual int GetPlayoutAudioParameters(AudioParameters* params) const = 0; + virtual int GetRecordAudioParameters(AudioParameters* params) const = 0; +#endif // WEBRTC_IOS + + virtual int32_t SetObserver(AudioDeviceObserver* observer) { return -1; } + virtual int32_t GetPlayoutDevice() const { return -1; } + virtual int32_t GetRecordingDevice() const { return -1; } + + protected: + ~AudioDeviceModule() override {} +}; + +// Extends the default ADM interface with some extra test methods. +// Intended for usage in tests only and requires a unique factory method. +class AudioDeviceModuleForTest : public AudioDeviceModule { + public: + // Triggers internal restart sequences of audio streaming. Can be used by + // tests to emulate events corresponding to e.g. removal of an active audio + // device or other actions which causes the stream to be disconnected. + virtual int RestartPlayoutInternally() = 0; + virtual int RestartRecordingInternally() = 0; + + virtual int SetPlayoutSampleRate(uint32_t sample_rate) = 0; + virtual int SetRecordingSampleRate(uint32_t sample_rate) = 0; +}; + +class AudioDeviceObserver { + public: + virtual ~AudioDeviceObserver() = default; + + // input/output devices updated or default device changed + virtual void OnDevicesUpdated() {} + virtual void OnSpeechActivityEvent(AudioDeviceModule::SpeechActivityEvent event) {} + + // AVAudioEngine lifecycle + virtual int32_t OnEngineDidCreate(AVAudioEngine* engine) { return 0; } + + virtual int32_t OnEngineWillEnable(AVAudioEngine* engine, bool playout_enabled, + bool recording_enabled) { + return 0; + } + + virtual int32_t OnEngineWillStart(AVAudioEngine* engine, bool playout_enabled, + bool recording_enabled) { + return 0; + } + + virtual int32_t OnEngineDidStop(AVAudioEngine* engine, bool playout_enabled, + bool recording_enabled) { + return 0; + } + + virtual int32_t OnEngineDidDisable(AVAudioEngine* engine, bool playout_enabled, + bool recording_enabled) { + return 0; + } + + virtual int32_t OnEngineWillRelease(AVAudioEngine* engine) { return 0; } + + // Override the input node configuration with a custom implementation. + virtual int32_t OnEngineWillConnectInput(AVAudioEngine* engine, AVAudioNode* src, + AVAudioNode* dst, AVAudioFormat* format, + NSDictionary* context) { + return 0; + } + + // Override the input node configuration with a custom implementation. + virtual int32_t OnEngineWillConnectOutput(AVAudioEngine* engine, AVAudioNode* src, + AVAudioNode* dst, AVAudioFormat* format, + NSDictionary* context) { + return 0; + } +}; + +} // namespace webrtc + +#endif // API_AUDIO_AUDIO_DEVICE_H_ diff --git a/pkg/apm/webrtc/api/audio/audio_device_defines.h b/pkg/apm/webrtc/api/audio/audio_device_defines.h new file mode 100644 index 00000000..63eca2a4 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio_device_defines.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_AUDIO_DEVICE_DEFINES_H_ +#define API_AUDIO_AUDIO_DEVICE_DEFINES_H_ + +#include + +#include +#include +#include + +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +static const int kAdmMaxDeviceNameSize = 128; +static const int kAdmMaxFileNameSize = 512; +static const int kAdmMaxGuidSize = 128; + +static const int kAdmMinPlayoutBufferSizeMs = 10; +static const int kAdmMaxPlayoutBufferSizeMs = 250; + +// ---------------------------------------------------------------------------- +// AudioTransport +// ---------------------------------------------------------------------------- + +class AudioTransport { + public: + // TODO(bugs.webrtc.org/13620) Deprecate this function + virtual int32_t RecordedDataIsAvailable(const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel) = 0; // NOLINT + + virtual int32_t RecordedDataIsAvailable( + const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel, + std::optional /* estimatedCaptureTimeNS */) { // NOLINT + // TODO(webrtc:13620) Make the default behaver of the new API to behave as + // the old API. This can be pure virtual if all uses of the old API is + // removed. + return RecordedDataIsAvailable( + audioSamples, nSamples, nBytesPerSample, nChannels, samplesPerSec, + totalDelayMS, clockDrift, currentMicLevel, keyPressed, newMicLevel); + } + + // Implementation has to setup safe values for all specified out parameters. + virtual int32_t NeedMorePlayData(size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + void* audioSamples, + size_t& nSamplesOut, // NOLINT + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) = 0; // NOLINT + + // Method to pull mixed render audio data from all active VoE channels. + // The data will not be passed as reference for audio processing internally. + virtual void PullRenderData(int bits_per_sample, + int sample_rate, + size_t number_of_channels, + size_t number_of_frames, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) = 0; + + protected: + virtual ~AudioTransport() {} +}; + +// Helper class for storage of fundamental audio parameters such as sample rate, +// number of channels, native buffer size etc. +// Note that one audio frame can contain more than one channel sample and each +// sample is assumed to be a 16-bit PCM sample. Hence, one audio frame in +// stereo contains 2 * (16/8) = 4 bytes of data. +class AudioParameters { + public: + // This implementation does only support 16-bit PCM samples. + static const size_t kBitsPerSample = 16; + AudioParameters() + : sample_rate_(0), + channels_(0), + frames_per_buffer_(0), + frames_per_10ms_buffer_(0) {} + AudioParameters(int sample_rate, size_t channels, size_t frames_per_buffer) + : sample_rate_(sample_rate), + channels_(channels), + frames_per_buffer_(frames_per_buffer), + frames_per_10ms_buffer_(static_cast(sample_rate / 100)) {} + void reset(int sample_rate, size_t channels, size_t frames_per_buffer) { + sample_rate_ = sample_rate; + channels_ = channels; + frames_per_buffer_ = frames_per_buffer; + frames_per_10ms_buffer_ = static_cast(sample_rate / 100); + } + size_t bits_per_sample() const { return kBitsPerSample; } + void reset(int sample_rate, size_t channels, double buffer_duration) { + reset(sample_rate, channels, + static_cast(sample_rate * buffer_duration + 0.5)); + } + void reset(int sample_rate, size_t channels) { + reset(sample_rate, channels, static_cast(0)); + } + int sample_rate() const { return sample_rate_; } + size_t channels() const { return channels_; } + size_t frames_per_buffer() const { return frames_per_buffer_; } + size_t frames_per_10ms_buffer() const { return frames_per_10ms_buffer_; } + size_t GetBytesPerFrame() const { return channels_ * kBitsPerSample / 8; } + size_t GetBytesPerBuffer() const { + return frames_per_buffer_ * GetBytesPerFrame(); + } + // The WebRTC audio device buffer (ADB) only requires that the sample rate + // and number of channels are configured. Hence, to be "valid", only these + // two attributes must be set. + bool is_valid() const { return ((sample_rate_ > 0) && (channels_ > 0)); } + // Most platforms also require that a native buffer size is defined. + // An audio parameter instance is considered to be "complete" if it is both + // "valid" (can be used by the ADB) and also has a native frame size. + bool is_complete() const { return (is_valid() && (frames_per_buffer_ > 0)); } + size_t GetBytesPer10msBuffer() const { + return frames_per_10ms_buffer_ * GetBytesPerFrame(); + } + double GetBufferSizeInMilliseconds() const { + if (sample_rate_ == 0) + return 0.0; + return frames_per_buffer_ / (sample_rate_ / 1000.0); + } + double GetBufferSizeInSeconds() const { + if (sample_rate_ == 0) + return 0.0; + return static_cast(frames_per_buffer_) / (sample_rate_); + } + std::string ToString() const { + char ss_buf[1024]; + SimpleStringBuilder ss(ss_buf); + ss << "AudioParameters: "; + ss << "sample_rate=" << sample_rate() << ", channels=" << channels(); + ss << ", frames_per_buffer=" << frames_per_buffer(); + ss << ", frames_per_10ms_buffer=" << frames_per_10ms_buffer(); + ss << ", bytes_per_frame=" << GetBytesPerFrame(); + ss << ", bytes_per_buffer=" << GetBytesPerBuffer(); + ss << ", bytes_per_10ms_buffer=" << GetBytesPer10msBuffer(); + ss << ", size_in_ms=" << GetBufferSizeInMilliseconds(); + return ss.str(); + } + + private: + int sample_rate_; + size_t channels_; + size_t frames_per_buffer_; + size_t frames_per_10ms_buffer_; +}; + +} // namespace webrtc + +#endif // API_AUDIO_AUDIO_DEVICE_DEFINES_H_ diff --git a/pkg/apm/webrtc/api/audio/audio_frame.cc b/pkg/apm/webrtc/api/audio/audio_frame.cc new file mode 100644 index 00000000..f578b719 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio_frame.cc @@ -0,0 +1,235 @@ +/* + * 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/audio/audio_frame.h" + +#include + +#include +#include + +#include "api/array_view.h" +#include "api/audio/audio_view.h" +#include "api/audio/channel_layout.h" +#include "api/rtp_packet_infos.h" +#include "rtc_base/checks.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +AudioFrame::AudioFrame() { + // Visual Studio doesn't like this in the class definition. + static_assert(sizeof(data_) == kMaxDataSizeBytes, "kMaxDataSizeBytes"); +} + +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() { + ResetWithoutMuting(); + muted_ = true; +} + +void AudioFrame::ResetWithoutMuting() { + // 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; + ntp_time_ms_ = -1; + samples_per_channel_ = 0; + sample_rate_hz_ = 0; + num_channels_ = 0; + channel_layout_ = CHANNEL_LAYOUT_NONE; + speech_type_ = kUndefined; + vad_activity_ = kVadUnknown; + profile_timestamp_ms_ = 0; + packet_infos_ = RtpPacketInfos(); + absolute_capture_timestamp_ms_ = std::nullopt; +} + +void AudioFrame::UpdateFrame(uint32_t timestamp, + const int16_t* data, + size_t samples_per_channel, + int sample_rate_hz, + 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; + speech_type_ = speech_type; + vad_activity_ = vad_activity; + num_channels_ = num_channels; + channel_layout_ = GuessChannelLayout(num_channels); + if (channel_layout_ != CHANNEL_LAYOUT_UNSUPPORTED) { + RTC_DCHECK_EQ(num_channels, ChannelLayoutToChannelCount(channel_layout_)); + } + + const size_t length = samples_per_channel * num_channels; + RTC_CHECK_LE(length, data_.size()); + if (data != nullptr) { + memcpy(data_.data(), data, sizeof(int16_t) * length); + muted_ = false; + } else { + muted_ = true; + } +} + +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_; + packet_infos_ = src.packet_infos_; + muted_ = src.muted(); + samples_per_channel_ = src.samples_per_channel_; + sample_rate_hz_ = src.sample_rate_hz_; + speech_type_ = src.speech_type_; + vad_activity_ = src.vad_activity_; + num_channels_ = src.num_channels_; + channel_layout_ = src.channel_layout_; + absolute_capture_timestamp_ms_ = src.absolute_capture_timestamp_ms(); + + 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()); + } +} + +void AudioFrame::UpdateProfileTimeStamp() { + profile_timestamp_ms_ = TimeMillis(); +} + +int64_t AudioFrame::ElapsedProfileTimeMs() const { + if (profile_timestamp_ms_ == 0) { + // Profiling has not been activated. + return -1; + } + return TimeSince(profile_timestamp_ms_); +} + +const int16_t* AudioFrame::data() const { + return muted_ ? zeroed_data().begin() : data_.data(); +} + +InterleavedView AudioFrame::data_view() const { + // If you get a nullptr from `data_view()`, it's likely because the + // samples_per_channel_ and/or num_channels_ members haven't been properly + // set. Since `data_view()` returns an InterleavedView<> (which internally + // uses webrtc::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(muted_ ? &zeroed_data()[0] : &data_[0], + samples_per_channel_, num_channels_); +} + +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_) { + ClearSamples(data_); + muted_ = false; + } + return &data_[0]; +} + +InterleavedView 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(&data_[0], samples_per_channel, num_channels); +} + +void AudioFrame::Mute() { + muted_ = true; +} + +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 +ArrayView AudioFrame::zeroed_data() { + static int16_t* null_data = new int16_t[kMaxDataSizeSamples](); + return ArrayView(null_data, kMaxDataSizeSamples); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/audio/audio_frame.h b/pkg/apm/webrtc/api/audio/audio_frame.h new file mode 100644 index 00000000..456bd288 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio_frame.h @@ -0,0 +1,230 @@ +/* + * 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_AUDIO_AUDIO_FRAME_H_ +#define API_AUDIO_AUDIO_FRAME_H_ + +#include +#include + +#include +#include + +#include "api/array_view.h" +#include "api/audio/audio_view.h" +#include "api/audio/channel_layout.h" +#include "api/rtp_packet_infos.h" +#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. + * + * Notes + * - This is a de-facto api, not designed for external use. The AudioFrame class + * is in need of overhaul or even replacement, and anyone depending on it + * should be prepared for that. + * - The total number of samples is samples_per_channel_ * num_channels_. + * - Stereo data is interleaved starting with the left channel. + */ +class AudioFrame { + public: + // Using constexpr here causes linker errors unless the variable also has an + // out-of-class definition, which is impractical in this header-only class. + // (This makes no sense because it compiles as an enum value, which we most + // certainly cannot take the address of, just fine.) C++17 introduces inline + // variables which should allow us to switch to constexpr and keep this a + // header-only class. + enum : size_t { + // Stereo, 32 kHz, 120 ms (2 * 32 * 120) + // Stereo, 192 kHz, 20 ms (2 * 192 * 20) + kMaxDataSizeSamples = 7680, + kMaxDataSizeBytes = kMaxDataSizeSamples * sizeof(int16_t), + }; + + enum VADActivity { kVadActive = 0, kVadPassive = 1, kVadUnknown = 2 }; + enum SpeechType { + kNormalSpeech = 0, + kPLC = 1, + kCNG = 2, + kPLCCNG = 3, + kCodecPLC = 5, + kUndefined = 4 + }; + + AudioFrame(); + + // 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(); + // Same as Reset(), but leaves mute state unchanged. Muting a frame requires + // the buffer to be zeroed on the next call to mutable_data(). Callers + // intending to write to the buffer immediately after Reset() can instead use + // 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, + int sample_rate_hz, + SpeechType speech_type, + VADActivity vad_activity, + size_t num_channels = 1); + + void CopyFrom(const AudioFrame& src); + + // Sets a wall-time clock timestamp in milliseconds to be used for profiling + // of time between two points in the audio chain. + // Example: + // t0: UpdateProfileTimeStamp() + // t1: ElapsedProfileTimeMs() => t1 - t0 [msec] + void UpdateProfileTimeStamp(); + // Returns the time difference between now and when UpdateProfileTimeStamp() + // was last called. Returns -1 if UpdateProfileTimeStamp() has not yet been + // called. + int64_t ElapsedProfileTimeMs() const; + + // data() returns a zeroed static buffer if the frame is muted. + // 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 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 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 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( + int64_t absolute_capture_time_stamp_ms) { + absolute_capture_timestamp_ms_ = absolute_capture_time_stamp_ms; + } + + std::optional 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. + // -1 represents an uninitialized value. + int64_t elapsed_time_ms_ = -1; + // NTP time of the estimated capture time in local timebase in milliseconds. + // -1 represents an uninitialized value. + int64_t ntp_time_ms_ = -1; + size_t samples_per_channel_ = 0; + int sample_rate_hz_ = 0; + size_t num_channels_ = 0; + 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 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 + // MediaStreamTrack, in order to implement getContributingSources(). See: + // https://w3c.github.io/webrtc-pc/#dom-rtcrtpreceiver-getcontributingsources + // + // TODO(bugs.webrtc.org/10757): + // Note that this information might not be fully accurate since we currently + // don't have a proper way to track it across the audio sync buffer. The + // 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 + // 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 zeroed + // buffer per translation unit is to wrap a static in an inline function. + static ArrayView zeroed_data(); + + std::array 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_`. + // This timestamp MUST be based on the same clock as webrtc::TimeMillis(). + std::optional absolute_capture_timestamp_ms_; +}; + +} // namespace webrtc + +#endif // API_AUDIO_AUDIO_FRAME_H_ diff --git a/pkg/apm/webrtc/api/audio/audio_frame_processor.h b/pkg/apm/webrtc/api/audio/audio_frame_processor.h new file mode 100644 index 00000000..cb65c481 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio_frame_processor.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_AUDIO_FRAME_PROCESSOR_H_ +#define API_AUDIO_AUDIO_FRAME_PROCESSOR_H_ + +#include +#include + +namespace webrtc { + +class AudioFrame; + +// If passed into PeerConnectionFactory, will be used for additional +// processing of captured audio frames, performed before encoding. +// Implementations must be thread-safe. +class AudioFrameProcessor { + public: + using OnAudioFrameCallback = std::function)>; + virtual ~AudioFrameProcessor() = default; + + // Processes the frame received from WebRTC, is called by WebRTC off the + // realtime audio capturing path. AudioFrameProcessor must reply with + // processed frames by calling `sink_callback` if it was provided in SetSink() + // call. `sink_callback` can be called in the context of Process(). + virtual void Process(std::unique_ptr frame) = 0; + + // Atomically replaces the current sink with the new one. Before the + // first call to this function, or if the provided `sink_callback` is nullptr, + // processed frames are simply discarded. + virtual void SetSink(OnAudioFrameCallback sink_callback) = 0; +}; + +} // namespace webrtc + +#endif // API_AUDIO_AUDIO_FRAME_PROCESSOR_H_ diff --git a/pkg/apm/webrtc/api/audio/audio_mixer.h b/pkg/apm/webrtc/api/audio/audio_mixer.h new file mode 100644 index 00000000..baf9f55a --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio_mixer.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_AUDIO_MIXER_H_ +#define API_AUDIO_AUDIO_MIXER_H_ + +#include + +#include "api/audio/audio_frame.h" +#include "api/ref_count.h" + +namespace webrtc { + +// WORK IN PROGRESS +// This class is under development and is not yet intended for for use outside +// of WebRtc/Libjingle. +class AudioMixer : public RefCountInterface { + public: + // A callback class that all mixer participants must inherit from/implement. + class Source { + public: + enum class AudioFrameInfo { + kNormal, // The samples in audio_frame are valid and should be used. + kMuted, // The samples in audio_frame should not be used, but + // should be implicitly interpreted as zero. Other + // fields in audio_frame may be read and should + // contain meaningful values. + kError, // The audio_frame will not be used. + }; + + // Overwrites `audio_frame`. The data_ field is overwritten with + // 10 ms of new audio (either 1 or 2 interleaved channels) at + // `sample_rate_hz`. All fields in `audio_frame` must be updated. + virtual AudioFrameInfo GetAudioFrameWithInfo(int sample_rate_hz, + AudioFrame* audio_frame) = 0; + + // A way for a mixer implementation to distinguish participants. + virtual int Ssrc() const = 0; + + // A way for this source to say that GetAudioFrameWithInfo called + // with this sample rate or higher will not cause quality loss. + virtual int PreferredSampleRate() const = 0; + + virtual ~Source() {} + }; + + // Returns true if adding was successful. A source is never added + // twice. Addition and removal can happen on different threads. + virtual bool AddSource(Source* audio_source) = 0; + + // Removal is never attempted if a source has not been successfully + // added to the mixer. + virtual void RemoveSource(Source* audio_source) = 0; + + // Performs mixing by asking registered audio sources for audio. The + // mixed result is placed in the provided AudioFrame. This method + // will only be called from a single thread. The channels argument + // specifies the number of channels of the mix result. The mixer + // should mix at a rate that doesn't cause quality loss of the + // sources' audio. The mixing rate is one of the rates listed in + // AudioProcessing::NativeRate. All fields in + // `audio_frame_for_mixing` must be updated. + virtual void Mix(size_t number_of_channels, + AudioFrame* audio_frame_for_mixing) = 0; + + protected: + // Since the mixer is reference counted, the destructor may be + // called from any thread. + ~AudioMixer() override {} +}; +} // namespace webrtc + +#endif // API_AUDIO_AUDIO_MIXER_H_ diff --git a/pkg/apm/webrtc/api/audio/audio_processing.cc b/pkg/apm/webrtc/api/audio/audio_processing.cc new file mode 100644 index 00000000..2941001c --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio_processing.cc @@ -0,0 +1,238 @@ +/* + * 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 +#include +#include + +#include "absl/base/nullability.h" +#include "api/environment/environment.h" +#include "api/scoped_refptr.h" +#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]; + 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(); +} + +absl_nonnull std::unique_ptr +CustomAudioProcessing( + absl_nonnull scoped_refptr audio_processing) { + class Builder : public AudioProcessingBuilderInterface { + public: + explicit Builder(absl_nonnull scoped_refptr ap) + : ap_(std::move(ap)) {} + + absl_nullable scoped_refptr Build( + const Environment& /*env*/) override { + return std::move(ap_); + } + + private: + absl_nonnull scoped_refptr ap_; + }; + + RTC_CHECK(audio_processing); + return std::make_unique(std::move(audio_processing)); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/audio/audio_processing.h b/pkg/apm/webrtc/api/audio/audio_processing.h new file mode 100644 index 00000000..5ba59bad --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio_processing.h @@ -0,0 +1,898 @@ +/* + * 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 +#include // size_t +#include // FILE +#include + +#include +#include +#include +#include +#include + +#include "absl/base/nullability.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/audio/audio_processing_statistics.h" +#include "api/audio/echo_control.h" +#include "api/environment/environment.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: +// +// 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; +// +// scoped_refptr apm = +// BuiltinAudioProcessingBuilder(config).Build(CreateEnvironment()); +// +// // 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(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; + virtual bool get_output_will_be_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( + ArrayView> 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, + TaskQueueBase* absl_nonnull + worker_queue) = 0; + virtual bool CreateAndAttachAecDump(FILE* absl_nonnull handle, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull + 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 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; } +}; + +class AudioProcessingBuilderInterface { + public: + virtual ~AudioProcessingBuilderInterface() = default; + + virtual absl_nullable scoped_refptr Build( + const Environment& env) = 0; +}; + +// Returns builder that returns the `audio_processing` ignoring the extra +// construction parameter `env`. +// nullptr `audio_processing` is not supported as in some scenarios that imply +// no audio processing, while in others - default builtin audio processing. +// Callers should be explicit which of these two behaviors they want. +absl_nonnull std::unique_ptr +CustomAudioProcessing( + absl_nonnull scoped_refptr audio_processing); + +// 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 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(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(ArrayView render_audio) = 0; + + // Analysis (not changing) of the capture signal. + virtual void AnalyzeCaptureAudio(ArrayView capture_audio) = 0; + + struct Metrics { + std::optional echo_likelihood; + std::optional echo_likelihood_recent_max; + }; + + // Collect current metrics from the echo detector. + virtual Metrics GetMetrics() const = 0; +}; + +} // namespace webrtc + +#endif // API_AUDIO_AUDIO_PROCESSING_H_ diff --git a/pkg/apm/webrtc/api/audio/audio_processing_statistics.cc b/pkg/apm/webrtc/api/audio/audio_processing_statistics.cc new file mode 100644 index 00000000..90da7e88 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio_processing_statistics.cc @@ -0,0 +1,22 @@ +/* + * 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. + */ + +#include "api/audio/audio_processing_statistics.h" + +namespace webrtc { + +AudioProcessingStats::AudioProcessingStats() = default; + +AudioProcessingStats::AudioProcessingStats(const AudioProcessingStats& other) = + default; + +AudioProcessingStats::~AudioProcessingStats() = default; + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/audio/audio_processing_statistics.h b/pkg/apm/webrtc/api/audio/audio_processing_statistics.h new file mode 100644 index 00000000..d6f8b6e6 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio_processing_statistics.h @@ -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 + +#include + +#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 voice_detected; + + // AEC Statistics. + // ERL = 10log_10(P_far / P_echo) + std::optional echo_return_loss; + // ERLE = 10log_10(P_echo / P_out) + std::optional echo_return_loss_enhancement; + // Fraction of time that the AEC linear filter is divergent, in a 1-second + // non-overlapped aggregation window. + std::optional 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 delay_median_ms; + std::optional delay_standard_deviation_ms; + + // Residual echo detector likelihood. + std::optional residual_echo_likelihood; + // Maximum residual echo likelihood from the last time period. + std::optional 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 delay_ms; +}; + +} // namespace webrtc + +#endif // API_AUDIO_AUDIO_PROCESSING_STATISTICS_H_ diff --git a/pkg/apm/webrtc/api/audio/audio_view.h b/pkg/apm/webrtc/api/audio/audio_view.h new file mode 100644 index 00000000..719d6089 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/audio_view.h @@ -0,0 +1,272 @@ +/* + * 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 +#include + +#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 webrtc::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 +using MonoView = ArrayView; + +// InterleavedView<> is a view over an interleaved audio buffer (e.g. from +// AudioFrame). +template +class InterleavedView { + public: + using value_type = T; + + InterleavedView() = default; + + template + 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 + InterleavedView(U (&array)[N], // NOLINT + size_t num_channels) + : InterleavedView(array, N / num_channels, num_channels) { + RTC_DCHECK_EQ(N % num_channels, 0u); + } + + template + InterleavedView(const InterleavedView& 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_; } + ArrayView data() const { return data_; } + bool empty() const { return data_.empty(); } + size_t size() const { return data_.size(); } + + MonoView 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 + void CopyFrom(const InterleavedView& 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 rbegin() const { return data_.rbegin(); } + std::reverse_iterator rend() const { return data_.rend(); } + std::reverse_iterator crbegin() const { return data_.crbegin(); } + std::reverse_iterator crend() const { return data_.crend(); } + + 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; + ArrayView data_; +}; + +template +class DeinterleavedView { + public: + using value_type = T; + + DeinterleavedView() = default; + + template + 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 + DeinterleavedView(const DeinterleavedView& 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 operator[](size_t idx) const { + RTC_DCHECK_LT(idx, num_channels_); + return MonoView(&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_; } + ArrayView 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 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; + ArrayView data_; +}; + +template +constexpr size_t NumChannels(const MonoView& /* view */) { + return 1u; +} + +template +size_t NumChannels(const InterleavedView& view) { + return view.num_channels(); +} + +template +size_t NumChannels(const DeinterleavedView& view) { + return view.num_channels(); +} + +template +constexpr bool IsMono(const MonoView& /* view */) { + return true; +} + +template +constexpr bool IsInterleavedView(const MonoView& /* view */) { + return true; +} + +template +constexpr bool IsInterleavedView(const InterleavedView& /* view */) { + return true; +} + +template +constexpr bool IsInterleavedView(const DeinterleavedView& /* view */) { + return false; +} + +template +bool IsMono(const InterleavedView& view) { + return NumChannels(view) == 1u; +} + +template +bool IsMono(const DeinterleavedView& view) { + return NumChannels(view) == 1u; +} + +template +size_t SamplesPerChannel(const MonoView& view) { + return view.size(); +} + +template +size_t SamplesPerChannel(const InterleavedView& view) { + return view.samples_per_channel(); +} + +template +size_t SamplesPerChannel(const DeinterleavedView& 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 +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 +void ClearSamples(T& view) { + memset(&view[0], 0, view.size() * sizeof(typename T::value_type)); +} + +// Same as `ClearSamples()` above but allows for clearing only the first +// `sample_count` number of samples. +template +void ClearSamples(T& view, size_t sample_count) { + RTC_DCHECK_LE(sample_count, view.size()); + memset(&view[0], 0, sample_count * sizeof(typename T::value_type)); +} + +} // namespace webrtc + +#endif // API_AUDIO_AUDIO_VIEW_H_ diff --git a/pkg/apm/webrtc/api/audio/builtin_audio_processing_builder.cc b/pkg/apm/webrtc/api/audio/builtin_audio_processing_builder.cc new file mode 100644 index 00000000..50c53413 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/builtin_audio_processing_builder.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "api/audio/builtin_audio_processing_builder.h" + +#include + +#include "absl/base/nullability.h" +#include "api/audio/audio_processing.h" +#include "api/environment/environment.h" +#include "api/make_ref_counted.h" +#include "api/scoped_refptr.h" +#include "modules/audio_processing/audio_processing_impl.h" + +namespace webrtc { + +absl_nullable scoped_refptr +BuiltinAudioProcessingBuilder::Build(const Environment& env) { + return make_ref_counted( + env, config_, std::move(capture_post_processing_), + std::move(render_pre_processing_), std::move(echo_control_factory_), + std::move(echo_detector_), std::move(capture_analyzer_)); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/audio/builtin_audio_processing_builder.h b/pkg/apm/webrtc/api/audio/builtin_audio_processing_builder.h new file mode 100644 index 00000000..eec0a06a --- /dev/null +++ b/pkg/apm/webrtc/api/audio/builtin_audio_processing_builder.h @@ -0,0 +1,96 @@ +/* + * 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_BUILTIN_AUDIO_PROCESSING_BUILDER_H_ +#define API_AUDIO_BUILTIN_AUDIO_PROCESSING_BUILDER_H_ + +#include +#include + +#include "absl/base/nullability.h" +#include "api/audio/audio_processing.h" +#include "api/audio/echo_control.h" +#include "api/environment/environment.h" +#include "api/scoped_refptr.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +class RTC_EXPORT BuiltinAudioProcessingBuilder + : public AudioProcessingBuilderInterface { + public: + BuiltinAudioProcessingBuilder() = default; + explicit BuiltinAudioProcessingBuilder(const AudioProcessing::Config& config) + : config_(config) {} + BuiltinAudioProcessingBuilder(const BuiltinAudioProcessingBuilder&) = delete; + BuiltinAudioProcessingBuilder& operator=( + const BuiltinAudioProcessingBuilder&) = delete; + ~BuiltinAudioProcessingBuilder() override = default; + + // Sets the APM configuration. + BuiltinAudioProcessingBuilder& SetConfig( + const AudioProcessing::Config& config) { + config_ = config; + return *this; + } + + // Sets the echo controller factory to inject when APM is created. + BuiltinAudioProcessingBuilder& SetEchoControlFactory( + std::unique_ptr 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. + BuiltinAudioProcessingBuilder& SetCapturePostProcessing( + std::unique_ptr 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. + BuiltinAudioProcessingBuilder& SetRenderPreProcessing( + std::unique_ptr render_pre_processing) { + render_pre_processing_ = std::move(render_pre_processing); + return *this; + } + + // Sets the echo detector to inject when APM is created. + BuiltinAudioProcessingBuilder& SetEchoDetector( + scoped_refptr echo_detector) { + echo_detector_ = std::move(echo_detector); + return *this; + } + + // Sets the capture analyzer sub-module to inject when APM is created. + BuiltinAudioProcessingBuilder& SetCaptureAnalyzer( + std::unique_ptr 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. + absl_nullable scoped_refptr Build( + const Environment& env) override; + + private: + AudioProcessing::Config config_; + std::unique_ptr echo_control_factory_; + std::unique_ptr capture_post_processing_; + std::unique_ptr render_pre_processing_; + scoped_refptr echo_detector_; + std::unique_ptr capture_analyzer_; +}; + +} // namespace webrtc + +#endif // API_AUDIO_BUILTIN_AUDIO_PROCESSING_BUILDER_H_ diff --git a/pkg/apm/webrtc/api/audio/channel_layout.cc b/pkg/apm/webrtc/api/audio/channel_layout.cc new file mode 100644 index 00000000..e4ae356f --- /dev/null +++ b/pkg/apm/webrtc/api/audio/channel_layout.cc @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "api/audio/channel_layout.h" + +#include + +#include "rtc_base/arraysize.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +static const int kLayoutToChannels[] = { + 0, // CHANNEL_LAYOUT_NONE + 0, // CHANNEL_LAYOUT_UNSUPPORTED + 1, // CHANNEL_LAYOUT_MONO + 2, // CHANNEL_LAYOUT_STEREO + 3, // CHANNEL_LAYOUT_2_1 + 3, // CHANNEL_LAYOUT_SURROUND + 4, // CHANNEL_LAYOUT_4_0 + 4, // CHANNEL_LAYOUT_2_2 + 4, // CHANNEL_LAYOUT_QUAD + 5, // CHANNEL_LAYOUT_5_0 + 6, // CHANNEL_LAYOUT_5_1 + 5, // CHANNEL_LAYOUT_5_0_BACK + 6, // CHANNEL_LAYOUT_5_1_BACK + 7, // CHANNEL_LAYOUT_7_0 + 8, // CHANNEL_LAYOUT_7_1 + 8, // CHANNEL_LAYOUT_7_1_WIDE + 2, // CHANNEL_LAYOUT_STEREO_DOWNMIX + 3, // CHANNEL_LAYOUT_2POINT1 + 4, // CHANNEL_LAYOUT_3_1 + 5, // CHANNEL_LAYOUT_4_1 + 6, // CHANNEL_LAYOUT_6_0 + 6, // CHANNEL_LAYOUT_6_0_FRONT + 6, // CHANNEL_LAYOUT_HEXAGONAL + 7, // CHANNEL_LAYOUT_6_1 + 7, // CHANNEL_LAYOUT_6_1_BACK + 7, // CHANNEL_LAYOUT_6_1_FRONT + 7, // CHANNEL_LAYOUT_7_0_FRONT + 8, // CHANNEL_LAYOUT_7_1_WIDE_BACK + 8, // CHANNEL_LAYOUT_OCTAGONAL + 0, // CHANNEL_LAYOUT_DISCRETE + 3, // CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC + 5, // CHANNEL_LAYOUT_4_1_QUAD_SIDE + 0, // CHANNEL_LAYOUT_BITSTREAM +}; + +// The channel orderings for each layout as specified by FFmpeg. Each value +// represents the index of each channel in each layout. Values of -1 mean the +// channel at that index is not used for that layout. For example, the left side +// surround sound channel in FFmpeg's 5.1 layout is in the 5th position (because +// the order is L, R, C, LFE, LS, RS), so +// kChannelOrderings[CHANNEL_LAYOUT_5_1][SIDE_LEFT] = 4; +static const int kChannelOrderings[CHANNEL_LAYOUT_MAX + 1][CHANNELS_MAX + 1] = { + // FL | FR | FC | LFE | BL | BR | FLofC | FRofC | BC | SL | SR + + // CHANNEL_LAYOUT_NONE + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_UNSUPPORTED + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_MONO + {-1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_STEREO + {0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_2_1 + {0, 1, -1, -1, -1, -1, -1, -1, 2, -1, -1}, + + // CHANNEL_LAYOUT_SURROUND + {0, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_4_0 + {0, 1, 2, -1, -1, -1, -1, -1, 3, -1, -1}, + + // CHANNEL_LAYOUT_2_2 + {0, 1, -1, -1, -1, -1, -1, -1, -1, 2, 3}, + + // CHANNEL_LAYOUT_QUAD + {0, 1, -1, -1, 2, 3, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_5_0 + {0, 1, 2, -1, -1, -1, -1, -1, -1, 3, 4}, + + // CHANNEL_LAYOUT_5_1 + {0, 1, 2, 3, -1, -1, -1, -1, -1, 4, 5}, + + // FL | FR | FC | LFE | BL | BR | FLofC | FRofC | BC | SL | SR + + // CHANNEL_LAYOUT_5_0_BACK + {0, 1, 2, -1, 3, 4, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_5_1_BACK + {0, 1, 2, 3, 4, 5, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_7_0 + {0, 1, 2, -1, 5, 6, -1, -1, -1, 3, 4}, + + // CHANNEL_LAYOUT_7_1 + {0, 1, 2, 3, 6, 7, -1, -1, -1, 4, 5}, + + // CHANNEL_LAYOUT_7_1_WIDE + {0, 1, 2, 3, -1, -1, 6, 7, -1, 4, 5}, + + // CHANNEL_LAYOUT_STEREO_DOWNMIX + {0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_2POINT1 + {0, 1, -1, 2, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_3_1 + {0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_4_1 + {0, 1, 2, 4, -1, -1, -1, -1, 3, -1, -1}, + + // CHANNEL_LAYOUT_6_0 + {0, 1, 2, -1, -1, -1, -1, -1, 5, 3, 4}, + + // CHANNEL_LAYOUT_6_0_FRONT + {0, 1, -1, -1, -1, -1, 4, 5, -1, 2, 3}, + + // FL | FR | FC | LFE | BL | BR | FLofC | FRofC | BC | SL | SR + + // CHANNEL_LAYOUT_HEXAGONAL + {0, 1, 2, -1, 3, 4, -1, -1, 5, -1, -1}, + + // CHANNEL_LAYOUT_6_1 + {0, 1, 2, 3, -1, -1, -1, -1, 6, 4, 5}, + + // CHANNEL_LAYOUT_6_1_BACK + {0, 1, 2, 3, 4, 5, -1, -1, 6, -1, -1}, + + // CHANNEL_LAYOUT_6_1_FRONT + {0, 1, -1, 6, -1, -1, 4, 5, -1, 2, 3}, + + // CHANNEL_LAYOUT_7_0_FRONT + {0, 1, 2, -1, -1, -1, 5, 6, -1, 3, 4}, + + // CHANNEL_LAYOUT_7_1_WIDE_BACK + {0, 1, 2, 3, 4, 5, 6, 7, -1, -1, -1}, + + // CHANNEL_LAYOUT_OCTAGONAL + {0, 1, 2, -1, 5, 6, -1, -1, 7, 3, 4}, + + // CHANNEL_LAYOUT_DISCRETE + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC + {0, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_4_1_QUAD_SIDE + {0, 1, -1, 4, -1, -1, -1, -1, -1, 2, 3}, + + // CHANNEL_LAYOUT_BITSTREAM + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // FL | FR | FC | LFE | BL | BR | FLofC | FRofC | BC | SL | SR +}; + +int ChannelLayoutToChannelCount(ChannelLayout layout) { + RTC_DCHECK_LT(static_cast(layout), arraysize(kLayoutToChannels)); + RTC_DCHECK_LE(kLayoutToChannels[layout], kMaxConcurrentChannels); + return kLayoutToChannels[layout]; +} + +// Converts a channel count into a channel layout. +ChannelLayout GuessChannelLayout(int channels) { + switch (channels) { + case 1: + return CHANNEL_LAYOUT_MONO; + case 2: + return CHANNEL_LAYOUT_STEREO; + case 3: + return CHANNEL_LAYOUT_SURROUND; + case 4: + return CHANNEL_LAYOUT_QUAD; + case 5: + return CHANNEL_LAYOUT_5_0; + case 6: + return CHANNEL_LAYOUT_5_1; + case 7: + return CHANNEL_LAYOUT_6_1; + case 8: + return CHANNEL_LAYOUT_7_1; + default: + RTC_DLOG(LS_WARNING) << "Unsupported channel count: " << channels; + } + return CHANNEL_LAYOUT_UNSUPPORTED; +} + +int ChannelOrder(ChannelLayout layout, Channels channel) { + RTC_DCHECK_LT(static_cast(layout), arraysize(kChannelOrderings)); + RTC_DCHECK_LT(static_cast(channel), arraysize(kChannelOrderings[0])); + return kChannelOrderings[layout][channel]; +} + +const char* ChannelLayoutToString(ChannelLayout layout) { + switch (layout) { + case CHANNEL_LAYOUT_NONE: + return "NONE"; + case CHANNEL_LAYOUT_UNSUPPORTED: + return "UNSUPPORTED"; + case CHANNEL_LAYOUT_MONO: + return "MONO"; + case CHANNEL_LAYOUT_STEREO: + return "STEREO"; + case CHANNEL_LAYOUT_2_1: + return "2.1"; + case CHANNEL_LAYOUT_SURROUND: + return "SURROUND"; + case CHANNEL_LAYOUT_4_0: + return "4.0"; + case CHANNEL_LAYOUT_2_2: + return "QUAD_SIDE"; + case CHANNEL_LAYOUT_QUAD: + return "QUAD"; + case CHANNEL_LAYOUT_5_0: + return "5.0"; + case CHANNEL_LAYOUT_5_1: + return "5.1"; + case CHANNEL_LAYOUT_5_0_BACK: + return "5.0_BACK"; + case CHANNEL_LAYOUT_5_1_BACK: + return "5.1_BACK"; + case CHANNEL_LAYOUT_7_0: + return "7.0"; + case CHANNEL_LAYOUT_7_1: + return "7.1"; + case CHANNEL_LAYOUT_7_1_WIDE: + return "7.1_WIDE"; + case CHANNEL_LAYOUT_STEREO_DOWNMIX: + return "STEREO_DOWNMIX"; + case CHANNEL_LAYOUT_2POINT1: + return "2POINT1"; + case CHANNEL_LAYOUT_3_1: + return "3.1"; + case CHANNEL_LAYOUT_4_1: + return "4.1"; + case CHANNEL_LAYOUT_6_0: + return "6.0"; + case CHANNEL_LAYOUT_6_0_FRONT: + return "6.0_FRONT"; + case CHANNEL_LAYOUT_HEXAGONAL: + return "HEXAGONAL"; + case CHANNEL_LAYOUT_6_1: + return "6.1"; + case CHANNEL_LAYOUT_6_1_BACK: + return "6.1_BACK"; + case CHANNEL_LAYOUT_6_1_FRONT: + return "6.1_FRONT"; + case CHANNEL_LAYOUT_7_0_FRONT: + return "7.0_FRONT"; + case CHANNEL_LAYOUT_7_1_WIDE_BACK: + return "7.1_WIDE_BACK"; + case CHANNEL_LAYOUT_OCTAGONAL: + return "OCTAGONAL"; + case CHANNEL_LAYOUT_DISCRETE: + return "DISCRETE"; + case CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC: + return "STEREO_AND_KEYBOARD_MIC"; + case CHANNEL_LAYOUT_4_1_QUAD_SIDE: + return "4.1_QUAD_SIDE"; + case CHANNEL_LAYOUT_BITSTREAM: + return "BITSTREAM"; + } + RTC_DCHECK_NOTREACHED() << "Invalid channel layout provided: " << layout; + return ""; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/audio/channel_layout.h b/pkg/apm/webrtc/api/audio/channel_layout.h new file mode 100644 index 00000000..175aee71 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/channel_layout.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CHANNEL_LAYOUT_H_ +#define API_AUDIO_CHANNEL_LAYOUT_H_ + +namespace webrtc { + +// This file is derived from Chromium's base/channel_layout.h. + +// Enumerates the various representations of the ordering of audio channels. +// Logged to UMA, so never reuse a value, always add new/greater ones! +enum ChannelLayout { + CHANNEL_LAYOUT_NONE = 0, + CHANNEL_LAYOUT_UNSUPPORTED = 1, + + // Front C + CHANNEL_LAYOUT_MONO = 2, + + // Front L, Front R + CHANNEL_LAYOUT_STEREO = 3, + + // Front L, Front R, Back C + CHANNEL_LAYOUT_2_1 = 4, + + // Front L, Front R, Front C + CHANNEL_LAYOUT_SURROUND = 5, + + // Front L, Front R, Front C, Back C + CHANNEL_LAYOUT_4_0 = 6, + + // Front L, Front R, Side L, Side R + CHANNEL_LAYOUT_2_2 = 7, + + // Front L, Front R, Back L, Back R + CHANNEL_LAYOUT_QUAD = 8, + + // Front L, Front R, Front C, Side L, Side R + CHANNEL_LAYOUT_5_0 = 9, + + // Front L, Front R, Front C, LFE, Side L, Side R + CHANNEL_LAYOUT_5_1 = 10, + + // Front L, Front R, Front C, Back L, Back R + CHANNEL_LAYOUT_5_0_BACK = 11, + + // Front L, Front R, Front C, LFE, Back L, Back R + CHANNEL_LAYOUT_5_1_BACK = 12, + + // Front L, Front R, Front C, Side L, Side R, Back L, Back R + CHANNEL_LAYOUT_7_0 = 13, + + // Front L, Front R, Front C, LFE, Side L, Side R, Back L, Back R + CHANNEL_LAYOUT_7_1 = 14, + + // Front L, Front R, Front C, LFE, Side L, Side R, Front LofC, Front RofC + CHANNEL_LAYOUT_7_1_WIDE = 15, + + // Stereo L, Stereo R + CHANNEL_LAYOUT_STEREO_DOWNMIX = 16, + + // Stereo L, Stereo R, LFE + CHANNEL_LAYOUT_2POINT1 = 17, + + // Stereo L, Stereo R, Front C, LFE + CHANNEL_LAYOUT_3_1 = 18, + + // Stereo L, Stereo R, Front C, Rear C, LFE + CHANNEL_LAYOUT_4_1 = 19, + + // Stereo L, Stereo R, Front C, Side L, Side R, Back C + CHANNEL_LAYOUT_6_0 = 20, + + // Stereo L, Stereo R, Side L, Side R, Front LofC, Front RofC + CHANNEL_LAYOUT_6_0_FRONT = 21, + + // Stereo L, Stereo R, Front C, Rear L, Rear R, Rear C + CHANNEL_LAYOUT_HEXAGONAL = 22, + + // Stereo L, Stereo R, Front C, LFE, Side L, Side R, Rear Center + CHANNEL_LAYOUT_6_1 = 23, + + // Stereo L, Stereo R, Front C, LFE, Back L, Back R, Rear Center + CHANNEL_LAYOUT_6_1_BACK = 24, + + // Stereo L, Stereo R, Side L, Side R, Front LofC, Front RofC, LFE + CHANNEL_LAYOUT_6_1_FRONT = 25, + + // Front L, Front R, Front C, Side L, Side R, Front LofC, Front RofC + CHANNEL_LAYOUT_7_0_FRONT = 26, + + // Front L, Front R, Front C, LFE, Back L, Back R, Front LofC, Front RofC + CHANNEL_LAYOUT_7_1_WIDE_BACK = 27, + + // Front L, Front R, Front C, Side L, Side R, Rear L, Back R, Back C. + CHANNEL_LAYOUT_OCTAGONAL = 28, + + // Channels are not explicitly mapped to speakers. + CHANNEL_LAYOUT_DISCRETE = 29, + + // Front L, Front R, Front C. Front C contains the keyboard mic audio. This + // layout is only intended for input for WebRTC. The Front C channel + // is stripped away in the WebRTC audio input pipeline and never seen outside + // of that. + CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC = 30, + + // Front L, Front R, Side L, Side R, LFE + CHANNEL_LAYOUT_4_1_QUAD_SIDE = 31, + + // Actual channel layout is specified in the bitstream and the actual channel + // count is unknown at Chromium media pipeline level (useful for audio + // pass-through mode). + CHANNEL_LAYOUT_BITSTREAM = 32, + + // Max value, must always equal the largest entry ever logged. + CHANNEL_LAYOUT_MAX = CHANNEL_LAYOUT_BITSTREAM +}; + +// Note: Do not reorder or reassign these values; other code depends on their +// ordering to operate correctly. E.g., CoreAudio channel layout computations. +enum Channels { + LEFT = 0, + RIGHT, + CENTER, + LFE, + BACK_LEFT, + BACK_RIGHT, + LEFT_OF_CENTER, + RIGHT_OF_CENTER, + BACK_CENTER, + SIDE_LEFT, + SIDE_RIGHT, + CHANNELS_MAX = + SIDE_RIGHT, // Must always equal the largest value ever logged. +}; + +// The maximum number of concurrently active channels for all possible layouts. +// ChannelLayoutToChannelCount() will never return a value higher than this. +constexpr int kMaxConcurrentChannels = 8; + +// Returns the expected channel position in an interleaved stream. Values of -1 +// mean the channel at that index is not used for that layout. Values range +// from 0 to ChannelLayoutToChannelCount(layout) - 1. +int ChannelOrder(ChannelLayout layout, Channels channel); + +// Returns the number of channels in a given ChannelLayout. +int ChannelLayoutToChannelCount(ChannelLayout layout); + +// Given the number of channels, return the best layout, +// or return CHANNEL_LAYOUT_UNSUPPORTED if there is no good match. +ChannelLayout GuessChannelLayout(int channels); + +// Returns a string representation of the channel layout. +const char* ChannelLayoutToString(ChannelLayout layout); + +} // namespace webrtc + +#endif // API_AUDIO_CHANNEL_LAYOUT_H_ diff --git a/pkg/apm/webrtc/api/audio/echo_canceller3_config.cc b/pkg/apm/webrtc/api/audio/echo_canceller3_config.cc new file mode 100644 index 00000000..973e9a7b --- /dev/null +++ b/pkg/apm/webrtc/api/audio/echo_canceller3_config.cc @@ -0,0 +1,295 @@ +/* + * 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/audio/echo_canceller3_config.h" + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { +bool Limit(float* value, float min, float max) { + float clamped = SafeClamp(*value, min, max); + clamped = std::isfinite(clamped) ? clamped : min; + bool res = *value == clamped; + *value = clamped; + return res; +} + +bool Limit(size_t* value, size_t min, size_t max) { + size_t clamped = SafeClamp(*value, min, max); + bool res = *value == clamped; + *value = clamped; + return res; +} + +bool Limit(int* value, int min, int max) { + int clamped = SafeClamp(*value, min, max); + bool res = *value == clamped; + *value = clamped; + return res; +} + +bool FloorLimit(size_t* value, size_t min) { + size_t clamped = *value >= min ? *value : min; + bool res = *value == clamped; + *value = clamped; + return res; +} + +} // namespace + +EchoCanceller3Config::EchoCanceller3Config() = default; +EchoCanceller3Config::EchoCanceller3Config(const EchoCanceller3Config& e) = + default; +EchoCanceller3Config& EchoCanceller3Config::operator=( + const EchoCanceller3Config& e) = default; +EchoCanceller3Config::Delay::Delay() = default; +EchoCanceller3Config::Delay::Delay(const EchoCanceller3Config::Delay& e) = + default; +EchoCanceller3Config::Delay& EchoCanceller3Config::Delay::operator=( + const Delay& e) = default; + +EchoCanceller3Config::EchoModel::EchoModel() = default; +EchoCanceller3Config::EchoModel::EchoModel( + const EchoCanceller3Config::EchoModel& e) = default; +EchoCanceller3Config::EchoModel& EchoCanceller3Config::EchoModel::operator=( + const EchoModel& e) = default; + +EchoCanceller3Config::Suppressor::Suppressor() = default; +EchoCanceller3Config::Suppressor::Suppressor( + const EchoCanceller3Config::Suppressor& e) = default; +EchoCanceller3Config::Suppressor& EchoCanceller3Config::Suppressor::operator=( + const Suppressor& e) = default; + +EchoCanceller3Config::Suppressor::MaskingThresholds::MaskingThresholds( + float enr_transparent, + float enr_suppress, + float emr_transparent) + : enr_transparent(enr_transparent), + enr_suppress(enr_suppress), + emr_transparent(emr_transparent) {} +EchoCanceller3Config::Suppressor::MaskingThresholds::MaskingThresholds( + const EchoCanceller3Config::Suppressor::MaskingThresholds& e) = default; +EchoCanceller3Config::Suppressor::MaskingThresholds& +EchoCanceller3Config::Suppressor::MaskingThresholds::operator=( + const MaskingThresholds& e) = default; + +EchoCanceller3Config::Suppressor::Tuning::Tuning(MaskingThresholds mask_lf, + MaskingThresholds mask_hf, + float max_inc_factor, + float max_dec_factor_lf) + : mask_lf(mask_lf), + mask_hf(mask_hf), + max_inc_factor(max_inc_factor), + max_dec_factor_lf(max_dec_factor_lf) {} +EchoCanceller3Config::Suppressor::Tuning::Tuning( + const EchoCanceller3Config::Suppressor::Tuning& e) = default; +EchoCanceller3Config::Suppressor::Tuning& +EchoCanceller3Config::Suppressor::Tuning::operator=(const Tuning& e) = default; + +bool EchoCanceller3Config::Validate(EchoCanceller3Config* config) { + RTC_DCHECK(config); + EchoCanceller3Config* c = config; + bool res = true; + + if (c->delay.down_sampling_factor != 4 && + c->delay.down_sampling_factor != 8) { + c->delay.down_sampling_factor = 4; + res = false; + } + + res = res & Limit(&c->delay.default_delay, 0, 5000); + res = res & Limit(&c->delay.num_filters, 0, 5000); + res = res & Limit(&c->delay.delay_headroom_samples, 0, 5000); + res = res & Limit(&c->delay.hysteresis_limit_blocks, 0, 5000); + res = res & Limit(&c->delay.fixed_capture_delay_samples, 0, 5000); + res = res & Limit(&c->delay.delay_estimate_smoothing, 0.f, 1.f); + res = res & Limit(&c->delay.delay_candidate_detection_threshold, 0.f, 1.f); + res = res & Limit(&c->delay.delay_selection_thresholds.initial, 1, 250); + res = res & Limit(&c->delay.delay_selection_thresholds.converged, 1, 250); + + res = res & FloorLimit(&c->filter.refined.length_blocks, 1); + res = res & Limit(&c->filter.refined.leakage_converged, 0.f, 1000.f); + res = res & Limit(&c->filter.refined.leakage_diverged, 0.f, 1000.f); + res = res & Limit(&c->filter.refined.error_floor, 0.f, 1000.f); + res = res & Limit(&c->filter.refined.error_ceil, 0.f, 100000000.f); + res = res & Limit(&c->filter.refined.noise_gate, 0.f, 100000000.f); + + res = res & FloorLimit(&c->filter.refined_initial.length_blocks, 1); + res = res & Limit(&c->filter.refined_initial.leakage_converged, 0.f, 1000.f); + res = res & Limit(&c->filter.refined_initial.leakage_diverged, 0.f, 1000.f); + res = res & Limit(&c->filter.refined_initial.error_floor, 0.f, 1000.f); + res = res & Limit(&c->filter.refined_initial.error_ceil, 0.f, 100000000.f); + res = res & Limit(&c->filter.refined_initial.noise_gate, 0.f, 100000000.f); + + if (c->filter.refined.length_blocks < + c->filter.refined_initial.length_blocks) { + c->filter.refined_initial.length_blocks = c->filter.refined.length_blocks; + res = false; + } + + res = res & FloorLimit(&c->filter.coarse.length_blocks, 1); + res = res & Limit(&c->filter.coarse.rate, 0.f, 1.f); + res = res & Limit(&c->filter.coarse.noise_gate, 0.f, 100000000.f); + + res = res & FloorLimit(&c->filter.coarse_initial.length_blocks, 1); + res = res & Limit(&c->filter.coarse_initial.rate, 0.f, 1.f); + res = res & Limit(&c->filter.coarse_initial.noise_gate, 0.f, 100000000.f); + + if (c->filter.coarse.length_blocks < c->filter.coarse_initial.length_blocks) { + c->filter.coarse_initial.length_blocks = c->filter.coarse.length_blocks; + res = false; + } + + 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); + res = res & Limit(&c->erle.max_h, 1.f, 100000.f); + if (c->erle.min > c->erle.max_l || c->erle.min > c->erle.max_h) { + c->erle.min = std::min(c->erle.max_l, c->erle.max_h); + res = false; + } + res = res & Limit(&c->erle.num_sections, 1, c->filter.refined.length_blocks); + + 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); + res = res & + Limit(&c->echo_audibility.normal_render_limit, 0.f, 32768.f * 32768.f); + res = res & Limit(&c->echo_audibility.floor_power, 0.f, 32768.f * 32768.f); + res = res & Limit(&c->echo_audibility.audibility_threshold_lf, 0.f, + 32768.f * 32768.f); + res = res & Limit(&c->echo_audibility.audibility_threshold_mf, 0.f, + 32768.f * 32768.f); + res = res & Limit(&c->echo_audibility.audibility_threshold_hf, 0.f, + 32768.f * 32768.f); + + res = res & + Limit(&c->render_levels.active_render_limit, 0.f, 32768.f * 32768.f); + res = res & Limit(&c->render_levels.poor_excitation_render_limit, 0.f, + 32768.f * 32768.f); + res = res & Limit(&c->render_levels.poor_excitation_render_limit_ds8, 0.f, + 32768.f * 32768.f); + + res = res & Limit(&c->echo_model.noise_floor_hold, 0, 1000); + res = res & Limit(&c->echo_model.min_noise_floor_power, 0, 2000000.f); + res = res & Limit(&c->echo_model.stationary_gate_slope, 0, 1000000.f); + res = res & Limit(&c->echo_model.noise_gate_power, 0, 1000000.f); + res = res & Limit(&c->echo_model.noise_gate_slope, 0, 1000000.f); + res = res & Limit(&c->echo_model.render_pre_window_size, 0, 100); + res = res & Limit(&c->echo_model.render_post_window_size, 0, 100); + + res = res & Limit(&c->comfort_noise.noise_floor_dbfs, -200.f, 0.f); + + res = res & Limit(&c->suppressor.nearend_average_blocks, 1, 5000); + + res = res & + Limit(&c->suppressor.normal_tuning.mask_lf.enr_transparent, 0.f, 100.f); + res = res & + Limit(&c->suppressor.normal_tuning.mask_lf.enr_suppress, 0.f, 100.f); + res = res & + Limit(&c->suppressor.normal_tuning.mask_lf.emr_transparent, 0.f, 100.f); + res = res & + Limit(&c->suppressor.normal_tuning.mask_hf.enr_transparent, 0.f, 100.f); + res = res & + Limit(&c->suppressor.normal_tuning.mask_hf.enr_suppress, 0.f, 100.f); + res = res & + Limit(&c->suppressor.normal_tuning.mask_hf.emr_transparent, 0.f, 100.f); + res = res & Limit(&c->suppressor.normal_tuning.max_inc_factor, 0.f, 100.f); + res = res & Limit(&c->suppressor.normal_tuning.max_dec_factor_lf, 0.f, 100.f); + + res = res & Limit(&c->suppressor.nearend_tuning.mask_lf.enr_transparent, 0.f, + 100.f); + res = res & + Limit(&c->suppressor.nearend_tuning.mask_lf.enr_suppress, 0.f, 100.f); + res = res & Limit(&c->suppressor.nearend_tuning.mask_lf.emr_transparent, 0.f, + 100.f); + res = res & Limit(&c->suppressor.nearend_tuning.mask_hf.enr_transparent, 0.f, + 100.f); + res = res & + Limit(&c->suppressor.nearend_tuning.mask_hf.enr_suppress, 0.f, 100.f); + res = res & Limit(&c->suppressor.nearend_tuning.mask_hf.emr_transparent, 0.f, + 100.f); + res = res & Limit(&c->suppressor.nearend_tuning.max_inc_factor, 0.f, 100.f); + 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, + 0.f, 1000000.f); + res = res & Limit(&c->suppressor.dominant_nearend_detection.hold_duration, 0, + 10000); + res = res & Limit(&c->suppressor.dominant_nearend_detection.trigger_threshold, + 0, 10000); + + res = res & + Limit(&c->suppressor.subband_nearend_detection.nearend_average_blocks, + 1, 1024); + res = + res & Limit(&c->suppressor.subband_nearend_detection.subband1.low, 0, 65); + res = res & Limit(&c->suppressor.subband_nearend_detection.subband1.high, + c->suppressor.subband_nearend_detection.subband1.low, 65); + res = + res & Limit(&c->suppressor.subband_nearend_detection.subband2.low, 0, 65); + res = res & Limit(&c->suppressor.subband_nearend_detection.subband2.high, + c->suppressor.subband_nearend_detection.subband2.low, 65); + res = res & Limit(&c->suppressor.subband_nearend_detection.nearend_threshold, + 0.f, 1.e24f); + res = res & Limit(&c->suppressor.subband_nearend_detection.snr_threshold, 0.f, + 1.e24f); + + res = res & Limit(&c->suppressor.high_bands_suppression.enr_threshold, 0.f, + 1000000.f); + res = res & Limit(&c->suppressor.high_bands_suppression.max_gain_during_echo, + 0.f, 1.f); + res = res & Limit(&c->suppressor.high_bands_suppression + .anti_howling_activation_threshold, + 0.f, 32768.f * 32768.f); + res = res & Limit(&c->suppressor.high_bands_suppression.anti_howling_gain, + 0.f, 1.f); + + res = res & Limit(&c->suppressor.floor_first_increase, 0.f, 1000000.f); + + return res; +} + +EchoCanceller3Config EchoCanceller3Config::CreateDefaultMultichannelConfig() { + EchoCanceller3Config cfg; + // Use shorter and more rapidly adapting coarse filter to compensate for + // the increased number of total filter parameters to adapt. + cfg.filter.coarse.length_blocks = 11; + cfg.filter.coarse.rate = 0.95f; + cfg.filter.coarse_initial.length_blocks = 11; + cfg.filter.coarse_initial.rate = 0.95f; + + // Use more conservative suppressor behavior for non-nearend speech. + cfg.suppressor.normal_tuning.max_dec_factor_lf = 0.35f; + cfg.suppressor.normal_tuning.max_inc_factor = 1.5f; + return cfg; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/audio/echo_canceller3_config.h b/pkg/apm/webrtc/api/audio/echo_canceller3_config.h new file mode 100644 index 00000000..66681deb --- /dev/null +++ b/pkg/apm/webrtc/api/audio/echo_canceller3_config.h @@ -0,0 +1,253 @@ +/* + * 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_AUDIO_ECHO_CANCELLER3_CONFIG_H_ +#define API_AUDIO_ECHO_CANCELLER3_CONFIG_H_ + +#include // size_t + +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Configuration struct for EchoCanceller3 +struct RTC_EXPORT EchoCanceller3Config { + // Checks and updates the config parameters to lie within (mostly) reasonable + // ranges. Returns true if and only of the config did not need to be changed. + static bool Validate(EchoCanceller3Config* config); + + // Produces a default configuration for multichannel. + static EchoCanceller3Config CreateDefaultMultichannelConfig(); + + EchoCanceller3Config(); + EchoCanceller3Config(const EchoCanceller3Config& e); + EchoCanceller3Config& operator=(const EchoCanceller3Config& other); + + struct Buffering { + size_t excess_render_detection_interval_blocks = 250; + size_t max_allowed_excess_render_blocks = 8; + } buffering; + + struct Delay { + Delay(); + Delay(const Delay& e); + Delay& operator=(const Delay& e); + size_t default_delay = 5; + size_t down_sampling_factor = 4; + size_t num_filters = 5; + size_t delay_headroom_samples = 32; + 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; + int converged; + } delay_selection_thresholds = {5, 20}; + bool use_external_delay_estimator = false; + bool log_warning_on_delay_changes = false; + struct AlignmentMixing { + bool downmix; + bool adaptive_selection; + float activity_power_threshold; + bool prefer_first_two_channels; + }; + 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 { + struct RefinedConfiguration { + size_t length_blocks; + float leakage_converged; + float leakage_diverged; + float error_floor; + float error_ceil; + float noise_gate; + }; + + struct CoarseConfiguration { + size_t length_blocks; + float rate; + float noise_gate; + }; + + RefinedConfiguration refined = {13, 0.00005f, 0.05f, + 0.001f, 2.f, 20075344.f}; + CoarseConfiguration coarse = {13, 0.7f, 20075344.f}; + + RefinedConfiguration refined_initial = {12, 0.005f, 0.5f, + 0.001f, 2.f, 20075344.f}; + CoarseConfiguration coarse_initial = {12, 0.9f, 20075344.f}; + + 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; + + struct Erle { + float min = 1.f; + float max_l = 4.f; + float max_h = 1.5f; + bool onset_detection = true; + size_t num_sections = 1; + bool clamp_quality_estimate_to_zero = true; + bool clamp_quality_estimate_to_one = true; + } erle; + + 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 { + float low_render_limit = 4 * 64.f; + float normal_render_limit = 64.f; + float floor_power = 2 * 64.f; + float audibility_threshold_lf = 10; + float audibility_threshold_mf = 10; + float audibility_threshold_hf = 10; + bool use_stationarity_properties = false; + bool use_stationarity_properties_at_init = false; + } echo_audibility; + + struct RenderLevels { + float active_render_limit = 100.f; + float poor_excitation_render_limit = 150.f; + float poor_excitation_render_limit_ds8 = 20.f; + float render_power_gain_db = 0.f; + } render_levels; + + struct EchoRemovalControl { + bool has_clock_drift = false; + bool linear_and_stable_echo_path = false; + } echo_removal_control; + + struct EchoModel { + EchoModel(); + EchoModel(const EchoModel& e); + EchoModel& operator=(const EchoModel& e); + size_t noise_floor_hold = 50; + float min_noise_floor_power = 1638400.f; + float stationary_gate_slope = 10.f; + float noise_gate_power = 27509.42f; + float noise_gate_slope = 0.3f; + size_t render_pre_window_size = 1; + size_t render_post_window_size = 1; + bool model_reverb_in_nonlinear_mode = true; + } echo_model; + + struct ComfortNoise { + float noise_floor_dbfs = -96.03406f; + } comfort_noise; + + struct Suppressor { + Suppressor(); + Suppressor(const Suppressor& e); + Suppressor& operator=(const Suppressor& e); + + size_t nearend_average_blocks = 4; + + struct MaskingThresholds { + MaskingThresholds(float enr_transparent, + float enr_suppress, + float emr_transparent); + MaskingThresholds(const MaskingThresholds& e); + MaskingThresholds& operator=(const MaskingThresholds& e); + float enr_transparent; + float enr_suppress; + float emr_transparent; + }; + + struct Tuning { + Tuning(MaskingThresholds mask_lf, + MaskingThresholds mask_hf, + float max_inc_factor, + float max_dec_factor_lf); + Tuning(const Tuning& e); + Tuning& operator=(const Tuning& e); + MaskingThresholds mask_lf; + MaskingThresholds mask_hf; + float max_inc_factor; + float max_dec_factor_lf; + }; + + Tuning normal_tuning = Tuning(MaskingThresholds(.3f, .4f, .3f), + MaskingThresholds(.07f, .1f, .3f), + 2.0f, + 0.25f); + Tuning nearend_tuning = Tuning(MaskingThresholds(1.09f, 1.1f, .3f), + MaskingThresholds(.1f, .3f, .3f), + 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; + float snr_threshold = 30.f; + 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 { + size_t nearend_average_blocks = 1; + struct SubbandRegion { + size_t low; + size_t high; + }; + SubbandRegion subband1 = {1, 1}; + SubbandRegion subband2 = {1, 1}; + float nearend_threshold = 1.f; + float snr_threshold = 1.f; + } subband_nearend_detection; + + bool use_subband_nearend_detection = false; + + struct HighBandsSuppression { + float enr_threshold = 1.f; + float max_gain_during_echo = 1.f; + float anti_howling_activation_threshold = 400.f; + float anti_howling_gain = 1.f; + } 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 + +#endif // API_AUDIO_ECHO_CANCELLER3_CONFIG_H_ diff --git a/pkg/apm/webrtc/api/audio/echo_canceller3_factory.cc b/pkg/apm/webrtc/api/audio/echo_canceller3_factory.cc new file mode 100644 index 00000000..6e3a54a7 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/echo_canceller3_factory.cc @@ -0,0 +1,43 @@ +/* + * 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/audio/echo_canceller3_factory.h" + +#include +#include + +#include "absl/base/nullability.h" +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "api/environment/environment.h" +#include "modules/audio_processing/aec3/echo_canceller3.h" + +namespace webrtc { + +EchoCanceller3Factory::EchoCanceller3Factory() {} + +EchoCanceller3Factory::EchoCanceller3Factory(const EchoCanceller3Config config) + : config_(config), multichannel_config_(std::nullopt) {} + +EchoCanceller3Factory::EchoCanceller3Factory( + const EchoCanceller3Config config, + std::optional multichannel_config) + : config_(config), multichannel_config_(multichannel_config) {} + +absl_nonnull std::unique_ptr EchoCanceller3Factory::Create( + const Environment& env, + int sample_rate_hz, + int num_render_channels, + int num_capture_channels) { + return std::make_unique(env, config_, multichannel_config_, + sample_rate_hz, num_render_channels, + num_capture_channels); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/audio/echo_canceller3_factory.h b/pkg/apm/webrtc/api/audio/echo_canceller3_factory.h new file mode 100644 index 00000000..e26f2989 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/echo_canceller3_factory.h @@ -0,0 +1,53 @@ +/* + * 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_AUDIO_ECHO_CANCELLER3_FACTORY_H_ +#define API_AUDIO_ECHO_CANCELLER3_FACTORY_H_ + +#include +#include + +#include "absl/base/nullability.h" +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "api/environment/environment.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +class RTC_EXPORT EchoCanceller3Factory : public EchoControlFactory { + public: + // Factory producing EchoCanceller3 instances with the default configuration. + EchoCanceller3Factory(); + + // Factory producing EchoCanceller3 instances with the specified + // configuration. + explicit EchoCanceller3Factory(const EchoCanceller3Config config); + + // Factory producing EchoCanceller3 instances with the specified + // configuration and multichannel configuration. + EchoCanceller3Factory( + const EchoCanceller3Config config, + std::optional multichannel_config); + + // Creates an EchoCanceller3 with a specified channel count and sampling rate. + absl_nonnull std::unique_ptr Create( + const Environment& env, + int sample_rate_hz, + int num_render_channels, + int num_capture_channels) override; + + private: + const EchoCanceller3Config config_; + const std::optional multichannel_config_; +}; +} // namespace webrtc + +#endif // API_AUDIO_ECHO_CANCELLER3_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/audio/echo_control.h b/pkg/apm/webrtc/api/audio/echo_control.h new file mode 100644 index 00000000..d1a1faad --- /dev/null +++ b/pkg/apm/webrtc/api/audio/echo_control.h @@ -0,0 +1,78 @@ +/* + * 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_AUDIO_ECHO_CONTROL_H_ +#define API_AUDIO_ECHO_CONTROL_H_ + +#include + +#include "absl/base/nullability.h" +#include "api/environment/environment.h" + +namespace webrtc { + +class AudioBuffer; + +// Interface for an acoustic echo cancellation (AEC) submodule. +class EchoControl { + public: + // Analysis (not changing) of the render signal. + virtual void AnalyzeRender(AudioBuffer* render) = 0; + + // Analysis (not changing) of the capture signal. + virtual void AnalyzeCapture(AudioBuffer* capture) = 0; + + // Processes the capture signal in order to remove the echo. + virtual void ProcessCapture(AudioBuffer* capture, bool level_change) = 0; + + // As above, but also returns the linear filter output. + virtual void ProcessCapture(AudioBuffer* capture, + AudioBuffer* linear_output, + bool level_change) = 0; + + struct Metrics { + double echo_return_loss; + double echo_return_loss_enhancement; + int delay_ms; + }; + + // Collect current metrics from the echo controller. + virtual Metrics GetMetrics() const = 0; + + // 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; + + virtual ~EchoControl() {} +}; + +// Interface for a factory that creates EchoControllers. +class EchoControlFactory { + public: + virtual ~EchoControlFactory() = default; + + virtual absl_nonnull std::unique_ptr Create( + const Environment& env, + int sample_rate_hz, + int num_render_channels, + int num_capture_channels) = 0; +}; +} // namespace webrtc + +#endif // API_AUDIO_ECHO_CONTROL_H_ diff --git a/pkg/apm/webrtc/api/audio/echo_detector_creator.cc b/pkg/apm/webrtc/api/audio/echo_detector_creator.cc new file mode 100644 index 00000000..bb807ed4 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/echo_detector_creator.cc @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "api/audio/echo_detector_creator.h" + +#include "api/audio/audio_processing.h" +#include "api/make_ref_counted.h" +#include "api/scoped_refptr.h" +#include "modules/audio_processing/residual_echo_detector.h" + +namespace webrtc { + +scoped_refptr CreateEchoDetector() { + return make_ref_counted(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/audio/echo_detector_creator.h b/pkg/apm/webrtc/api/audio/echo_detector_creator.h new file mode 100644 index 00000000..8f260ae0 --- /dev/null +++ b/pkg/apm/webrtc/api/audio/echo_detector_creator.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_ECHO_DETECTOR_CREATOR_H_ +#define API_AUDIO_ECHO_DETECTOR_CREATOR_H_ + +#include "api/audio/audio_processing.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +// Returns an instance of the WebRTC implementation of a residual echo detector. +// It can be provided to the webrtc::BuiltinAudioProcessingBuilder to obtain the +// usual residual echo metrics. +scoped_refptr CreateEchoDetector(); + +} // namespace webrtc + +#endif // API_AUDIO_ECHO_DETECTOR_CREATOR_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/L16/audio_decoder_L16.h b/pkg/apm/webrtc/api/audio_codecs/L16/audio_decoder_L16.h new file mode 100644 index 00000000..339527f9 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/L16/audio_decoder_L16.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_L16_AUDIO_DECODER_L16_H_ +#define API_AUDIO_CODECS_L16_AUDIO_DECODER_L16_H_ + +#include +#include +#include + +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_decoder.h" +#include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// L16 decoder API for use as a template parameter to +// CreateAudioDecoderFactory<...>(). +struct RTC_EXPORT AudioDecoderL16 { + struct Config { + bool IsOk() const { + return (sample_rate_hz == 8000 || sample_rate_hz == 16000 || + sample_rate_hz == 32000 || sample_rate_hz == 48000) && + (num_channels >= 1 && + num_channels <= AudioDecoder::kMaxNumberOfChannels); + } + int sample_rate_hz = 8000; + int num_channels = 1; + }; + static std::optional SdpToConfig(const SdpAudioFormat& audio_format); + static void AppendSupportedDecoders(std::vector* specs); + static std::unique_ptr MakeAudioDecoder( + const Config& config, + std::optional codec_pair_id = std::nullopt, + const FieldTrialsView* field_trials = nullptr); +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_L16_AUDIO_DECODER_L16_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/L16/audio_encoder_L16.h b/pkg/apm/webrtc/api/audio_codecs/L16/audio_encoder_L16.h new file mode 100644 index 00000000..a104a630 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/L16/audio_encoder_L16.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_L16_AUDIO_ENCODER_L16_H_ +#define API_AUDIO_CODECS_L16_AUDIO_ENCODER_L16_H_ + +#include +#include +#include + +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_encoder.h" +#include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// L16 encoder API for use as a template parameter to +// CreateAudioEncoderFactory<...>(). +struct RTC_EXPORT AudioEncoderL16 { + struct Config { + bool IsOk() const { + return (sample_rate_hz == 8000 || sample_rate_hz == 16000 || + sample_rate_hz == 32000 || sample_rate_hz == 48000) && + num_channels >= 1 && + num_channels <= AudioEncoder::kMaxNumberOfChannels && + frame_size_ms > 0 && frame_size_ms <= 120 && + frame_size_ms % 10 == 0; + } + int sample_rate_hz = 8000; + int num_channels = 1; + int frame_size_ms = 10; + }; + static std::optional SdpToConfig(const SdpAudioFormat& audio_format); + static void AppendSupportedEncoders(std::vector* specs); + static AudioCodecInfo QueryAudioEncoder(const Config& config); + static std::unique_ptr MakeAudioEncoder( + const Config& config, + int payload_type, + std::optional codec_pair_id = std::nullopt, + const FieldTrialsView* field_trials = nullptr); +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_L16_AUDIO_ENCODER_L16_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/audio_codec_pair_id.h b/pkg/apm/webrtc/api/audio_codecs/audio_codec_pair_id.h new file mode 100644 index 00000000..b10f14ea --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/audio_codec_pair_id.h @@ -0,0 +1,74 @@ +/* + * 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_AUDIO_CODECS_AUDIO_CODEC_PAIR_ID_H_ +#define API_AUDIO_CODECS_AUDIO_CODEC_PAIR_ID_H_ + +#include + +#include + +namespace webrtc { + +class AudioCodecPairId final { + public: + // Copyable, but not default constructible. + AudioCodecPairId() = delete; + AudioCodecPairId(const AudioCodecPairId&) = default; + AudioCodecPairId(AudioCodecPairId&&) = default; + AudioCodecPairId& operator=(const AudioCodecPairId&) = default; + AudioCodecPairId& operator=(AudioCodecPairId&&) = default; + + friend void swap(AudioCodecPairId& a, AudioCodecPairId& b) { + using std::swap; + swap(a.id_, b.id_); + } + + // Creates a new ID, unequal to any previously created ID. + static AudioCodecPairId Create(); + + // IDs can be tested for equality. + friend bool operator==(AudioCodecPairId a, AudioCodecPairId b) { + return a.id_ == b.id_; + } + friend bool operator!=(AudioCodecPairId a, AudioCodecPairId b) { + return a.id_ != b.id_; + } + + // Comparisons. The ordering of ID values is completely arbitrary, but + // stable, so it's useful e.g. if you want to use IDs as keys in an ordered + // map. + friend bool operator<(AudioCodecPairId a, AudioCodecPairId b) { + return a.id_ < b.id_; + } + friend bool operator<=(AudioCodecPairId a, AudioCodecPairId b) { + return a.id_ <= b.id_; + } + friend bool operator>=(AudioCodecPairId a, AudioCodecPairId b) { + return a.id_ >= b.id_; + } + friend bool operator>(AudioCodecPairId a, AudioCodecPairId b) { + return a.id_ > b.id_; + } + + // Returns a numeric representation of the ID. The numeric values are + // completely arbitrary, but stable, collision-free, and reasonably evenly + // distributed, so they are e.g. useful as hash values in unordered maps. + uint64_t NumericRepresentation() const { return id_; } + + private: + explicit AudioCodecPairId(uint64_t id) : id_(id) {} + + uint64_t id_; +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_AUDIO_CODEC_PAIR_ID_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/audio_decoder.h b/pkg/apm/webrtc/api/audio_codecs/audio_decoder.h new file mode 100644 index 00000000..d2d5e7b3 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/audio_decoder.h @@ -0,0 +1,195 @@ +/* + * 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 +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "rtc_base/buffer.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; + + AudioDecoder(const AudioDecoder&) = delete; + AudioDecoder& operator=(const AudioDecoder&) = delete; + + 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 std::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 + // std::optional. Decode may be called at most once per frame object. + virtual std::optional Decode( + ArrayView decoded) const = 0; + }; + + struct ParseResult { + ParseResult(); + ParseResult(uint32_t timestamp, + int priority, + std::unique_ptr 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 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 ParsePayload(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 implementation. + virtual void GeneratePlc(size_t requested_samples_per_channel, + BufferT* 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; + + // The maximum number of audio channels supported by WebRTC decoders. + static constexpr int kMaxNumberOfChannels = 24; + + 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); +}; + +} // namespace webrtc +#endif // API_AUDIO_CODECS_AUDIO_DECODER_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/audio_decoder_factory.h b/pkg/apm/webrtc/api/audio_codecs/audio_decoder_factory.h new file mode 100644 index 00000000..775afafe --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/audio_decoder_factory.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef API_AUDIO_CODECS_AUDIO_DECODER_FACTORY_H_ +#define API_AUDIO_CODECS_AUDIO_DECODER_FACTORY_H_ + +#include +#include +#include + +#include "absl/base/nullability.h" +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_decoder.h" +#include "api/audio_codecs/audio_format.h" +#include "api/environment/environment.h" +#include "api/ref_count.h" + +namespace webrtc { + +// A factory that creates AudioDecoders. +class AudioDecoderFactory : public RefCountInterface { + public: + virtual std::vector GetSupportedDecoders() = 0; + + virtual bool IsSupportedDecoder(const SdpAudioFormat& format) = 0; + + // Create a new decoder instance. The `codec_pair_id` argument is used to link + // encoders and decoders that talk to the same remote entity: if a + // AudioEncoderFactory::Create() and a AudioDecoderFactory::Create() call + // receive non-null IDs that compare equal, the factory implementations may + // assume that the encoder and decoder form a pair. (The intended use case for + // this is to set up communication between the AudioEncoder and AudioDecoder + // instances, which is needed for some codecs with built-in bandwidth + // adaptation.) + // + // Returns null if the format isn't supported. + // + // Note: Implementations need to be robust against combinations other than + // one encoder, one decoder getting the same ID; such decoders must still + // work. + virtual absl_nullable std::unique_ptr Create( + const Environment& env, + const SdpAudioFormat& format, + std::optional codec_pair_id) = 0; +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_AUDIO_DECODER_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/audio_decoder_factory_template.h b/pkg/apm/webrtc/api/audio_codecs/audio_decoder_factory_template.h new file mode 100644 index 00000000..e55d43b0 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/audio_decoder_factory_template.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_AUDIO_DECODER_FACTORY_TEMPLATE_H_ +#define API_AUDIO_CODECS_AUDIO_DECODER_FACTORY_TEMPLATE_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/nullability.h" +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_decoder.h" +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/audio_codecs/audio_format.h" +#include "api/environment/environment.h" +#include "api/make_ref_counted.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +namespace audio_decoder_factory_template_impl { + +template +struct Helper; + +// Base case: 0 template parameters. +template <> +struct Helper<> { + static void AppendSupportedDecoders( + std::vector* /* specs */) {} + static bool IsSupportedDecoder(const SdpAudioFormat& /* format */) { + return false; + } + + static absl_nullable std::unique_ptr MakeAudioDecoder( + const Environment& /* env */, + const SdpAudioFormat& /* format */, + std::optional /* codec_pair_id */) { + return nullptr; + } +}; + +// Use ranked overloads (abseil.io/tips/229) for dispatching. +struct Rank0 {}; +struct Rank1 : Rank0 {}; + +template (), + std::declval(), + std::declval>())), + std::unique_ptr>>> +absl_nullable std::unique_ptr CreateDecoder( + Rank1, + const Environment& env, + const typename Trait::Config& config, + std::optional codec_pair_id) { + return Trait::MakeAudioDecoder(env, config, codec_pair_id); +} + +template (), + std::declval>())), + std::unique_ptr>>> +absl_nullable std::unique_ptr CreateDecoder( + Rank0, + const Environment& /* env */, + const typename Trait::Config& config, + std::optional codec_pair_id) { + return Trait::MakeAudioDecoder(config, codec_pair_id); +} + +// Inductive case: Called with n + 1 template parameters; calls subroutines +// with n template parameters. +template +struct Helper { + static void AppendSupportedDecoders(std::vector* specs) { + T::AppendSupportedDecoders(specs); + Helper::AppendSupportedDecoders(specs); + } + static bool IsSupportedDecoder(const SdpAudioFormat& format) { + auto opt_config = T::SdpToConfig(format); + static_assert(std::is_same>::value, + "T::SdpToConfig() must return a value of type " + "std::optional"); + return opt_config ? true : Helper::IsSupportedDecoder(format); + } + + static absl_nullable std::unique_ptr MakeAudioDecoder( + const Environment& env, + const SdpAudioFormat& format, + std::optional codec_pair_id) { + auto opt_config = T::SdpToConfig(format); + return opt_config.has_value() + ? CreateDecoder(Rank1{}, env, *opt_config, codec_pair_id) + : Helper::MakeAudioDecoder(env, format, codec_pair_id); + } +}; + +template +class AudioDecoderFactoryT : public AudioDecoderFactory { + public: + std::vector GetSupportedDecoders() override { + std::vector specs; + Helper::AppendSupportedDecoders(&specs); + return specs; + } + + bool IsSupportedDecoder(const SdpAudioFormat& format) override { + return Helper::IsSupportedDecoder(format); + } + + absl_nullable std::unique_ptr Create( + const Environment& env, + const SdpAudioFormat& format, + std::optional codec_pair_id) override { + return Helper::MakeAudioDecoder(env, format, codec_pair_id); + } +}; + +} // namespace audio_decoder_factory_template_impl + +// Make an AudioDecoderFactory that can create instances of the given decoders. +// +// Each decoder type is given as a template argument to the function; it should +// be a struct with the following static member functions: +// +// // Converts `audio_format` to a ConfigType instance. Returns an empty +// // optional if `audio_format` doesn't correctly specify a decoder of our +// // type. +// std::optional SdpToConfig(const SdpAudioFormat& audio_format); +// +// // Appends zero or more AudioCodecSpecs to the list that will be returned +// // by AudioDecoderFactory::GetSupportedDecoders(). +// void AppendSupportedDecoders(std::vector* specs); +// +// // Creates an AudioDecoder for the specified format. Used to implement +// // AudioDecoderFactory::Create(). +// std::unique_ptr MakeAudioDecoder( +// const Environment& env, +// const ConfigType& config, +// std::optional codec_pair_id); +// or +// std::unique_ptr MakeAudioDecoder( +// const ConfigType& config, +// std::optional codec_pair_id); +// +// ConfigType should be a type that encapsulates all the settings needed to +// create an AudioDecoder. T::Config (where T is the decoder struct) should +// either be the config type, or an alias for it. +// +// Whenever it tries to do something, the new factory will try each of the +// decoder types in the order they were specified in the template argument +// list, stopping at the first one that claims to be able to do the job. +// +// TODO(kwiberg): Point at CreateBuiltinAudioDecoderFactory() for an example of +// how it is used. +template +scoped_refptr CreateAudioDecoderFactory() { + // There's no technical reason we couldn't allow zero template parameters, + // but such a factory couldn't create any decoders, and callers can do this + // by mistake by simply forgetting the <> altogether. So we forbid it in + // order to prevent caller foot-shooting. + static_assert(sizeof...(Ts) >= 1, + "Caller must give at least one template parameter"); + + return make_ref_counted< + audio_decoder_factory_template_impl::AudioDecoderFactoryT>(); +} + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_AUDIO_DECODER_FACTORY_TEMPLATE_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/audio_encoder.h b/pkg/apm/webrtc/api/audio_codecs/audio_encoder.h new file mode 100644 index 00000000..56ea2873 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/audio_encoder.h @@ -0,0 +1,269 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "api/array_view.h" +#include "api/call/bitrate_allocation.h" +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" +#include "rtc_base/buffer.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. + std::optional 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. + std::optional 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. + std::optional 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. + std::optional 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. + std::optional 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. + std::optional 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. + std::optional 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, + + // 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 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, + ArrayView audio, + 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); + + // 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). + ABSL_DEPRECATED("Use OnReceivedTargetAudioBitrate instead") + 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 ArrayView> 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); + + ABSL_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, + std::optional 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 such + // information. This is used together with the bitrate range to calculate the + // full bitrate range, including overhead. + virtual std::optional> GetFrameLengthRange() + const = 0; + + // The range of payload bitrates that are supported. This is used together + // with the frame length range to calculate the full bitrate range, including + // overhead. + virtual std::optional> GetBitrateRange() const { + return std::nullopt; + } + + // The maximum number of audio channels supported by WebRTC encoders. + static constexpr int kMaxNumberOfChannels = 24; + + protected: + // Subclasses implement this to perform the actual encoding. Called by + // Encode(). + virtual EncodedInfo EncodeImpl(uint32_t rtp_timestamp, + ArrayView audio, + Buffer* encoded) = 0; +}; +} // namespace webrtc +#endif // API_AUDIO_CODECS_AUDIO_ENCODER_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/audio_encoder_factory.h b/pkg/apm/webrtc/api/audio_codecs/audio_encoder_factory.h new file mode 100644 index 00000000..df595fa5 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/audio_encoder_factory.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_AUDIO_ENCODER_FACTORY_H_ +#define API_AUDIO_CODECS_AUDIO_ENCODER_FACTORY_H_ + +#include +#include +#include + +#include "absl/base/nullability.h" +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_encoder.h" +#include "api/audio_codecs/audio_format.h" +#include "api/environment/environment.h" +#include "api/ref_count.h" + +namespace webrtc { + +// A factory that creates AudioEncoders. +class AudioEncoderFactory : public RefCountInterface { + public: + struct Options { + // The encoder will tags its payloads with the specified payload type. + // TODO(ossu): Try to avoid audio encoders having to know their payload + // type. + int payload_type = -1; + + // Links encoders and decoders that talk to the same remote entity: if + // a AudioEncoderFactory::Create() and a AudioDecoderFactory::Create() call + // receive non-null IDs that compare equal, the factory implementations may + // assume that the encoder and decoder form a pair. (The intended use case + // for this is to set up communication between the AudioEncoder and + // AudioDecoder instances, which is needed for some codecs with built-in + // bandwidth adaptation.) + // + // Note: Implementations need to be robust against combinations other than + // one encoder, one decoder getting the same ID; such encoders must still + // work. + std::optional codec_pair_id; + }; + + // Returns a prioritized list of audio codecs, to use for signaling etc. + virtual std::vector GetSupportedEncoders() = 0; + + // Returns information about how this format would be encoded, provided it's + // supported. More format and format variations may be supported than those + // returned by GetSupportedEncoders(). + virtual std::optional QueryAudioEncoder( + const SdpAudioFormat& format) = 0; + + // Creates an AudioEncoder for the specified format. + // Returns null if the format isn't supported. + virtual absl_nullable std::unique_ptr Create( + const Environment& env, + const SdpAudioFormat& format, + Options options) = 0; +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_AUDIO_ENCODER_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/audio_encoder_factory_template.h b/pkg/apm/webrtc/api/audio_codecs/audio_encoder_factory_template.h new file mode 100644 index 00000000..845194f9 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/audio_encoder_factory_template.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_AUDIO_ENCODER_FACTORY_TEMPLATE_H_ +#define API_AUDIO_CODECS_AUDIO_ENCODER_FACTORY_TEMPLATE_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/nullability.h" +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_encoder.h" +#include "api/audio_codecs/audio_encoder_factory.h" +#include "api/audio_codecs/audio_format.h" +#include "api/environment/environment.h" +#include "api/make_ref_counted.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +namespace audio_encoder_factory_template_impl { + +template +struct Helper; + +// Base case: 0 template parameters. +template <> +struct Helper<> { + static void AppendSupportedEncoders( + std::vector* /* specs */) {} + static std::optional QueryAudioEncoder( + const SdpAudioFormat& /* format */) { + return std::nullopt; + } + static absl_nullable std::unique_ptr CreateAudioEncoder( + const Environment& /* env */, + const SdpAudioFormat& /* format */, + const AudioEncoderFactory::Options& /* options */) { + return nullptr; + } +}; + +// Use ranked overloads (abseil.io/tips/229) for dispatching. +struct Rank0 {}; +struct Rank1 : Rank0 {}; + +template (), + std::declval(), + std::declval())), + std::unique_ptr>>> +absl_nullable std::unique_ptr CreateEncoder( + Rank1, + const Environment& env, + const typename Trait::Config& config, + const AudioEncoderFactory::Options& options) { + return Trait::MakeAudioEncoder(env, config, options); +} + +template (), + int{}, + std::declval>())), + std::unique_ptr>>> +absl_nullable std::unique_ptr CreateEncoder( + Rank0, + const Environment& /* env */, + const typename Trait::Config& config, + const AudioEncoderFactory::Options& options) { + return Trait::MakeAudioEncoder(config, options.payload_type, + options.codec_pair_id); +} + +// Inductive case: Called with n + 1 template parameters; calls subroutines +// with n template parameters. +template +struct Helper { + static void AppendSupportedEncoders(std::vector* specs) { + T::AppendSupportedEncoders(specs); + Helper::AppendSupportedEncoders(specs); + } + static std::optional QueryAudioEncoder( + const SdpAudioFormat& format) { + auto opt_config = T::SdpToConfig(format); + static_assert(std::is_same>::value, + "T::SdpToConfig() must return a value of type " + "std::optional"); + return opt_config ? std::optional( + T::QueryAudioEncoder(*opt_config)) + : Helper::QueryAudioEncoder(format); + } + + static absl_nullable std::unique_ptr CreateAudioEncoder( + const Environment& env, + const SdpAudioFormat& format, + const AudioEncoderFactory::Options& options) { + if (auto opt_config = T::SdpToConfig(format); opt_config.has_value()) { + return CreateEncoder(Rank1{}, env, *opt_config, options); + } + return Helper::CreateAudioEncoder(env, format, options); + } +}; + +template +class AudioEncoderFactoryT : public AudioEncoderFactory { + public: + std::vector GetSupportedEncoders() override { + std::vector specs; + Helper::AppendSupportedEncoders(&specs); + return specs; + } + + std::optional QueryAudioEncoder( + const SdpAudioFormat& format) override { + return Helper::QueryAudioEncoder(format); + } + + absl_nullable std::unique_ptr Create( + const Environment& env, + const SdpAudioFormat& format, + Options options) override { + return Helper::CreateAudioEncoder(env, format, options); + } +}; + +} // namespace audio_encoder_factory_template_impl + +// Make an AudioEncoderFactory that can create instances of the given encoders. +// +// Each encoder type is given as a template argument to the function; it should +// be a struct with the following static member functions: +// +// // Converts `audio_format` to a ConfigType instance. Returns an empty +// // optional if `audio_format` doesn't correctly specify an encoder of our +// // type. +// std::optional SdpToConfig(const SdpAudioFormat& audio_format); +// +// // Appends zero or more AudioCodecSpecs to the list that will be returned +// // by AudioEncoderFactory::GetSupportedEncoders(). +// void AppendSupportedEncoders(std::vector* specs); +// +// // Returns information about how this format would be encoded. Used to +// // implement AudioEncoderFactory::QueryAudioEncoder(). +// AudioCodecInfo QueryAudioEncoder(const ConfigType& config); +// +// // Creates an AudioEncoder for the specified format. Used to implement +// // AudioEncoderFactory::Create. +// std::unique_ptr MakeAudioEncoder( +// const Environment& env, +// const ConfigType& config, +// const AudioEncoderFactory::Options& options); +// or +// std::unique_ptr MakeAudioEncoder( +// const ConfigType& config, +// int payload_type, +// std::optional codec_pair_id); +// +// ConfigType should be a type that encapsulates all the settings needed to +// create an AudioEncoder. T::Config (where T is the encoder struct) should +// either be the config type, or an alias for it. +// When both MakeAudioEncoder signatures are present, 1st one is preferred. +// +// Whenever it tries to do something, the new factory will try each of the +// encoders in the order they were specified in the template argument list, +// stopping at the first one that claims to be able to do the job. +// +// TODO(kwiberg): Point at CreateBuiltinAudioEncoderFactory() for an example of +// how it is used. +template +scoped_refptr CreateAudioEncoderFactory() { + // There's no technical reason we couldn't allow zero template parameters, + // but such a factory couldn't create any encoders, and callers can do this + // by mistake by simply forgetting the <> altogether. So we forbid it in + // order to prevent caller foot-shooting. + static_assert(sizeof...(Ts) >= 1, + "Caller must give at least one template parameter"); + + return make_ref_counted< + audio_encoder_factory_template_impl::AudioEncoderFactoryT>(); +} + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_AUDIO_ENCODER_FACTORY_TEMPLATE_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/audio_format.h b/pkg/apm/webrtc/api/audio_codecs/audio_format.h new file mode 100644 index 00000000..a5d4a920 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/audio_format.h @@ -0,0 +1,169 @@ +/* + * 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. + */ + +#ifndef API_AUDIO_CODECS_AUDIO_FORMAT_H_ +#define API_AUDIO_CODECS_AUDIO_FORMAT_H_ + +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/rtp_parameters.h" +#include "rtc_base/checks.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/system/rtc_export.h" // IWYU pragma: private + +namespace webrtc { + +// SDP specification for a single audio codec. +struct RTC_EXPORT SdpAudioFormat { + using Parameters [[deprecated("Use webrtc::CodecParameterMap")]] = + std::map; + + SdpAudioFormat(const SdpAudioFormat&); + SdpAudioFormat(SdpAudioFormat&&); + SdpAudioFormat(absl::string_view name, int clockrate_hz, size_t num_channels); + SdpAudioFormat(absl::string_view name, + int clockrate_hz, + size_t num_channels, + const CodecParameterMap& param); + SdpAudioFormat(absl::string_view name, + int clockrate_hz, + size_t num_channels, + CodecParameterMap&& param); + ~SdpAudioFormat(); + + // Returns true if this format is compatible with `o`. In SDP terminology: + // would it represent the same codec between an offer and an answer? As + // opposed to operator==, this method disregards codec parameters. + bool Matches(const SdpAudioFormat& o) const; + + SdpAudioFormat& operator=(const SdpAudioFormat&); + SdpAudioFormat& operator=(SdpAudioFormat&&); + + friend bool operator==(const SdpAudioFormat& a, const SdpAudioFormat& b); + friend bool operator!=(const SdpAudioFormat& a, const SdpAudioFormat& b) { + return !(a == b); + } + + template + friend void AbslStringify(Sink& sink, const SdpAudioFormat& saf) { + StringBuilder sb("{"); + bool first = true; + for (const auto& [key, value] : saf.parameters) { + if (!first) { + sb << ", "; + } + first = false; + sb << key << ": " << value; + } + sb << "}"; + absl::Format( + &sink, "{name: %s, clockrate_hz: %d, num_channels: %d, parameters: %v}", + saf.name, saf.clockrate_hz, saf.num_channels, sb.Release()); + } + + std::string name; + int clockrate_hz; + size_t num_channels; + CodecParameterMap parameters; +}; + +// Information about how an audio format is treated by the codec implementation. +// Contains basic information, such as sample rate and number of channels, which +// isn't uniformly presented by SDP. Also contains flags indicating support for +// integrating with other parts of WebRTC, like external VAD and comfort noise +// level calculation. +// +// To avoid API breakage, and make the code clearer, AudioCodecInfo should not +// be directly initializable with any flags indicating optional support. If it +// were, these initializers would break any time a new flag was added. It's also +// more difficult to understand: +// AudioCodecInfo info{16000, 1, 32000, true, false, false, true, true}; +// than +// AudioCodecInfo info(16000, 1, 32000); +// info.allow_comfort_noise = true; +// info.future_flag_b = true; +// info.future_flag_c = true; +struct AudioCodecInfo { + AudioCodecInfo(int sample_rate_hz, size_t num_channels, int bitrate_bps); + AudioCodecInfo(int sample_rate_hz, + size_t num_channels, + int default_bitrate_bps, + int min_bitrate_bps, + int max_bitrate_bps); + AudioCodecInfo(const AudioCodecInfo& b) = default; + ~AudioCodecInfo() = default; + + bool operator==(const AudioCodecInfo& b) const { + return sample_rate_hz == b.sample_rate_hz && + num_channels == b.num_channels && + default_bitrate_bps == b.default_bitrate_bps && + min_bitrate_bps == b.min_bitrate_bps && + max_bitrate_bps == b.max_bitrate_bps && + allow_comfort_noise == b.allow_comfort_noise && + supports_network_adaption == b.supports_network_adaption; + } + + bool operator!=(const AudioCodecInfo& b) const { return !(*this == b); } + + bool HasFixedBitrate() const { + RTC_DCHECK_GE(min_bitrate_bps, 0); + RTC_DCHECK_LE(min_bitrate_bps, default_bitrate_bps); + RTC_DCHECK_GE(max_bitrate_bps, default_bitrate_bps); + return min_bitrate_bps == max_bitrate_bps; + } + + template + friend void AbslStringify(Sink& sink, const AudioCodecInfo& aci) { + absl::Format(&sink, + "{sample_rate_hz: %d, num_channels: %d, default_bitrate_bps: " + "%d, min_bitrate_bps: %d, max_bitrate_bps: %d, " + "allow_comfort_noise: %v, supports_network_adaption: %v}", + aci.sample_rate_hz, aci.num_channels, aci.default_bitrate_bps, + aci.min_bitrate_bps, aci.max_bitrate_bps, + aci.allow_comfort_noise, aci.supports_network_adaption); + } + + int sample_rate_hz; + size_t num_channels; + int default_bitrate_bps; + int min_bitrate_bps; + int max_bitrate_bps; + + bool allow_comfort_noise = true; // This codec can be used with an external + // comfort noise generator. + bool supports_network_adaption = false; // This codec can adapt to varying + // network conditions. +}; + +// AudioCodecSpec ties an audio format to specific information about the codec +// and its implementation. +struct AudioCodecSpec { + bool operator==(const AudioCodecSpec& b) const { + return format == b.format && info == b.info; + } + + bool operator!=(const AudioCodecSpec& b) const { return !(*this == b); } + + template + friend void AbslStringify(Sink& sink, const AudioCodecSpec& acs) { + absl::Format(&sink, "{format: %v, info: %v}", acs.format, acs.info); + } + + SdpAudioFormat format; + AudioCodecInfo info; +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_AUDIO_FORMAT_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/builtin_audio_decoder_factory.h b/pkg/apm/webrtc/api/audio_codecs/builtin_audio_decoder_factory.h new file mode 100644 index 00000000..41f7c125 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/builtin_audio_decoder_factory.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#ifndef API_AUDIO_CODECS_BUILTIN_AUDIO_DECODER_FACTORY_H_ +#define API_AUDIO_CODECS_BUILTIN_AUDIO_DECODER_FACTORY_H_ + +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +// Creates a new factory that can create the built-in types of audio decoders. +// Note: This will link with all the code implementing those codecs, so if you +// only need a subset of the codecs, consider using +// CreateAudioDecoderFactory<...codecs listed here...>() or +// CreateOpusAudioDecoderFactory() instead. +scoped_refptr CreateBuiltinAudioDecoderFactory(); + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_BUILTIN_AUDIO_DECODER_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/builtin_audio_encoder_factory.h b/pkg/apm/webrtc/api/audio_codecs/builtin_audio_encoder_factory.h new file mode 100644 index 00000000..8c0a4ab4 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/builtin_audio_encoder_factory.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#ifndef API_AUDIO_CODECS_BUILTIN_AUDIO_ENCODER_FACTORY_H_ +#define API_AUDIO_CODECS_BUILTIN_AUDIO_ENCODER_FACTORY_H_ + +#include "api/audio_codecs/audio_encoder_factory.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +// Creates a new factory that can create the built-in types of audio encoders. +// Note: This will link with all the code implementing those codecs, so if you +// only need a subset of the codecs, consider using +// CreateAudioEncoderFactory<...codecs listed here...>() or +// CreateOpusAudioEncoderFactory() instead. +scoped_refptr CreateBuiltinAudioEncoderFactory(); + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_BUILTIN_AUDIO_ENCODER_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/g711/audio_decoder_g711.h b/pkg/apm/webrtc/api/audio_codecs/g711/audio_decoder_g711.h new file mode 100644 index 00000000..155c13d4 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/g711/audio_decoder_g711.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_G711_AUDIO_DECODER_G711_H_ +#define API_AUDIO_CODECS_G711_AUDIO_DECODER_G711_H_ + +#include +#include +#include + +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_decoder.h" +#include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// G711 decoder API for use as a template parameter to +// CreateAudioDecoderFactory<...>(). +struct RTC_EXPORT AudioDecoderG711 { + struct Config { + enum class Type { kPcmU, kPcmA }; + bool IsOk() const { + return (type == Type::kPcmU || type == Type::kPcmA) && + num_channels >= 1 && + num_channels <= AudioDecoder::kMaxNumberOfChannels; + } + Type type; + int num_channels; + }; + static std::optional SdpToConfig(const SdpAudioFormat& audio_format); + static void AppendSupportedDecoders(std::vector* specs); + static std::unique_ptr MakeAudioDecoder( + const Config& config, + std::optional codec_pair_id = std::nullopt, + const FieldTrialsView* field_trials = nullptr); +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_G711_AUDIO_DECODER_G711_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/g711/audio_encoder_g711.h b/pkg/apm/webrtc/api/audio_codecs/g711/audio_encoder_g711.h new file mode 100644 index 00000000..db39a987 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/g711/audio_encoder_g711.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_G711_AUDIO_ENCODER_G711_H_ +#define API_AUDIO_CODECS_G711_AUDIO_ENCODER_G711_H_ + +#include +#include +#include + +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_encoder.h" +#include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// G711 encoder API for use as a template parameter to +// CreateAudioEncoderFactory<...>(). +struct RTC_EXPORT AudioEncoderG711 { + struct Config { + enum class Type { kPcmU, kPcmA }; + bool IsOk() const { + return (type == Type::kPcmU || type == Type::kPcmA) && + frame_size_ms > 0 && frame_size_ms % 10 == 0 && + num_channels >= 1 && + num_channels <= AudioEncoder::kMaxNumberOfChannels; + } + Type type = Type::kPcmU; + int num_channels = 1; + int frame_size_ms = 20; + }; + static std::optional SdpToConfig( + const SdpAudioFormat& audio_format); + static void AppendSupportedEncoders(std::vector* specs); + static AudioCodecInfo QueryAudioEncoder(const Config& config); + static std::unique_ptr MakeAudioEncoder( + const Config& config, + int payload_type, + std::optional codec_pair_id = std::nullopt, + const FieldTrialsView* field_trials = nullptr); +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_G711_AUDIO_ENCODER_G711_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/g722/audio_decoder_g722.h b/pkg/apm/webrtc/api/audio_codecs/g722/audio_decoder_g722.h new file mode 100644 index 00000000..18598663 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/g722/audio_decoder_g722.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_G722_AUDIO_DECODER_G722_H_ +#define API_AUDIO_CODECS_G722_AUDIO_DECODER_G722_H_ + +#include +#include +#include + +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_decoder.h" +#include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// G722 decoder API for use as a template parameter to +// CreateAudioDecoderFactory<...>(). +struct RTC_EXPORT AudioDecoderG722 { + struct Config { + bool IsOk() const { return num_channels == 1 || num_channels == 2; } + int num_channels; + }; + static std::optional SdpToConfig(const SdpAudioFormat& audio_format); + static void AppendSupportedDecoders(std::vector* specs); + static std::unique_ptr MakeAudioDecoder( + Config config, + std::optional codec_pair_id = std::nullopt, + const FieldTrialsView* field_trials = nullptr); +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_G722_AUDIO_DECODER_G722_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/g722/audio_encoder_g722.h b/pkg/apm/webrtc/api/audio_codecs/g722/audio_encoder_g722.h new file mode 100644 index 00000000..0997d8bb --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/g722/audio_encoder_g722.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_H_ +#define API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_H_ + +#include +#include +#include + +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_encoder.h" +#include "api/audio_codecs/audio_format.h" +#include "api/audio_codecs/g722/audio_encoder_g722_config.h" +#include "api/field_trials_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// G722 encoder API for use as a template parameter to +// CreateAudioEncoderFactory<...>(). +struct RTC_EXPORT AudioEncoderG722 { + using Config = AudioEncoderG722Config; + static std::optional SdpToConfig( + const SdpAudioFormat& audio_format); + static void AppendSupportedEncoders(std::vector* specs); + static AudioCodecInfo QueryAudioEncoder(const AudioEncoderG722Config& config); + static std::unique_ptr MakeAudioEncoder( + const AudioEncoderG722Config& config, + int payload_type, + std::optional codec_pair_id = std::nullopt, + const FieldTrialsView* field_trials = nullptr); +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/g722/audio_encoder_g722_config.h b/pkg/apm/webrtc/api/audio_codecs/g722/audio_encoder_g722_config.h new file mode 100644 index 00000000..f3f3a9f0 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/g722/audio_encoder_g722_config.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_CONFIG_H_ +#define API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_CONFIG_H_ + +#include "api/audio_codecs/audio_encoder.h" + +namespace webrtc { + +struct AudioEncoderG722Config { + bool IsOk() const { + return frame_size_ms > 0 && frame_size_ms % 10 == 0 && num_channels >= 1 && + num_channels <= AudioEncoder::kMaxNumberOfChannels; + } + int frame_size_ms = 20; + int num_channels = 1; +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_CONFIG_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h b/pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h new file mode 100644 index 00000000..d9fc693f --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_H_ +#define API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_H_ + +#include +#include +#include + +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_decoder.h" +#include "api/audio_codecs/audio_format.h" +#include "api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h" +#include "api/field_trials_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Opus decoder API for use as a template parameter to +// CreateAudioDecoderFactory<...>(). +struct RTC_EXPORT AudioDecoderMultiChannelOpus { + using Config = AudioDecoderMultiChannelOpusConfig; + static std::optional SdpToConfig( + const SdpAudioFormat& audio_format); + static void AppendSupportedDecoders(std::vector* specs); + static std::unique_ptr MakeAudioDecoder( + AudioDecoderMultiChannelOpusConfig config, + std::optional codec_pair_id = std::nullopt, + const FieldTrialsView* field_trials = nullptr); +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h b/pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h new file mode 100644 index 00000000..a24e28e0 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_CONFIG_H_ +#define API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_CONFIG_H_ + +#include +#include + +#include "api/audio_codecs/audio_decoder.h" + +namespace webrtc { +struct AudioDecoderMultiChannelOpusConfig { + // The number of channels that the decoder will output. + int num_channels; + + // Number of mono or stereo encoded Opus streams. + int num_streams; + + // Number of channel pairs coupled together, see RFC 7845 section + // 5.1.1. Has to be less than the number of streams. + int coupled_streams; + + // Channel mapping table, defines the mapping from encoded streams to output + // channels. See RFC 7845 section 5.1.1. + std::vector channel_mapping; + + bool IsOk() const { + if (num_channels < 1 || num_channels > AudioDecoder::kMaxNumberOfChannels || + num_streams < 0 || coupled_streams < 0) { + return false; + } + if (num_streams < coupled_streams) { + return false; + } + if (channel_mapping.size() != static_cast(num_channels)) { + return false; + } + + // Every mono stream codes one channel, every coupled stream codes two. This + // is the total coded channel count: + const int max_coded_channel = num_streams + coupled_streams; + for (const auto& x : channel_mapping) { + // Coded channels >= max_coded_channel don't exist. Except for 255, which + // tells Opus to put silence in output channel x. + if (x >= max_coded_channel && x != 255) { + return false; + } + } + + if (num_channels > 255 || max_coded_channel >= 255) { + return false; + } + return true; + } +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_CONFIG_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_opus.h b/pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_opus.h new file mode 100644 index 00000000..50cf80de --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/opus/audio_decoder_opus.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_OPUS_AUDIO_DECODER_OPUS_H_ +#define API_AUDIO_CODECS_OPUS_AUDIO_DECODER_OPUS_H_ + +#include +#include +#include + +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_decoder.h" +#include "api/audio_codecs/audio_format.h" +#include "api/environment/environment.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Opus decoder API for use as a template parameter to +// CreateAudioDecoderFactory<...>(). +struct RTC_EXPORT AudioDecoderOpus { + struct Config { + bool IsOk() const; // Checks if the values are currently OK. + int sample_rate_hz = 48000; + std::optional num_channels; + }; + static std::optional SdpToConfig(const SdpAudioFormat& audio_format); + static void AppendSupportedDecoders(std::vector* specs); + + static std::unique_ptr MakeAudioDecoder(const Environment& env, + Config config); + static std::unique_ptr MakeAudioDecoder( + const Environment& env, + Config config, + std::optional /*codec_pair_id*/) { + return MakeAudioDecoder(env, config); + } +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_OPUS_AUDIO_DECODER_OPUS_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h b/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h new file mode 100644 index 00000000..923e5c02 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_MULTI_CHANNEL_OPUS_H_ +#define API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_MULTI_CHANNEL_OPUS_H_ + +#include +#include +#include + +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_encoder.h" +#include "api/audio_codecs/audio_format.h" +#include "api/audio_codecs/opus/audio_encoder_multi_channel_opus_config.h" +#include "api/field_trials_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Opus encoder API for use as a template parameter to +// CreateAudioEncoderFactory<...>(). +struct RTC_EXPORT AudioEncoderMultiChannelOpus { + using Config = AudioEncoderMultiChannelOpusConfig; + static std::optional SdpToConfig(const SdpAudioFormat& audio_format); + static void AppendSupportedEncoders(std::vector* specs); + static AudioCodecInfo QueryAudioEncoder(const Config& config); + static std::unique_ptr MakeAudioEncoder( + const Config& config, + int payload_type, + std::optional codec_pair_id = std::nullopt, + const FieldTrialsView* field_trials = nullptr); +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_MULTI_CHANNEL_OPUS_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_multi_channel_opus_config.h b/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_multi_channel_opus_config.h new file mode 100644 index 00000000..8de80739 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_multi_channel_opus_config.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_MULTI_CHANNEL_OPUS_CONFIG_H_ +#define API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_MULTI_CHANNEL_OPUS_CONFIG_H_ + +#include + +#include + +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +struct RTC_EXPORT AudioEncoderMultiChannelOpusConfig { + static constexpr int kDefaultFrameSizeMs = 20; + + // Opus API allows a min bitrate of 500bps, but Opus documentation suggests + // bitrate should be in the range of 6000 to 510000, inclusive. + static constexpr int kMinBitrateBps = 6000; + static constexpr int kMaxBitrateBps = 510000; + + AudioEncoderMultiChannelOpusConfig(); + AudioEncoderMultiChannelOpusConfig(const AudioEncoderMultiChannelOpusConfig&); + ~AudioEncoderMultiChannelOpusConfig(); + AudioEncoderMultiChannelOpusConfig& operator=( + const AudioEncoderMultiChannelOpusConfig&); + + int frame_size_ms; + size_t num_channels; + enum class ApplicationMode { kVoip, kAudio }; + ApplicationMode application; + int bitrate_bps; + bool fec_enabled; + bool cbr_enabled; + bool dtx_enabled; + int max_playback_rate_hz; + std::vector supported_frame_lengths_ms; + + int complexity; + + // Number of mono/stereo Opus streams. + int num_streams; + + // Number of channel pairs coupled together, see RFC 7845 section + // 5.1.1. Has to be less than the number of streams + int coupled_streams; + + // Channel mapping table, defines the mapping from encoded streams to input + // channels. See RFC 7845 section 5.1.1. + std::vector channel_mapping; + + bool IsOk() const; +}; + +} // namespace webrtc +#endif // API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_MULTI_CHANNEL_OPUS_CONFIG_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_opus.h b/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_opus.h new file mode 100644 index 00000000..1a7a4ca2 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_opus.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_H_ +#define API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_H_ + +#include +#include +#include + +#include "api/audio_codecs/audio_encoder.h" +#include "api/audio_codecs/audio_encoder_factory.h" +#include "api/audio_codecs/audio_format.h" +#include "api/audio_codecs/opus/audio_encoder_opus_config.h" +#include "api/environment/environment.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Opus encoder API for use as a template parameter to +// CreateAudioEncoderFactory<...>(). +struct RTC_EXPORT AudioEncoderOpus { + using Config = AudioEncoderOpusConfig; + static std::optional SdpToConfig( + const SdpAudioFormat& audio_format); + static void AppendSupportedEncoders(std::vector* specs); + static AudioCodecInfo QueryAudioEncoder(const AudioEncoderOpusConfig& config); + static std::unique_ptr MakeAudioEncoder( + const Environment& env, + const AudioEncoderOpusConfig& config, + const AudioEncoderFactory::Options& options); +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_opus_config.h b/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_opus_config.h new file mode 100644 index 00000000..1fe5c18d --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/opus/audio_encoder_opus_config.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_CONFIG_H_ +#define API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_CONFIG_H_ + +#include + +#include +#include + +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +struct RTC_EXPORT AudioEncoderOpusConfig { + static constexpr int kDefaultFrameSizeMs = 20; + + // Opus API allows a min bitrate of 500bps, but Opus documentation suggests + // bitrate should be in the range of 6000 to 510000, inclusive. + static constexpr int kMinBitrateBps = 6000; + static constexpr int kMaxBitrateBps = 510000; + + AudioEncoderOpusConfig(); + AudioEncoderOpusConfig(const AudioEncoderOpusConfig&); + ~AudioEncoderOpusConfig(); + AudioEncoderOpusConfig& operator=(const AudioEncoderOpusConfig&); + + bool IsOk() const; // Checks if the values are currently OK. + + int frame_size_ms; + int sample_rate_hz; + size_t num_channels; + enum class ApplicationMode { kVoip, kAudio }; + ApplicationMode application; + + // NOTE: This member must always be set. + // TODO(kwiberg): Turn it into just an int. + std::optional bitrate_bps; + + bool fec_enabled; + bool cbr_enabled; + int max_playback_rate_hz; + + // `complexity` is used when the bitrate goes above + // `complexity_threshold_bps` + `complexity_threshold_window_bps`; + // `low_rate_complexity` is used when the bitrate falls below + // `complexity_threshold_bps` - `complexity_threshold_window_bps`. In the + // interval in the middle, we keep using the most recent of the two + // complexity settings. + int complexity; + int low_rate_complexity; + int complexity_threshold_bps; + int complexity_threshold_window_bps; + + bool dtx_enabled; + std::vector supported_frame_lengths_ms; + int uplink_bandwidth_update_interval_ms; + + // NOTE: This member isn't necessary, and will soon go away. See + // https://bugs.chromium.org/p/webrtc/issues/detail?id=7847 + int payload_type; +}; + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_CONFIG_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/opus_audio_decoder_factory.h b/pkg/apm/webrtc/api/audio_codecs/opus_audio_decoder_factory.h new file mode 100644 index 00000000..90917fbe --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/opus_audio_decoder_factory.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_OPUS_AUDIO_DECODER_FACTORY_H_ +#define API_AUDIO_CODECS_OPUS_AUDIO_DECODER_FACTORY_H_ + +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +// Creates a new factory that can create only Opus audio decoders. Works like +// CreateAudioDecoderFactory(), but is easier to use and is +// not inline because it isn't a template. +scoped_refptr CreateOpusAudioDecoderFactory(); + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_OPUS_AUDIO_DECODER_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/audio_codecs/opus_audio_encoder_factory.h b/pkg/apm/webrtc/api/audio_codecs/opus_audio_encoder_factory.h new file mode 100644 index 00000000..90c82c26 --- /dev/null +++ b/pkg/apm/webrtc/api/audio_codecs/opus_audio_encoder_factory.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_FACTORY_H_ +#define API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_FACTORY_H_ + +#include "api/audio_codecs/audio_encoder_factory.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +// Creates a new factory that can create only Opus audio encoders. Works like +// CreateAudioEncoderFactory(), but is easier to use and is +// not inline because it isn't a template. +scoped_refptr CreateOpusAudioEncoderFactory(); + +} // namespace webrtc + +#endif // API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/audio_options.h b/pkg/apm/webrtc/api/audio_options.h new file mode 100644 index 00000000..723ee0ff --- /dev/null +++ b/pkg/apm/webrtc/api/audio_options.h @@ -0,0 +1,81 @@ +/* + * 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_AUDIO_OPTIONS_H_ +#define API_AUDIO_OPTIONS_H_ + +#include +#include + +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Options that can be applied to a VoiceMediaChannel or a VoiceMediaEngine. +// Used to be flags, but that makes it hard to selectively apply options. +// We are moving all of the setting of options to structs like this, +// but some things currently still use flags. +struct RTC_EXPORT AudioOptions { + AudioOptions(); + ~AudioOptions(); + void SetAll(const AudioOptions& change); + + bool operator==(const AudioOptions& o) const; + bool operator!=(const AudioOptions& o) const { return !(*this == o); } + + std::string ToString() const; + + // Audio processing that attempts to filter away the output signal from + // later inbound pickup. + std::optional echo_cancellation; +#if defined(WEBRTC_IOS) + // Forces software echo cancellation on iOS. This is a temporary workaround + // (until Apple fixes the bug) for a device with non-functioning AEC. May + // improve performance on that particular device, but will cause unpredictable + // behavior in all other cases. See http://bugs.webrtc.org/8682. + std::optional ios_force_software_aec_HACK; +#endif + // Audio processing to adjust the sensitivity of the local mic dynamically. + std::optional auto_gain_control; + // Audio processing to filter out background noise. + std::optional noise_suppression; + // Audio processing to remove background noise of lower frequencies. + std::optional highpass_filter; + // Audio processing to swap the left and right channels. + std::optional stereo_swapping; + // Audio receiver jitter buffer (NetEq) max capacity in number of packets. + std::optional audio_jitter_buffer_max_packets; + // Audio receiver jitter buffer (NetEq) fast accelerate mode. + std::optional audio_jitter_buffer_fast_accelerate; + // Audio receiver jitter buffer (NetEq) minimum target delay in milliseconds. + std::optional audio_jitter_buffer_min_delay_ms; + // Enable audio network adaptor. + // TODO(webrtc:11717): Remove this API in favor of adaptivePtime in + // RtpEncodingParameters. + std::optional audio_network_adaptor; + // Config string for audio network adaptor. + std::optional audio_network_adaptor_config; + // Pre-initialize the ADM for recording when starting to send. Default to + // true. + // TODO(webrtc:13566): Remove this option. See issue for details. + std::optional init_recording_on_send; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace cricket { +using ::webrtc::AudioOptions; +} // namespace cricket +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // API_AUDIO_OPTIONS_H_ diff --git a/pkg/apm/webrtc/api/call/audio_sink.h b/pkg/apm/webrtc/api/call/audio_sink.h new file mode 100644 index 00000000..fec26593 --- /dev/null +++ b/pkg/apm/webrtc/api/call/audio_sink.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_CALL_AUDIO_SINK_H_ +#define API_CALL_AUDIO_SINK_H_ + +#include +#include + +namespace webrtc { + +// Represents a simple push audio sink. +class AudioSinkInterface { + public: + virtual ~AudioSinkInterface() {} + + struct Data { + Data(const int16_t* data, + size_t samples_per_channel, + int sample_rate, + size_t channels, + uint32_t timestamp) + : data(data), + samples_per_channel(samples_per_channel), + sample_rate(sample_rate), + channels(channels), + timestamp(timestamp) {} + + const int16_t* data; // The actual 16bit audio data. + size_t samples_per_channel; // Number of frames in the buffer. + int sample_rate; // Sample rate in Hz. + size_t channels; // Number of channels in the audio data. + uint32_t timestamp; // The RTP timestamp of the first sample. + }; + + virtual void OnData(const Data& audio) = 0; +}; + +} // namespace webrtc + +#endif // API_CALL_AUDIO_SINK_H_ diff --git a/pkg/apm/webrtc/api/call/bitrate_allocation.h b/pkg/apm/webrtc/api/call/bitrate_allocation.h new file mode 100644 index 00000000..4b4e5e7a --- /dev/null +++ b/pkg/apm/webrtc/api/call/bitrate_allocation.h @@ -0,0 +1,45 @@ +/* + * 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_ diff --git a/pkg/apm/webrtc/api/call/transport.h b/pkg/apm/webrtc/api/call/transport.h new file mode 100644 index 00000000..b2bc18ef --- /dev/null +++ b/pkg/apm/webrtc/api/call/transport.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_CALL_TRANSPORT_H_ +#define API_CALL_TRANSPORT_H_ + +#include + +#include "api/array_view.h" + +namespace webrtc { + +// TODO(holmer): Look into unifying this with the PacketOptions in +// asyncpacketsocket.h. +struct PacketOptions { + PacketOptions(); + PacketOptions(const PacketOptions&); + ~PacketOptions(); + + // Negative ids are invalid and should be interpreted + // as packet_id not being set. + int64_t packet_id = -1; + // Whether this is an audio or video packet, excluding retransmissions. + bool is_media = true; + bool included_in_feedback = false; + bool included_in_allocation = false; + bool send_as_ect1 = false; + // Whether this packet can be part of a packet batch at lower levels. + bool batchable = false; + // Whether this packet is the last of a batch. + bool last_packet_in_batch = false; +}; + +class Transport { + public: + virtual bool SendRtp(ArrayView packet, + const PacketOptions& options) = 0; + virtual bool SendRtcp(ArrayView packet) = 0; + + protected: + virtual ~Transport() {} +}; + +} // namespace webrtc + +#endif // API_CALL_TRANSPORT_H_ diff --git a/pkg/apm/webrtc/api/candidate.h b/pkg/apm/webrtc/api/candidate.h new file mode 100644 index 00000000..b02d0ac3 --- /dev/null +++ b/pkg/apm/webrtc/api/candidate.h @@ -0,0 +1,305 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_CANDIDATE_H_ +#define API_CANDIDATE_H_ + +#include +#include + +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/network_constants.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +enum class IceCandidateType : int { kHost, kSrflx, kPrflx, kRelay }; +RTC_EXPORT absl::string_view IceCandidateTypeToString(IceCandidateType); + +// TODO(tommi): Remove. No usage in WebRTC now, remove once downstream projects +// don't have reliance. +[[deprecated("Use IceCandidateType")]] static constexpr char LOCAL_PORT_TYPE[] = + "local"; +[[deprecated("Use IceCandidateType")]] static constexpr char STUN_PORT_TYPE[] = + "stun"; +[[deprecated("Use IceCandidateType")]] static constexpr char PRFLX_PORT_TYPE[] = + "prflx"; +[[deprecated("Use IceCandidateType")]] static constexpr char RELAY_PORT_TYPE[] = + "relay"; + +// TURN servers are limited to 32 in accordance with +// https://w3c.github.io/webrtc-pc/#dom-rtcconfiguration-iceservers +static constexpr size_t kMaxTurnServers = 32; + +// Candidate for ICE based connection discovery. +class RTC_EXPORT Candidate { + public: + Candidate(); + Candidate(int component, + absl::string_view protocol, + const SocketAddress& address, + uint32_t priority, + absl::string_view username, + absl::string_view password, + IceCandidateType type, + uint32_t generation, + absl::string_view foundation, + uint16_t network_id = 0, + uint16_t network_cost = 0); + Candidate(const Candidate&); + ~Candidate(); + + // 8 character long randomized ID string for logging purposes. + const std::string& id() const { return id_; } + // Generates a new, 8 character long, id. + void generate_id(); + + int component() const { return component_; } + void set_component(int component) { component_ = component; } + + const std::string& protocol() const { return protocol_; } + + // Valid protocol values are: + // UDP_PROTOCOL_NAME, TCP_PROTOCOL_NAME, SSLTCP_PROTOCOL_NAME, + // TLS_PROTOCOL_NAME. + void set_protocol(absl::string_view protocol) { Assign(protocol_, protocol); } + + // The protocol used to talk to relay. + const std::string& relay_protocol() const { return relay_protocol_; } + + // Valid protocol values are: + // UDP_PROTOCOL_NAME, TCP_PROTOCOL_NAME, SSLTCP_PROTOCOL_NAME, + // TLS_PROTOCOL_NAME. + void set_relay_protocol(absl::string_view protocol) { + Assign(relay_protocol_, protocol); + } + + const SocketAddress& address() const { return address_; } + void set_address(const SocketAddress& address) { address_ = address; } + + uint32_t priority() const { return priority_; } + void set_priority(const uint32_t priority) { priority_ = priority; } + + // TODO(honghaiz): Change to usernameFragment or ufrag. + const std::string& username() const { return username_; } + void set_username(absl::string_view username) { Assign(username_, username); } + + const std::string& password() const { return password_; } + void set_password(absl::string_view password) { Assign(password_, password); } + + IceCandidateType type() const { return type_; } + + // Returns the name of the candidate type as specified in + // https://datatracker.ietf.org/doc/html/rfc5245#section-15.1 + absl::string_view type_name() const; + + // Setting the type requires a constant string (e.g. + // webrtc::LOCAL_PORT_TYPE). The type should really be an enum rather than a + // string, but until we make that change the lifetime attribute helps us lock + // things down. See also the `Port` class. + void set_type(IceCandidateType type) { type_ = type; } + + // Simple checkers for checking the candidate type without dependency on the + // IceCandidateType enum. The `is_local()` and `is_stun()` names are legacy + // names and should now more accurately be `is_host()` and `is_srflx()`. + bool is_local() const; + bool is_stun() const; + bool is_prflx() const; + bool is_relay() const; + + // Returns the type preference, a value between 0-126 inclusive, with 0 being + // the lowest preference value, as described in RFC 5245. + // https://datatracker.ietf.org/doc/html/rfc5245#section-4.1.2.1 + int type_preference() const { + // From https://datatracker.ietf.org/doc/html/rfc5245#section-4.1.4 : + // It is RECOMMENDED that default candidates be chosen based on the + // likelihood of those candidates to work with the peer that is being + // contacted. + // I.e. it is recommended that relayed > reflexive > host. + if (is_local()) + return 1; // Host. + if (is_stun()) + return 2; // Reflexive. + if (is_relay()) + return 3; // Relayed. + return 0; // Unknown, lowest preference. + } + + const std::string& network_name() const { return network_name_; } + void set_network_name(absl::string_view network_name) { + Assign(network_name_, network_name); + } + + AdapterType network_type() const { return network_type_; } + void set_network_type(AdapterType network_type) { + network_type_ = network_type; + } + + AdapterType underlying_type_for_vpn() const { + return underlying_type_for_vpn_; + } + void set_underlying_type_for_vpn(AdapterType network_type) { + underlying_type_for_vpn_ = network_type; + } + + // Candidates in a new generation replace those in the old generation. + uint32_t generation() const { return generation_; } + void set_generation(uint32_t generation) { generation_ = generation; } + + // `network_cost` measures the cost/penalty of using this candidate. A network + // cost of 0 indicates this candidate can be used freely. A value of + // webrtc::kNetworkCostMax indicates it should be used only as the last + // resort. + void set_network_cost(uint16_t network_cost) { + RTC_DCHECK_LE(network_cost, webrtc::kNetworkCostMax); + network_cost_ = network_cost; + } + uint16_t network_cost() const { return network_cost_; } + + // An ID assigned to the network hosting the candidate. + uint16_t network_id() const { return network_id_; } + void set_network_id(uint16_t network_id) { network_id_ = network_id; } + + // From RFC 5245, section-7.2.1.3: + // The foundation of the candidate is set to an arbitrary value, different + // from the foundation for all other remote candidates. + // Note: Use ComputeFoundation to populate this value. + const std::string& foundation() const { return foundation_; } + + // TODO(tommi): Deprecate in favor of ComputeFoundation. + // For situations where serializing/deserializing a candidate is needed, + // the constructor can be used to inject a value for the foundation. + void set_foundation(absl::string_view foundation) { + Assign(foundation_, foundation); + } + + const SocketAddress& related_address() const { return related_address_; } + void set_related_address(const SocketAddress& related_address) { + related_address_ = related_address; + } + const std::string& tcptype() const { return tcptype_; } + void set_tcptype(absl::string_view tcptype) { Assign(tcptype_, tcptype); } + + // The name of the transport channel of this candidate. + // TODO(phoglund): remove. + const std::string& transport_name() const { return transport_name_; } + void set_transport_name(absl::string_view transport_name) { + Assign(transport_name_, transport_name); + } + + // The URL of the ICE server which this candidate is gathered from. + const std::string& url() const { return url_; } + void set_url(absl::string_view url) { Assign(url_, url); } + + // Determines whether this candidate is equivalent to the given one. + bool IsEquivalent(const Candidate& c) const; + + // Determines whether this candidate can be considered equivalent to the + // given one when looking for a matching candidate to remove. + bool MatchesForRemoval(const Candidate& c) const; + + std::string ToString() const { return ToStringInternal(false); } + + std::string ToSensitiveString() const { return ToStringInternal(true); } + + uint32_t GetPriority(uint32_t type_preference, + int network_adapter_preference, + int relay_preference, + bool adjust_local_preference) const; + + bool operator==(const Candidate& o) const; + bool operator!=(const Candidate& o) const; + + // Returns a sanitized copy configured by the given booleans. If + // `use_host_address` is true, the returned copy has its IP removed from + // `address()`, which leads `address()` to be a hostname address. If + // `filter_related_address`, the returned copy has its related address reset + // to the wildcard address (i.e. 0.0.0.0 for IPv4 and :: for IPv6). Note that + // setting both booleans to false returns an identical copy to the original + // candidate. + // The username fragment may be filtered, e.g. for prflx candidates before + // any remote ice parameters have been set. + [[deprecated("Use variant with filter_ufrag")]] Candidate ToSanitizedCopy( + bool use_hostname_address, + bool filter_related_address) const { + return ToSanitizedCopy(use_hostname_address, filter_related_address, false); + } + Candidate ToSanitizedCopy(bool use_hostname_address, + bool filter_related_address, + bool filter_ufrag) const; + + // Computes and populates the `foundation()` field. + // Foundation: An arbitrary string that is the same for two candidates + // that have the same type, base IP address, protocol (UDP, TCP, + // etc.), and STUN or TURN server. If any of these are different, + // then the foundation will be different. Two candidate pairs with + // the same foundation pairs are likely to have similar network + // characteristics. Foundations are used in the frozen algorithm. + // A session wide (peerconnection) tie-breaker is applied to the foundation, + // adds additional randomness and must be the same for all candidates. + void ComputeFoundation(const SocketAddress& base_address, + uint64_t tie_breaker); + + // https://www.rfc-editor.org/rfc/rfc5245#section-7.2.1.3 + // Call to populate the foundation field for a new peer reflexive remote + // candidate. The type of the candidate must be "prflx". + // The foundation of the candidate is set to an arbitrary value, different + // from the foundation for all other remote candidates. + void ComputePrflxFoundation(); + + private: + // TODO(bugs.webrtc.org/13220): With C++17, we get a std::string assignment + // operator accepting any object implicitly convertible to std::string_view, + // and then we don't need this workaround. + static void Assign(std::string& s, absl::string_view view); + std::string ToStringInternal(bool sensitive) const; + + std::string id_; + int component_; + std::string protocol_; + std::string relay_protocol_; + SocketAddress address_; + uint32_t priority_; + std::string username_; + std::string password_; + IceCandidateType type_ = IceCandidateType::kHost; + std::string network_name_; + AdapterType network_type_; + AdapterType underlying_type_for_vpn_; + uint32_t generation_; + std::string foundation_; + SocketAddress related_address_; + std::string tcptype_; + std::string transport_name_; + uint16_t network_id_; + uint16_t network_cost_; + std::string url_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace cricket { +using ::webrtc::Candidate; +using ::webrtc::kMaxTurnServers; +using ::webrtc::LOCAL_PORT_TYPE; +using ::webrtc::PRFLX_PORT_TYPE; +using ::webrtc::RELAY_PORT_TYPE; +using ::webrtc::STUN_PORT_TYPE; +} // namespace cricket +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // API_CANDIDATE_H_ diff --git a/pkg/apm/webrtc/api/create_peerconnection_factory.h b/pkg/apm/webrtc/api/create_peerconnection_factory.h new file mode 100644 index 00000000..68311008 --- /dev/null +++ b/pkg/apm/webrtc/api/create_peerconnection_factory.h @@ -0,0 +1,53 @@ +/* + * 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_CREATE_PEERCONNECTION_FACTORY_H_ +#define API_CREATE_PEERCONNECTION_FACTORY_H_ +// IWYU pragma: no_include "rtc_base/thread.h" + +#include + +#include "api/audio/audio_device.h" +#include "api/audio/audio_mixer.h" +#include "api/audio/audio_processing.h" +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/audio_codecs/audio_encoder_factory.h" +#include "api/field_trials_view.h" +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" +#include "api/video_codecs/video_decoder_factory.h" +#include "api/video_codecs/video_encoder_factory.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread.h" + +namespace webrtc { +class AudioFrameProcessor; + +// Create a new instance of PeerConnectionFactoryInterface with optional video +// codec factories. These video factories represents all video codecs, i.e. no +// extra internal video codecs will be added. +RTC_EXPORT scoped_refptr +CreatePeerConnectionFactory( + Thread* network_thread, + Thread* worker_thread, + Thread* signaling_thread, + scoped_refptr default_adm, + scoped_refptr audio_encoder_factory, + scoped_refptr audio_decoder_factory, + std::unique_ptr video_encoder_factory, + std::unique_ptr video_decoder_factory, + scoped_refptr audio_mixer, + scoped_refptr audio_processing, + std::unique_ptr audio_frame_processor = nullptr, + std::unique_ptr field_trials = nullptr); + +} // namespace webrtc + +#endif // API_CREATE_PEERCONNECTION_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/crypto/crypto_options.h b/pkg/apm/webrtc/api/crypto/crypto_options.h new file mode 100644 index 00000000..a9374907 --- /dev/null +++ b/pkg/apm/webrtc/api/crypto/crypto_options.h @@ -0,0 +1,73 @@ +/* + * 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_CRYPTO_CRYPTO_OPTIONS_H_ +#define API_CRYPTO_CRYPTO_OPTIONS_H_ + +#include + +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// CryptoOptions defines advanced cryptographic settings for native WebRTC. +// These settings must be passed into PeerConnectionFactoryInterface::Options +// and are only applicable to native use cases of WebRTC. +struct RTC_EXPORT CryptoOptions { + CryptoOptions(); + CryptoOptions(const CryptoOptions& other); + ~CryptoOptions(); + + // Helper method to return an instance of the CryptoOptions with GCM crypto + // suites disabled. This method should be used instead of depending on current + // default values set by the constructor. + static CryptoOptions NoGcm(); + + // Returns a list of the supported DTLS-SRTP Crypto suites based on this set + // of crypto options. + std::vector GetSupportedDtlsSrtpCryptoSuites() const; + + bool operator==(const CryptoOptions& other) const; + bool operator!=(const CryptoOptions& other) const; + + // SRTP Related Peer Connection options. + struct Srtp { + // Enable GCM crypto suites from RFC 7714 for SRTP. GCM will only be used + // if both sides enable it. + bool enable_gcm_crypto_suites = true; + + // If set to true, the (potentially insecure) crypto cipher + // kSrtpAes128CmSha1_32 will be included in the list of supported ciphers + // during negotiation. It will only be used if both peers support it and no + // other ciphers get preferred. + bool enable_aes128_sha1_32_crypto_cipher = false; + + // The most commonly used cipher. Can be disabled, mostly for testing + // purposes. + bool enable_aes128_sha1_80_crypto_cipher = true; + + // This feature enables encrypting RTP header extensions using RFC 6904, if + // requested. For this to work the Chromium field trial + // `kWebRtcEncryptedRtpHeaderExtensions` must be enabled. + bool enable_encrypted_rtp_header_extensions = true; + } srtp; + + // Options to be used when the FrameEncryptor / FrameDecryptor APIs are used. + struct SFrame { + // If set all RtpSenders must have an FrameEncryptor attached to them before + // they are allowed to send packets. All RtpReceivers must have a + // FrameDecryptor attached to them before they are able to receive packets. + bool require_frame_encryption = false; + } sframe; +}; + +} // namespace webrtc + +#endif // API_CRYPTO_CRYPTO_OPTIONS_H_ diff --git a/pkg/apm/webrtc/api/crypto/frame_crypto_transformer.h b/pkg/apm/webrtc/api/crypto/frame_crypto_transformer.h new file mode 100644 index 00000000..dd8e3358 --- /dev/null +++ b/pkg/apm/webrtc/api/crypto/frame_crypto_transformer.h @@ -0,0 +1,524 @@ +/* + * Copyright 2022 LiveKit + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WEBRTC_FRAME_CRYPTOR_TRANSFORMER_H_ +#define WEBRTC_FRAME_CRYPTOR_TRANSFORMER_H_ + +#include + +#include "api/frame_transformer_interface.h" +#include "api/make_ref_counted.h" +#include "api/rtc_error.h" +#include "api/task_queue/pending_task_safety_flag.h" +#include "api/task_queue/task_queue_base.h" +#include "rtc_base/buffer.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread.h" + +int DerivePBKDF2KeyFromRawKey(const std::vector raw_key, + const std::vector& salt, + unsigned int optional_length_bits, + std::vector* derived_key); + +namespace webrtc { + +const size_t DEFAULT_KEYRING_SIZE = 16; +const size_t MAX_KEYRING_SIZE = 255; + +class ParticipantKeyHandler; + +struct KeyProviderOptions { + bool shared_key; + std::vector ratchet_salt; + std::vector uncrypted_magic_bytes; + int ratchet_window_size; + int failure_tolerance; + // key ring size should be between 1 and 255 + int key_ring_size; + bool discard_frame_when_cryptor_not_ready; + KeyProviderOptions() + : shared_key(false), + ratchet_window_size(0), + failure_tolerance(-1), + key_ring_size(DEFAULT_KEYRING_SIZE), + discard_frame_when_cryptor_not_ready(false) {} + KeyProviderOptions(KeyProviderOptions& copy) + : shared_key(copy.shared_key), + ratchet_salt(copy.ratchet_salt), + uncrypted_magic_bytes(copy.uncrypted_magic_bytes), + ratchet_window_size(copy.ratchet_window_size), + failure_tolerance(copy.failure_tolerance), + key_ring_size(copy.key_ring_size) {} +}; + +class KeyProvider : public webrtc::RefCountInterface { + public: + virtual bool SetSharedKey(int key_index, std::vector key) = 0; + + virtual const webrtc::scoped_refptr GetSharedKey( + const std::string participant_id) = 0; + + virtual const std::vector RatchetSharedKey(int key_index) = 0; + + virtual const std::vector ExportSharedKey(int key_index) const = 0; + + virtual bool SetKey(const std::string participant_id, + int key_index, + std::vector key) = 0; + + virtual const webrtc::scoped_refptr GetKey( + const std::string participant_id) const = 0; + + virtual const std::vector RatchetKey( + const std::string participant_id, + int key_index) = 0; + + virtual const std::vector ExportKey(const std::string participant_id, + int key_index) const = 0; + + virtual void SetSifTrailer(const std::vector trailer) = 0; + + virtual KeyProviderOptions& options() = 0; + + protected: + virtual ~KeyProvider() {} +}; + +class ParticipantKeyHandler : public webrtc::RefCountInterface { + public: + struct KeySet : public webrtc::RefCountInterface { + std::vector material; + std::vector encryption_key; + KeySet(std::vector material, std::vector encryptionKey) + : material(material), encryption_key(encryptionKey) {} + }; + + public: + ParticipantKeyHandler(KeyProvider* key_provider) + : key_provider_(key_provider) { + int key_ring_size = key_provider_->options().key_ring_size; + if (key_ring_size <= 0) { + key_ring_size = DEFAULT_KEYRING_SIZE; + } else if (key_ring_size > (int)MAX_KEYRING_SIZE) { + // Keyring size needs to be between 1 and 256 + key_ring_size = MAX_KEYRING_SIZE; + } + crypto_key_ring_.resize(key_ring_size); + } + + virtual ~ParticipantKeyHandler() = default; + + webrtc::scoped_refptr Clone() { + auto clone = webrtc::make_ref_counted(key_provider_); + clone->crypto_key_ring_ = crypto_key_ring_; + clone->current_key_index_ = current_key_index_; + clone->has_valid_key_ = has_valid_key_; + return clone; + } + + virtual std::vector RatchetKey(int key_index) { + auto key_set = GetKeySet(key_index); + if (!key_set) { + return std::vector(); + } + auto current_material = key_set->material; + std::vector new_material; + if (DerivePBKDF2KeyFromRawKey(current_material, + key_provider_->options().ratchet_salt, 256, + &new_material) != 0) { + return std::vector(); + } + SetKeyFromMaterial(new_material, + key_index != -1 ? key_index : current_key_index_); + SetHasValidKey(); + return new_material; + } + + virtual webrtc::scoped_refptr GetKeySet(int key_index) { + webrtc::MutexLock lock(&mutex_); + return crypto_key_ring_[key_index != -1 ? key_index : current_key_index_]; + } + + virtual void SetKey(std::vector password, int key_index) { + SetKeyFromMaterial(password, key_index); + SetHasValidKey(); + } + + std::vector RatchetKeyMaterial( + std::vector current_material) { + std::vector new_material; + if (DerivePBKDF2KeyFromRawKey(current_material, + key_provider_->options().ratchet_salt, 256, + &new_material) != 0) { + return std::vector(); + } + return new_material; + } + + webrtc::scoped_refptr DeriveKeys(std::vector password, + std::vector ratchet_salt, + unsigned int optional_length_bits) { + std::vector derived_key; + if (DerivePBKDF2KeyFromRawKey(password, ratchet_salt, optional_length_bits, + &derived_key) == 0) { + return webrtc::make_ref_counted(password, derived_key); + } + return nullptr; + } + + bool HasValidKey() { + webrtc::MutexLock lock(&mutex_); + return has_valid_key_; + } + + void SetHasValidKey() { + webrtc::MutexLock lock(&mutex_); + decryption_failure_count_ = 0; + has_valid_key_ = true; + } + + void SetKeyFromMaterial(std::vector password, int key_index) { + webrtc::MutexLock lock(&mutex_); + if (key_index >= 0) { + current_key_index_ = key_index % crypto_key_ring_.size(); + } + crypto_key_ring_[current_key_index_] = + DeriveKeys(password, key_provider_->options().ratchet_salt, 128); + } + + bool DecryptionFailure() { + webrtc::MutexLock lock(&mutex_); + if (key_provider_->options().failure_tolerance < 0) { + return false; + } + decryption_failure_count_ += 1; + + if (decryption_failure_count_ > + key_provider_->options().failure_tolerance) { + has_valid_key_ = false; + return true; + } + return false; + } + + private: + bool has_valid_key_ = false; + int decryption_failure_count_ = 0; + mutable webrtc::Mutex mutex_; + int current_key_index_ = 0; + KeyProvider* key_provider_; + std::vector> crypto_key_ring_; +}; + +class DefaultKeyProviderImpl : public KeyProvider { + public: + DefaultKeyProviderImpl(KeyProviderOptions options) : options_(options) {} + ~DefaultKeyProviderImpl() override = default; + + /// Set the shared key. + bool SetSharedKey(int key_index, std::vector key) override { + webrtc::MutexLock lock(&mutex_); + if (options_.shared_key) { + if (keys_.find("shared") == keys_.end()) { + keys_["shared"] = webrtc::make_ref_counted(this); + } + + auto key_handler = keys_["shared"]; + key_handler->SetKey(key, key_index); + + for (auto& key_pair : keys_) { + if (key_pair.first != "shared") { + key_pair.second->SetKey(key, key_index); + } + } + return true; + } + return false; + } + + const std::vector RatchetSharedKey(int key_index) override { + webrtc::MutexLock lock(&mutex_); + auto it = keys_.find("shared"); + if (it == keys_.end()) { + return std::vector(); + } + auto new_key = it->second->RatchetKey(key_index); + if (options_.shared_key) { + for (auto& key_pair : keys_) { + if (key_pair.first != "shared") { + key_pair.second->SetKey(new_key, key_index); + } + } + } + return new_key; + } + + const std::vector ExportSharedKey(int key_index) const override { + webrtc::MutexLock lock(&mutex_); + auto it = keys_.find("shared"); + if (it == keys_.end()) { + return std::vector(); + } + auto key_set = it->second->GetKeySet(key_index); + if (key_set) { + return key_set->material; + } + return std::vector(); + } + + const webrtc::scoped_refptr GetSharedKey( + const std::string participant_id) override { + webrtc::MutexLock lock(&mutex_); + if (options_.shared_key && keys_.find("shared") != keys_.end()) { + auto shared_key_handler = keys_["shared"]; + if (keys_.find(participant_id) != keys_.end()) { + return keys_[participant_id]; + } else { + auto key_handler_clone = shared_key_handler->Clone(); + keys_[participant_id] = key_handler_clone; + return key_handler_clone; + } + } + return nullptr; + } + + /// Set the key at the given index. + bool SetKey(const std::string participant_id, + int index, + std::vector key) override { + webrtc::MutexLock lock(&mutex_); + + if (keys_.find(participant_id) == keys_.end()) { + keys_[participant_id] = + webrtc::make_ref_counted(this); + } + + auto key_handler = keys_[participant_id]; + key_handler->SetKey(key, index); + return true; + } + + const webrtc::scoped_refptr GetKey( + const std::string participant_id) const override { + webrtc::MutexLock lock(&mutex_); + + if (keys_.find(participant_id) == keys_.end()) { + return nullptr; + } + + return keys_.find(participant_id)->second; + } + + const std::vector RatchetKey(const std::string participant_id, + int key_index) override { + auto key_handler = GetKey(participant_id); + if (key_handler) { + return key_handler->RatchetKey(key_index); + } + return std::vector(); + } + + const std::vector ExportKey(const std::string participant_id, + int key_index) const override { + auto key_handler = GetKey(participant_id); + if (key_handler) { + auto key_set = key_handler->GetKeySet(key_index); + if (key_set) { + return key_set->material; + } + } + return std::vector(); + } + + void SetSifTrailer(const std::vector trailer) override { + webrtc::MutexLock lock(&mutex_); + options_.uncrypted_magic_bytes = trailer; + } + + KeyProviderOptions& options() override { return options_; } + + private: + mutable webrtc::Mutex mutex_; + KeyProviderOptions options_; + std::unordered_map> + keys_; +}; + +enum FrameCryptionState { + kNew = 0, + kOk, + kEncryptionFailed, + kDecryptionFailed, + kMissingKey, + kKeyRatcheted, + kInternalError, +}; + +class FrameCryptorTransformerObserver : public webrtc::RefCountInterface { + public: + virtual void OnFrameCryptionStateChanged(const std::string participant_id, + FrameCryptionState error) = 0; + + protected: + virtual ~FrameCryptorTransformerObserver() {} +}; + +class RTC_EXPORT FrameCryptorTransformer + : public webrtc::RefCountedObject { + public: + enum class MediaType { + kAudioFrame = 0, + kVideoFrame, + }; + + enum class Algorithm { + kAesGcm = 0, + kAesCbc, + }; + + explicit FrameCryptorTransformer( + rtc::Thread* signaling_thread, + const std::string participant_id, + MediaType type, + Algorithm algorithm, + webrtc::scoped_refptr key_provider); + ~FrameCryptorTransformer(); + virtual void RegisterFrameCryptorTransformerObserver( + webrtc::scoped_refptr observer) { + webrtc::MutexLock lock(&mutex_); + observer_ = observer; + } + + virtual void UnRegisterFrameCryptorTransformerObserver() { + webrtc::MutexLock lock(&mutex_); + observer_ = nullptr; + } + + virtual void SetKeyIndex(int index) { + webrtc::MutexLock lock(&mutex_); + key_index_ = index; + } + + virtual int key_index() const { return key_index_; } + + virtual void SetEnabled(bool enabled) { + webrtc::MutexLock lock(&mutex_); + enabled_cryption_ = enabled; + } + virtual bool enabled() const { + webrtc::MutexLock lock(&mutex_); + return enabled_cryption_; + } + virtual const std::string participant_id() const { return participant_id_; } + + protected: + virtual void RegisterTransformedFrameCallback( + webrtc::scoped_refptr callback) + override { + webrtc::MutexLock lock(&sink_mutex_); + sink_callback_ = callback; + } + virtual void UnregisterTransformedFrameCallback() override { + webrtc::MutexLock lock(&sink_mutex_); + sink_callback_ = nullptr; + } + virtual void RegisterTransformedFrameSinkCallback( + webrtc::scoped_refptr callback, + uint32_t ssrc) override { + webrtc::MutexLock lock(&sink_mutex_); + sink_callbacks_[ssrc] = callback; + } + virtual void UnregisterTransformedFrameSinkCallback(uint32_t ssrc) override { + webrtc::MutexLock lock(&sink_mutex_); + auto it = sink_callbacks_.find(ssrc); + if (it != sink_callbacks_.end()) { + sink_callbacks_.erase(it); + } + } + + virtual void Transform( + std::unique_ptr frame) override; + + private: + void encryptFrame(std::unique_ptr frame); + void decryptFrame(std::unique_ptr frame); + void onFrameCryptionStateChanged(FrameCryptionState error); + rtc::Buffer makeIv(uint32_t ssrc, uint32_t timestamp); + uint8_t getIvSize(); + + private: + TaskQueueBase* const signaling_thread_; + std::unique_ptr thread_; + std::string participant_id_; + mutable webrtc::Mutex mutex_; + mutable webrtc::Mutex sink_mutex_; + bool enabled_cryption_ RTC_GUARDED_BY(mutex_) = false; + MediaType type_; + Algorithm algorithm_; + webrtc::scoped_refptr sink_callback_; + std::map> + sink_callbacks_; + int key_index_ = 0; + std::map send_counts_; + webrtc::scoped_refptr key_provider_; + webrtc::scoped_refptr observer_; + FrameCryptionState last_enc_error_ = FrameCryptionState::kNew; + FrameCryptionState last_dec_error_ = FrameCryptionState::kNew; +}; + +class RTC_EXPORT EncryptedPacket : public webrtc::RefCountInterface { + public: + EncryptedPacket() = default; + EncryptedPacket(std::vector data, + std::vector iv, + uint8_t key_index) + : data(data), iv(iv), key_index(key_index) {} + ~EncryptedPacket() = default; + + std::vector data; + std::vector iv; + uint8_t key_index = 0; +}; + +class RTC_EXPORT DataPacketCryptor : public webrtc::RefCountInterface { + public: + DataPacketCryptor(FrameCryptorTransformer::Algorithm algorithm, + webrtc::scoped_refptr key_provider); + ~DataPacketCryptor(); + + virtual RTCErrorOr> Encrypt( + const std::string participant_id, + int key_index, + const std::vector& data); + + virtual RTCErrorOr> Decrypt( + const std::string participant_id, + const webrtc::scoped_refptr encryptedPacket); + + private: + rtc::Buffer makeIv(uint32_t timestamp); + + private: + FrameCryptorTransformer::Algorithm algorithm_; + webrtc::scoped_refptr key_provider_; + uint32_t send_count_ = 0; + mutable webrtc::Mutex mutex_; +}; + +} // namespace webrtc + +#endif // WEBRTC_FRAME_CRYPTOR_TRANSFORMER_H_ diff --git a/pkg/apm/webrtc/api/crypto/frame_decryptor_interface.h b/pkg/apm/webrtc/api/crypto/frame_decryptor_interface.h new file mode 100644 index 00000000..7ed165fc --- /dev/null +++ b/pkg/apm/webrtc/api/crypto/frame_decryptor_interface.h @@ -0,0 +1,78 @@ +/* + * 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_CRYPTO_FRAME_DECRYPTOR_INTERFACE_H_ +#define API_CRYPTO_FRAME_DECRYPTOR_INTERFACE_H_ + +#include +#include +#include + +#include "api/array_view.h" +#include "api/media_types.h" +#include "api/ref_count.h" + +namespace webrtc { + +// FrameDecryptorInterface allows users to provide a custom decryption +// implementation for all incoming audio and video frames. The user must also +// provide a FrameEncryptorInterface to be able to encrypt the frames being +// sent out of the device. Note this is an additional layer of encyrption in +// addition to the standard SRTP mechanism and is not intended to be used +// without it. You may assume that this interface will have the same lifetime +// as the RTPReceiver it is attached to. It must only be attached to one +// RTPReceiver. Additional data may be null. +class FrameDecryptorInterface : public RefCountInterface { + public: + // The Status enum represents all possible states that can be + // returned when attempting to decrypt a frame. kRecoverable indicates that + // there was an error with the given frame and so it should not be passed to + // the decoder, however it hints that the receive stream is still decryptable + // which is important for determining when to send key frame requests + // kUnknown should never be returned by the implementor. + enum class Status { kOk, kRecoverable, kFailedToDecrypt, kUnknown }; + + struct Result { + Result(Status status, size_t bytes_written) + : status(status), bytes_written(bytes_written) {} + + bool IsOk() const { return status == Status::kOk; } + + const Status status; + const size_t bytes_written; + }; + + ~FrameDecryptorInterface() override {} + + // Attempts to decrypt the encrypted frame. You may assume the frame size will + // be allocated to the size returned from GetMaxPlaintextSize. You may assume + // that the frames are in order if SRTP is enabled. The stream is not provided + // here and it is up to the implementor to transport this information to the + // receiver if they care about it. You must set bytes_written to how many + // bytes you wrote to in the frame buffer. kOk must be returned if successful, + // kRecoverable should be returned if the failure was due to something other + // than a decryption failure. kFailedToDecrypt should be returned in all other + // cases. + virtual Result Decrypt(webrtc::MediaType media_type, + const std::vector& csrcs, + ArrayView additional_data, + ArrayView encrypted_frame, + ArrayView frame) = 0; + + // Returns the total required length in bytes for the output of the + // decryption. This can be larger than the actual number of bytes you need but + // must never be smaller as it informs the size of the frame buffer. + virtual size_t GetMaxPlaintextByteSize(webrtc::MediaType media_type, + size_t encrypted_frame_size) = 0; +}; + +} // namespace webrtc + +#endif // API_CRYPTO_FRAME_DECRYPTOR_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/crypto/frame_encryptor_interface.h b/pkg/apm/webrtc/api/crypto/frame_encryptor_interface.h new file mode 100644 index 00000000..9a2b1709 --- /dev/null +++ b/pkg/apm/webrtc/api/crypto/frame_encryptor_interface.h @@ -0,0 +1,57 @@ +/* + * 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_CRYPTO_FRAME_ENCRYPTOR_INTERFACE_H_ +#define API_CRYPTO_FRAME_ENCRYPTOR_INTERFACE_H_ + +#include +#include + +#include "api/array_view.h" +#include "api/media_types.h" +#include "api/ref_count.h" + +namespace webrtc { + +// FrameEncryptorInterface allows users to provide a custom encryption +// implementation to encrypt all outgoing audio and video frames. The user must +// also provide a FrameDecryptorInterface to be able to decrypt the frames on +// the receiving device. Note this is an additional layer of encryption in +// addition to the standard SRTP mechanism and is not intended to be used +// without it. Implementations of this interface will have the same lifetime as +// the RTPSenders it is attached to. Additional data may be null. +class FrameEncryptorInterface : public RefCountInterface { + public: + ~FrameEncryptorInterface() override {} + + // Attempts to encrypt the provided frame. You may assume the encrypted_frame + // will match the size returned by GetMaxCiphertextByteSize for a give frame. + // You may assume that the frames will arrive in order if SRTP is enabled. + // The ssrc will simply identify which stream the frame is travelling on. You + // must set bytes_written to the number of bytes you wrote in the + // encrypted_frame. 0 must be returned if successful all other numbers can be + // selected by the implementer to represent error codes. + virtual int Encrypt(webrtc::MediaType media_type, + uint32_t ssrc, + ArrayView additional_data, + ArrayView frame, + ArrayView encrypted_frame, + size_t* bytes_written) = 0; + + // Returns the total required length in bytes for the output of the + // encryption. This can be larger than the actual number of bytes you need but + // must never be smaller as it informs the size of the encrypted_frame buffer. + virtual size_t GetMaxCiphertextByteSize(webrtc::MediaType media_type, + size_t frame_size) = 0; +}; + +} // namespace webrtc + +#endif // API_CRYPTO_FRAME_ENCRYPTOR_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/data_channel_event_observer_interface.h b/pkg/apm/webrtc/api/data_channel_event_observer_interface.h new file mode 100644 index 00000000..f9b59020 --- /dev/null +++ b/pkg/apm/webrtc/api/data_channel_event_observer_interface.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2025 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can 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_DATA_CHANNEL_EVENT_OBSERVER_INTERFACE_H_ +#define API_DATA_CHANNEL_EVENT_OBSERVER_INTERFACE_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" + +namespace webrtc { + +// TODO: issues.chromium.org/407785197 - Maybe update the observer to also +// notify on controll messages as well. +// TODO: issues.chromium.org/407785197 - Remove comment below when DataChannel +// logging has been launched. +// NOTE: This class is still under development and may change without notice. +class DataChannelEventObserverInterface { + public: + virtual ~DataChannelEventObserverInterface() = default; + + class Message { + public: + enum class Direction { kSend, kReceive }; + enum class DataType { kString, kBinary }; + + // When `direction` is `kSend` the timestamp represent when the message was + // handed over to the transport, if `direction` is `kReceive` then it + // represent when the message was received from the transport. + int64_t unix_timestamp_ms() const { return unix_timestamp_; } + void set_unix_timestamp_ms(int64_t timestamp) { + unix_timestamp_ = timestamp; + } + + int datachannel_id() const { return datachannel_id_; } + void set_datachannel_id(int id) { datachannel_id_ = id; } + + absl::string_view label() const { return label_; } + void set_label(absl::string_view label) { label_ = std::string(label); } + + Direction direction() const { return direction_; } + void set_direction(Direction direction) { direction_ = direction; } + + DataType data_type() const { return data_type_; } + void set_data_type(DataType type) { data_type_ = type; } + + const std::vector& data() const { return data_; } + void set_data(ArrayView d) { + data_.assign(d.begin(), d.end()); + } + + private: + int64_t unix_timestamp_; + int datachannel_id_; + std::string label_; + Direction direction_; + DataType data_type_; + std::vector data_; + }; + + virtual void OnMessage(const Message& message) = 0; +}; + +} // namespace webrtc + +#endif // API_DATA_CHANNEL_EVENT_OBSERVER_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/data_channel_interface.h b/pkg/apm/webrtc/api/data_channel_interface.h new file mode 100644 index 00000000..4a8299a0 --- /dev/null +++ b/pkg/apm/webrtc/api/data_channel_interface.h @@ -0,0 +1,224 @@ +/* + * Copyright 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file contains interfaces for DataChannels +// http://dev.w3.org/2011/webrtc/editor/webrtc.html#rtcdatachannel + +#ifndef API_DATA_CHANNEL_INTERFACE_H_ +#define API_DATA_CHANNEL_INTERFACE_H_ + +#include +#include + +#include +#include + +#include "absl/functional/any_invocable.h" +#include "api/priority.h" +#include "api/ref_count.h" +#include "api/rtc_error.h" +#include "rtc_base/checks.h" +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// C++ version of: https://www.w3.org/TR/webrtc/#idl-def-rtcdatachannelinit +// TODO(deadbeef): Use std::optional for the "-1 if unset" things. +struct DataChannelInit { + // Deprecated. Reliability is assumed, and channel will be unreliable if + // maxRetransmitTime or MaxRetransmits is set. + bool reliable = false; + + // True if ordered delivery is required. + bool ordered = true; + + // The max period of time in milliseconds in which retransmissions will be + // sent. After this time, no more retransmissions will be sent. + // + // Cannot be set along with `maxRetransmits`. + // This is called `maxPacketLifeTime` in the WebRTC JS API. + // Negative values are ignored, and positive values are clamped to [0-65535] + std::optional maxRetransmitTime; + + // The max number of retransmissions. + // + // Cannot be set along with `maxRetransmitTime`. + // Negative values are ignored, and positive values are clamped to [0-65535] + std::optional maxRetransmits; + + // This is set by the application and opaque to the WebRTC implementation. + std::string protocol; + + // True if the channel has been externally negotiated and we do not send an + // in-band signalling in the form of an "open" message. If this is true, `id` + // below must be set; otherwise it should be unset and will be negotiated + // in-band. + bool negotiated = false; + + // The stream id, or SID, for SCTP data channels. -1 if unset (see above). + int id = -1; + + // https://w3c.github.io/webrtc-priority/#new-rtcdatachannelinit-member + std::optional priority; +}; + +// At the JavaScript level, data can be passed in as a string or a blob, so +// this structure's `binary` flag tells whether the data should be interpreted +// as binary or text. +struct DataBuffer { + DataBuffer(const CopyOnWriteBuffer& data, bool binary) + : data(data), binary(binary) {} + // For convenience for unit tests. + explicit DataBuffer(const std::string& text) + : data(text.data(), text.length()), binary(false) {} + size_t size() const { return data.size(); } + + CopyOnWriteBuffer data; + // Indicates if the received data contains UTF-8 or binary data. + // Note that the upper layers are left to verify the UTF-8 encoding. + // TODO(jiayl): prefer to use an enum instead of a bool. + bool binary; +}; + +// Used to implement RTCDataChannel events. +// +// The code responding to these callbacks should unwind the stack before +// using any other webrtc APIs; re-entrancy is not supported. +class DataChannelObserver { + public: + // The data channel state have changed. + virtual void OnStateChange() = 0; + // A data buffer was successfully received. + virtual void OnMessage(const DataBuffer& buffer) = 0; + // The data channel's buffered_amount has changed. + virtual void OnBufferedAmountChange(uint64_t /* sent_data_size */) {} + + // Override this to get callbacks directly on the network thread. + // An implementation that does that must not block the network thread + // but rather only use the callback to trigger asynchronous processing + // elsewhere as a result of the notification. + // The default return value, `false`, means that notifications will be + // delivered on the signaling thread associated with the peerconnection + // instance. + // TODO(webrtc:11547): Eventually all DataChannelObserver implementations + // should be called on the network thread and this method removed. + virtual bool IsOkToCallOnTheNetworkThread() { return false; } + + protected: + virtual ~DataChannelObserver() = default; +}; + +class RTC_EXPORT DataChannelInterface : public RefCountInterface { + public: + // C++ version of: https://www.w3.org/TR/webrtc/#idl-def-rtcdatachannelstate + // Unlikely to change, but keep in sync with DataChannel.java:State and + // RTCDataChannel.h:RTCDataChannelState. + enum DataState { + kConnecting, + kOpen, // The DataChannel is ready to send data. + kClosing, + kClosed + }; + + static const char* DataStateString(DataState state) { + switch (state) { + case kConnecting: + return "connecting"; + case kOpen: + return "open"; + case kClosing: + return "closing"; + case kClosed: + return "closed"; + } + RTC_CHECK(false) << "Unknown DataChannel state: " << state; + return ""; + } + + // Used to receive events from the data channel. Only one observer can be + // registered at a time. UnregisterObserver should be called before the + // observer object is destroyed. + virtual void RegisterObserver(DataChannelObserver* observer) = 0; + virtual void UnregisterObserver() = 0; + + // The label attribute represents a label that can be used to distinguish this + // DataChannel object from other DataChannel objects. + virtual std::string label() const = 0; + + // The accessors below simply return the properties from the DataChannelInit + // the data channel was constructed with. + virtual bool reliable() const = 0; + // TODO(deadbeef): Remove these dummy implementations when all classes have + // implemented these APIs. They should all just return the values the + // DataChannel was created with. + virtual bool ordered() const; + virtual std::optional maxRetransmitsOpt() const; + virtual std::optional maxPacketLifeTime() const; + virtual std::string protocol() const; + virtual bool negotiated() const; + + // Returns the ID from the DataChannelInit, if it was negotiated out-of-band. + // If negotiated in-band, this ID will be populated once the DTLS role is + // determined, and until then this will return -1. + virtual int id() const = 0; + virtual PriorityValue priority() const; + virtual DataState state() const = 0; + // When state is kClosed, and the DataChannel was not closed using + // the closing procedure, returns the error information about the closing. + // The default implementation returns "no error". + virtual RTCError error() const { return RTCError(); } + virtual uint32_t messages_sent() const = 0; + virtual uint64_t bytes_sent() const = 0; + virtual uint32_t messages_received() const = 0; + virtual uint64_t bytes_received() const = 0; + + // Returns the number of bytes of application data (UTF-8 text and binary + // data) that have been queued using Send but have not yet been processed at + // the SCTP level. See comment above Send below. + // Values are less or equal to MaxSendQueueSize(). + virtual uint64_t buffered_amount() const = 0; + + // Begins the graceful data channel closing procedure. See: + // https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.7 + virtual void Close() = 0; + + // Sends `data` to the remote peer. If the data can't be sent at the SCTP + // level (due to congestion control), it's buffered at the data channel level, + // up to a maximum of MaxSendQueueSize(). + // Returns false if the data channel is not in open state or if the send + // buffer is full. + // TODO(webrtc:13289): Return an RTCError with information about the failure. + // TODO(tommi): Remove this method once downstream implementations don't refer + // to it. + virtual bool Send(const DataBuffer& buffer); + + // Queues up an asynchronus send operation to run on a network thread. + // Once the operation has completed the `on_complete` callback is invoked, + // on the thread the send operation was done on. It's important that + // `on_complete` implementations do not block the current thread but rather + // post any expensive operations to other worker threads. + // TODO(tommi): Make pure virtual after updating mock class in Chromium. + // Deprecate `Send` in favor of this variant since the return value of `Send` + // is limiting for a fully async implementation (yet in practice is ignored). + virtual void SendAsync(DataBuffer buffer, + absl::AnyInvocable on_complete); + + // Amount of bytes that can be queued for sending on the data channel. + // Those are bytes that have not yet been processed at the SCTP level. + static uint64_t MaxSendQueueSize(); + + protected: + ~DataChannelInterface() override = default; +}; + +} // namespace webrtc + +#endif // API_DATA_CHANNEL_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/dtls_transport_interface.h b/pkg/apm/webrtc/api/dtls_transport_interface.h new file mode 100644 index 00000000..10fc1746 --- /dev/null +++ b/pkg/apm/webrtc/api/dtls_transport_interface.h @@ -0,0 +1,124 @@ +/* + * 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_DTLS_TRANSPORT_INTERFACE_H_ +#define API_DTLS_TRANSPORT_INTERFACE_H_ + +#include +#include + +#include "absl/base/attributes.h" +#include "api/ice_transport_interface.h" +#include "api/ref_count.h" +#include "api/rtc_error.h" +#include "api/scoped_refptr.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// States of a DTLS transport, corresponding to the JS API specification. +// http://w3c.github.io/webrtc-pc/#dom-rtcdtlstransportstate +enum class DtlsTransportState { + kNew, // Has not started negotiating yet. + kConnecting, // In the process of negotiating a secure connection. + kConnected, // Completed negotiation and verified fingerprints. + kClosed, // Intentionally closed. + kFailed, // Failure due to an error or failing to verify a remote + // fingerprint. + kNumValues +}; + +enum class DtlsTransportTlsRole { + kServer, // Other end sends CLIENT_HELLO + kClient // This end sends CLIENT_HELLO +}; + +// This object gives snapshot information about the changeable state of a +// DTLSTransport. +class RTC_EXPORT DtlsTransportInformation { + public: + DtlsTransportInformation(); + explicit DtlsTransportInformation(DtlsTransportState state); + DtlsTransportInformation( + DtlsTransportState state, + std::optional role, + std::optional tls_version, + std::optional ssl_cipher_suite, + std::optional srtp_cipher_suite, + std::unique_ptr remote_ssl_certificates); + ABSL_DEPRECATED("Use version with role parameter") + DtlsTransportInformation( + DtlsTransportState state, + std::optional tls_version, + std::optional ssl_cipher_suite, + std::optional srtp_cipher_suite, + std::unique_ptr remote_ssl_certificates); + + // Copy and assign + DtlsTransportInformation(const DtlsTransportInformation& c); + DtlsTransportInformation& operator=(const DtlsTransportInformation& c); + // Move + DtlsTransportInformation(DtlsTransportInformation&& other) = default; + DtlsTransportInformation& operator=(DtlsTransportInformation&& other) = + default; + + DtlsTransportState state() const { return state_; } + std::optional role() const { return role_; } + std::optional tls_version() const { return tls_version_; } + std::optional ssl_cipher_suite() const { return ssl_cipher_suite_; } + std::optional srtp_cipher_suite() const { return srtp_cipher_suite_; } + // The accessor returns a temporary pointer, it does not release ownership. + const SSLCertChain* remote_ssl_certificates() const { + return remote_ssl_certificates_.get(); + } + + private: + DtlsTransportState state_; + std::optional role_; + std::optional tls_version_; + std::optional ssl_cipher_suite_; + std::optional srtp_cipher_suite_; + std::unique_ptr remote_ssl_certificates_; +}; + +class DtlsTransportObserverInterface { + public: + // This callback carries information about the state of the transport. + // The argument is a pass-by-value snapshot of the state. + virtual void OnStateChange(DtlsTransportInformation info) = 0; + // This callback is called when an error occurs, causing the transport + // to go to the kFailed state. + virtual void OnError(RTCError error) = 0; + + protected: + virtual ~DtlsTransportObserverInterface() = default; +}; + +// A DTLS transport, as represented to the outside world. +// This object is created on the network thread, and can only be +// accessed on that thread, except for functions explicitly marked otherwise. +// References can be held by other threads, and destruction can therefore +// be initiated by other threads. +class DtlsTransportInterface : public webrtc::RefCountInterface { + public: + // Returns a pointer to the ICE transport that is owned by the DTLS transport. + virtual scoped_refptr ice_transport() = 0; + // Returns information on the state of the DtlsTransport. + // This function can be called from other threads. + virtual DtlsTransportInformation Information() = 0; + // Observer management. + virtual void RegisterObserver(DtlsTransportObserverInterface* observer) = 0; + virtual void UnregisterObserver() = 0; +}; + +} // namespace webrtc + +#endif // API_DTLS_TRANSPORT_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/dtmf_sender_interface.h b/pkg/apm/webrtc/api/dtmf_sender_interface.h new file mode 100644 index 00000000..16ce665e --- /dev/null +++ b/pkg/apm/webrtc/api/dtmf_sender_interface.h @@ -0,0 +1,124 @@ +/* + * Copyright 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_DTMF_SENDER_INTERFACE_H_ +#define API_DTMF_SENDER_INTERFACE_H_ + +#include + +#include "api/ref_count.h" + +namespace webrtc { + +// DtmfSender callback interface, used to implement RTCDtmfSender events. +// Applications should implement this interface to get notifications from the +// DtmfSender. +class DtmfSenderObserverInterface { + public: + // Triggered when DTMF `tone` is sent. + // If `tone` is empty that means the DtmfSender has sent out all the given + // tones. + // The callback includes the state of the tone buffer at the time when + // the tone finished playing. + virtual void OnToneChange(const std::string& /* tone */, + const std::string& /* tone_buffer */) {} + // DEPRECATED: Older API without tone buffer. + // TODO(bugs.webrtc.org/9725): Remove old API and default implementation + // when old callers are gone. + virtual void OnToneChange(const std::string& /* tone */) {} + + protected: + virtual ~DtmfSenderObserverInterface() = default; +}; + +// The interface of native implementation of the RTCDTMFSender defined by the +// WebRTC W3C Editor's Draft. +// See: https://www.w3.org/TR/webrtc/#peer-to-peer-dtmf +class DtmfSenderInterface : public webrtc::RefCountInterface { + public: + // Provides the spec compliant default 2 second delay for the ',' character. + static const int kDtmfDefaultCommaDelayMs = 2000; + + // Used to receive events from the DTMF sender. Only one observer can be + // registered at a time. UnregisterObserver should be called before the + // observer object is destroyed. + virtual void RegisterObserver(DtmfSenderObserverInterface* observer) = 0; + virtual void UnregisterObserver() = 0; + + // Returns true if this DtmfSender is capable of sending DTMF. Otherwise + // returns false. To be able to send DTMF, the associated RtpSender must be + // able to send packets, and a "telephone-event" codec must be negotiated. + virtual bool CanInsertDtmf() = 0; + + // Queues a task that sends the DTMF `tones`. The `tones` parameter is treated + // as a series of characters. The characters 0 through 9, A through D, #, and + // * generate the associated DTMF tones. The characters a to d are equivalent + // to A to D. The character ',' indicates a delay of 2 seconds before + // processing the next character in the tones parameter. + // + // Unrecognized characters are ignored. + // + // The `duration` parameter indicates the duration in ms to use for each + // character passed in the `tones` parameter. The duration cannot be more + // than 6000 or less than 70. + // + // The `inter_tone_gap` parameter indicates the gap between tones in ms. The + // `inter_tone_gap` must be at least 50 ms but should be as short as + // possible. + // + // The `comma_delay` parameter indicates the delay after the ',' + // character. InsertDtmf specifies `comma_delay` as an argument + // with a default value of 2 seconds as per the WebRTC spec. This parameter + // allows users to comply with legacy WebRTC clients. The `comma_delay` + // must be at least 50 ms. + // + // If InsertDtmf is called on the same object while an existing task for this + // object to generate DTMF is still running, the previous task is canceled. + // Returns true on success and false on failure. + virtual bool InsertDtmf(const std::string& tones, + int duration, + int inter_tone_gap) { + return InsertDtmf(tones, duration, inter_tone_gap, + kDtmfDefaultCommaDelayMs); + } + virtual bool InsertDtmf(const std::string& tones, + int duration, + int inter_tone_gap, + int /* comma_delay */) { + // TODO(bugs.webrtc.org/165700): Remove once downstream implementations + // override this signature rather than the 3-parameter one. + return InsertDtmf(tones, duration, inter_tone_gap); + } + + // Returns the tones remaining to be played out. + virtual std::string tones() const = 0; + + // Returns the current tone duration value in ms. + // This value will be the value last set via the InsertDtmf() method, or the + // default value of 100 ms if InsertDtmf() was never called. + virtual int duration() const = 0; + + // Returns the current value of the between-tone gap in ms. + // This value will be the value last set via the InsertDtmf() method, or the + // default value of 50 ms if InsertDtmf() was never called. + virtual int inter_tone_gap() const = 0; + + // Returns the current value of the "," character delay in ms. + // This value will be the value last set via the InsertDtmf() method, or the + // default value of 2000 ms if InsertDtmf() was never called. + virtual int comma_delay() const { return kDtmfDefaultCommaDelayMs; } + + protected: + ~DtmfSenderInterface() override = default; +}; + +} // namespace webrtc + +#endif // API_DTMF_SENDER_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/environment/environment.go b/pkg/apm/webrtc/api/environment/environment.go new file mode 100644 index 00000000..bd5c25aa --- /dev/null +++ b/pkg/apm/webrtc/api/environment/environment.go @@ -0,0 +1,10 @@ +//go:build console + +package environment + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/api/environment/environment.h b/pkg/apm/webrtc/api/environment/environment.h new file mode 100644 index 00000000..60f064c6 --- /dev/null +++ b/pkg/apm/webrtc/api/environment/environment.h @@ -0,0 +1,143 @@ +/* + * 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. + */ + +// This header file provides wrapper for common WebRTC utilities. +// Different application may need different implementations of these utilities, +// Moreover, single application may need to use WebRTC for multiple purposes, +// and thus would need to provide different utilities implementations for +// different peer connections. +// The main purpose of the `Environment` class below is to propagate references +// to those utilities to all WebRTC classes that need them. + +#ifndef API_ENVIRONMENT_ENVIRONMENT_H_ +#define API_ENVIRONMENT_ENVIRONMENT_H_ + +#include + +#include "absl/base/nullability.h" +#include "api/field_trials_view.h" +#include "api/ref_counted_base.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_factory.h" +#include "rtc_base/system/rtc_export.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { +// Contains references to WebRTC utilities. Object of this class should be +// passed as a construction parameter and saved by value in each class that +// needs it. Most classes shouldn't create a new instance of the `Environment`, +// but instead should use a propagated copy. +// Usually Environment should be the first parameter in a constructor or a +// factory, and the first member in the class. Keeping Environment as the first +// member in the class ensures utilities (e.g. clock) are still valid during +// destruction of other members. +// +// Example: +// class PeerConnection { +// public: +// PeerConnection(const Environment& env, ...) +// : env_(env), +// log_duration_on_destruction_(&env_.clock()), +// rtp_manager_(env_, ...), +// ... +// +// const FieldTrialsView& trials() const { return env_.field_trials(); } +// +// scoped_refptr AddTransceiver(...) { +// return make_ref_counted(env_, ...); +// } +// +// private: +// const Environment env_; +// Stats log_duration_on_destruction_; +// RtpTransmissionManager rtp_manager_; +// }; +// This class is thread safe. +class RTC_EXPORT Environment final { + public: + // Default constructor is deleted in favor of creating this object using + // `EnvironmentFactory`. To create the default environment use + // `EnvironmentFactory().Create()` or `CreateEnvironment()`. + Environment() = delete; + + Environment(const Environment&) = default; + Environment(Environment&&) = default; + Environment& operator=(const Environment&) = default; + Environment& operator=(Environment&&) = default; + + ~Environment() = default; + + // Provides means to alter behavior, mostly for A/B testing new features. + // See ../../g3doc/field-trials.md + const FieldTrialsView& field_trials() const; + + // Provides an interface to query current time. + // See ../../g3doc/implementation_basics.md#time + Clock& clock() const; + + // Provides a factory for task queues, WebRTC threading primitives. + // See ../../g3doc/implementation_basics.md#threads + TaskQueueFactory& task_queue_factory() const; + + // Provides an interface for collecting structured logs. + // See ../../logging/g3doc/rtc_event_log.md + RtcEventLog& event_log() const; + + private: + friend class EnvironmentFactory; + Environment(scoped_refptr storage, + const FieldTrialsView* absl_nonnull field_trials, + Clock* absl_nonnull clock, + TaskQueueFactory* absl_nonnull task_queue_factory, + RtcEventLog* absl_nonnull event_log) + : storage_(std::move(storage)), + field_trials_(field_trials), + clock_(clock), + task_queue_factory_(task_queue_factory), + event_log_(event_log) {} + + // Container that keeps ownership of the utilities below. + // Defining this as a RefCountedBase allows `Environment` to share this + // storage with another `Environment`, in particular allows `Environment` to + // be copyable. It is up to the `EnvironmentFactory` to provide an object that + // ensures references to utilties below are valid while object in the + // `storage_` is alive. + scoped_refptr storage_; + + const FieldTrialsView* absl_nonnull field_trials_; + Clock* absl_nonnull clock_; + TaskQueueFactory* absl_nonnull task_queue_factory_; + RtcEventLog* absl_nonnull event_log_; +}; + +//------------------------------------------------------------------------------ +// Implementation details follow +//------------------------------------------------------------------------------ + +inline const FieldTrialsView& Environment::field_trials() const { + return *field_trials_; +} + +inline Clock& Environment::clock() const { + return *clock_; +} + +inline TaskQueueFactory& Environment::task_queue_factory() const { + return *task_queue_factory_; +} + +inline RtcEventLog& Environment::event_log() const { + return *event_log_; +} + +} // namespace webrtc + +#endif // API_ENVIRONMENT_ENVIRONMENT_H_ diff --git a/pkg/apm/webrtc/api/environment/environment_factory.cc b/pkg/apm/webrtc/api/environment/environment_factory.cc new file mode 100644 index 00000000..692ecc09 --- /dev/null +++ b/pkg/apm/webrtc/api/environment/environment_factory.cc @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#include "api/environment/environment_factory.h" + +#include +#include + +#include "absl/base/nullability.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "api/make_ref_counted.h" +#include "api/ref_counted_base.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/default_task_queue_factory.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/transport/field_trial_based_config.h" +#include "rtc_base/checks.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { +namespace { + +template +void Store(absl_nonnull std::unique_ptr value, + scoped_refptr& leaf) { + class StorageNode : public RefCountedBase { + public: + StorageNode(scoped_refptr parent, + absl_nonnull std::unique_ptr value) + : parent_(std::move(parent)), value_(std::move(value)) {} + + StorageNode(const StorageNode&) = delete; + StorageNode& operator=(const StorageNode&) = delete; + + ~StorageNode() override = default; + + private: + scoped_refptr parent_; + absl_nonnull std::unique_ptr value_; + }; + + // Utilities provided with ownership form a tree: + // Root is nullptr, each node keeps an ownership of one utility. + // Each child node has a link to the parent, but parent is unaware of its + // children. Each `EnvironmentFactory` and `Environment` keep a reference to a + // 'leaf_' - node with the last provided utility. This way `Environment` keeps + // ownership of a single branch of the storage tree with each used utiltity + // owned by one of the nodes on that branch. + leaf = make_ref_counted(std::move(leaf), std::move(value)); +} + +} // namespace + +EnvironmentFactory::EnvironmentFactory(const Environment& env) + : leaf_(env.storage_), + field_trials_(env.field_trials_), + clock_(env.clock_), + task_queue_factory_(env.task_queue_factory_), + event_log_(env.event_log_) {} + +void EnvironmentFactory::Set( + absl_nullable std::unique_ptr utility) { + if (utility != nullptr) { + field_trials_ = utility.get(); + Store(std::move(utility), leaf_); + } +} + +void EnvironmentFactory::Set(absl_nullable std::unique_ptr utility) { + if (utility != nullptr) { + clock_ = utility.get(); + Store(std::move(utility), leaf_); + } +} + +void EnvironmentFactory::Set( + absl_nullable std::unique_ptr utility) { + if (utility != nullptr) { + task_queue_factory_ = utility.get(); + Store(std::move(utility), leaf_); + } +} + +void EnvironmentFactory::Set( + absl_nullable std::unique_ptr utility) { + if (utility != nullptr) { + event_log_ = utility.get(); + Store(std::move(utility), leaf_); + } +} + +Environment EnvironmentFactory::CreateWithDefaults() && { + if (field_trials_ == nullptr) { + Set(std::make_unique()); + } + if (clock_ == nullptr) { + Set(Clock::GetRealTimeClock()); + } + if (task_queue_factory_ == nullptr) { + Set(CreateDefaultTaskQueueFactory(field_trials_)); + } + if (event_log_ == nullptr) { + Set(std::make_unique()); + } + + RTC_DCHECK(field_trials_ != nullptr); + RTC_DCHECK(clock_ != nullptr); + RTC_DCHECK(task_queue_factory_ != nullptr); + RTC_DCHECK(event_log_ != nullptr); + return Environment(std::move(leaf_), // + field_trials_, clock_, task_queue_factory_, event_log_); +} + +Environment EnvironmentFactory::Create() const { + // Create a temporary copy to avoid mutating `this` with default utilities. + return EnvironmentFactory(*this).CreateWithDefaults(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/environment/environment_factory.h b/pkg/apm/webrtc/api/environment/environment_factory.h new file mode 100644 index 00000000..3023ea42 --- /dev/null +++ b/pkg/apm/webrtc/api/environment/environment_factory.h @@ -0,0 +1,144 @@ +/* + * 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_ENVIRONMENT_ENVIRONMENT_FACTORY_H_ +#define API_ENVIRONMENT_ENVIRONMENT_FACTORY_H_ + +#include +#include + +#include "absl/base/nullability.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "api/ref_counted_base.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_factory.h" +#include "rtc_base/system/rtc_export.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { +// Constructs `Environment`. +// Individual utilities are provided using one of the `Set` functions. +// `Set` functions do nothing when nullptr value is passed. +// Creates default implementations for utilities that are not provided. +// +// Examples: +// Environment default_env = EnvironmentFactory().Create(); +// +// EnvironmentFactory factory; +// factory.Set(std::make_unique()); +// factory.Set(std::make_unique()); +// Environment custom_env = factory.Create(); +// +class RTC_EXPORT EnvironmentFactory final { + public: + EnvironmentFactory() = default; + explicit EnvironmentFactory(const Environment& env); + + EnvironmentFactory(const EnvironmentFactory&) = default; + EnvironmentFactory(EnvironmentFactory&&) = default; + EnvironmentFactory& operator=(const EnvironmentFactory&) = default; + EnvironmentFactory& operator=(EnvironmentFactory&&) = default; + + ~EnvironmentFactory() = default; + + void Set(absl_nullable std::unique_ptr utility); + void Set(absl_nullable std::unique_ptr utility); + void Set(absl_nullable std::unique_ptr utility); + void Set(absl_nullable std::unique_ptr utility); + + void Set(const FieldTrialsView* absl_nullable utility); + void Set(Clock* absl_nullable utility); + void Set(TaskQueueFactory* absl_nullable utility); + void Set(RtcEventLog* absl_nullable utility); + + Environment Create() const; + + private: + Environment CreateWithDefaults() &&; + + scoped_refptr leaf_; + + const FieldTrialsView* absl_nullable field_trials_ = nullptr; + Clock* absl_nullable clock_ = nullptr; + TaskQueueFactory* absl_nullable task_queue_factory_ = nullptr; + RtcEventLog* absl_nullable event_log_ = nullptr; +}; + +// Helper for concise way to create an environment. +// `Environment env = CreateEnvironment(utility1, utility2)` is a shortcut to +// `EnvironmentFactory factory; +// factory.Set(utility1); +// factory.Set(utility2); +// Environment env = factory.Create();` +// +// Examples: +// Environment default_env = CreateEnvironment(); +// Environment custom_env = +// CreateEnvironment(std::make_unique(), +// std::make_unique()); +template +Environment CreateEnvironment(Utilities&&... utilities); + +//------------------------------------------------------------------------------ +// Implementation details follow +//------------------------------------------------------------------------------ + +inline void EnvironmentFactory::Set( + const FieldTrialsView* absl_nullable utility) { + if (utility != nullptr) { + field_trials_ = utility; + } +} + +inline void EnvironmentFactory::Set(Clock* absl_nullable utility) { + if (utility != nullptr) { + clock_ = utility; + } +} + +inline void EnvironmentFactory::Set(TaskQueueFactory* absl_nullable utility) { + if (utility != nullptr) { + task_queue_factory_ = utility; + } +} + +inline void EnvironmentFactory::Set(RtcEventLog* absl_nullable utility) { + if (utility != nullptr) { + event_log_ = utility; + } +} + +namespace webrtc_create_environment_internal { + +inline void Set(EnvironmentFactory& /* factory */) {} + +template +void Set(EnvironmentFactory& factory, + FirstUtility&& first, + Utilities&&... utilities) { + factory.Set(std::forward(first)); + Set(factory, std::forward(utilities)...); +} + +} // namespace webrtc_create_environment_internal + +template +Environment CreateEnvironment(Utilities&&... utilities) { + EnvironmentFactory factory; + webrtc_create_environment_internal::Set( + factory, std::forward(utilities)...); + return factory.Create(); +} + +} // namespace webrtc + +#endif // API_ENVIRONMENT_ENVIRONMENT_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/fec_controller.h b/pkg/apm/webrtc/api/fec_controller.h new file mode 100644 index 00000000..a6cf7695 --- /dev/null +++ b/pkg/apm/webrtc/api/fec_controller.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef API_FEC_CONTROLLER_H_ +#define API_FEC_CONTROLLER_H_ + +#include +#include +#include +#include + +#include "api/environment/environment.h" +#include "api/video/video_frame_type.h" +#include "modules/include/module_fec_types.h" + +namespace webrtc { +// TODO(yinwa): work in progress. API in class FecController should not be +// used by other users until this comment is removed. + +// Callback class used for telling the user about how to configure the FEC, +// and the rates sent the last second is returned to the VCM. +class VCMProtectionCallback { + public: + virtual int ProtectionRequest(const FecProtectionParams* delta_params, + const FecProtectionParams* key_params, + uint32_t* sent_video_rate_bps, + uint32_t* sent_nack_rate_bps, + uint32_t* sent_fec_rate_bps) = 0; + + // 'retransmission_mode' is either a value of enum RetransmissionMode, or + // computed with bitwise operators on values of enum RetransmissionMode. + virtual void SetRetransmissionMode(int retransmission_mode) = 0; + + protected: + virtual ~VCMProtectionCallback() {} +}; + +// FecController calculates how much of the allocated network +// capacity that can be used by an encoder and how much that +// is needed for redundant packets such as FEC and NACK. It uses an +// implementation of `VCMProtectionCallback` to set new FEC parameters and get +// the bitrate currently used for FEC and NACK. +// Usage: +// Setup by calling SetProtectionMethod and SetEncodingData. +// For each encoded image, call UpdateWithEncodedData. +// Each time the bandwidth estimate change, call UpdateFecRates. UpdateFecRates +// will return the bitrate that can be used by an encoder. +// A lock is used to protect internal states, so methods can be called on an +// arbitrary thread. +class FecController { + public: + virtual ~FecController() {} + + virtual void SetProtectionCallback( + VCMProtectionCallback* protection_callback) = 0; + virtual void SetProtectionMethod(bool enable_fec, bool enable_nack) = 0; + + // Informs loss protectoin logic of initial encoding state. + virtual void SetEncodingData(size_t width, + size_t height, + size_t num_temporal_layers, + size_t max_payload_size) = 0; + + // Returns target rate for the encoder given the channel parameters. + // Inputs: estimated_bitrate_bps - the estimated network bitrate in bits/s. + // actual_framerate - encoder frame rate. + // fraction_lost - packet loss rate in % in the network. + // loss_mask_vector - packet loss mask since last time this method + // was called. round_trip_time_ms - round trip time in milliseconds. + virtual uint32_t UpdateFecRates(uint32_t estimated_bitrate_bps, + int actual_framerate, + uint8_t fraction_lost, + std::vector loss_mask_vector, + int64_t round_trip_time_ms) = 0; + + // Informs of encoded output. + virtual void UpdateWithEncodedData( + size_t encoded_image_length, + VideoFrameType encoded_image_frametype) = 0; + + // Returns whether this FEC Controller needs Loss Vector Mask as input. + virtual bool UseLossVectorMask() = 0; +}; + +class FecControllerFactoryInterface { + public: + virtual ~FecControllerFactoryInterface() = default; + + virtual std::unique_ptr CreateFecController( + const Environment& env) = 0; +}; + +} // namespace webrtc +#endif // API_FEC_CONTROLLER_H_ diff --git a/pkg/apm/webrtc/api/fec_controller_override.h b/pkg/apm/webrtc/api/fec_controller_override.h new file mode 100644 index 00000000..233812fb --- /dev/null +++ b/pkg/apm/webrtc/api/fec_controller_override.h @@ -0,0 +1,28 @@ +/* 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. + */ + +// This is an EXPERIMENTAL interface. + +#ifndef API_FEC_CONTROLLER_OVERRIDE_H_ +#define API_FEC_CONTROLLER_OVERRIDE_H_ + +namespace webrtc { + +// Interface for temporarily overriding FecController's bitrate allocation. +class FecControllerOverride { + public: + virtual void SetFecAllowed(bool fec_allowed) = 0; + + protected: + virtual ~FecControllerOverride() = default; +}; + +} // namespace webrtc + +#endif // API_FEC_CONTROLLER_OVERRIDE_H_ diff --git a/pkg/apm/webrtc/api/field_trials.h b/pkg/apm/webrtc/api/field_trials.h new file mode 100644 index 00000000..693c1afe --- /dev/null +++ b/pkg/apm/webrtc/api/field_trials.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_FIELD_TRIALS_H_ +#define API_FIELD_TRIALS_H_ + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/field_trials_registry.h" +#include "rtc_base/containers/flat_map.h" + +namespace webrtc { + +// The FieldTrials class is used to inject field trials into webrtc. +// +// Field trials allow webrtc clients (such as Chromium) to turn on feature code +// in binaries out in the field and gather information with that. +// +// They are designed to be easy to use with Chromium field trials and to speed +// up developers by reducing the need to wire up APIs to control whether a +// feature is on/off. +// +// The field trials are injected into objects that use them at creation time. +// +// NOTE: Creating multiple FieldTrials-object is currently prohibited +// until we remove the global string (TODO(bugs.webrtc.org/10335)) +// (unless using CreateNoGlobal): +class FieldTrials : public FieldTrialsRegistry { + public: + explicit FieldTrials(absl::string_view s); + ~FieldTrials(); + + // Create a FieldTrials object that is not reading/writing from + // global variable (i.e can not be used for all parts of webrtc). + static std::unique_ptr CreateNoGlobal(absl::string_view s); + + private: + explicit FieldTrials(absl::string_view s, bool); + + std::string GetValue(absl::string_view key) const override; + + const bool uses_global_; + const std::string field_trial_string_; + const char* const previous_field_trial_string_; + const flat_map key_value_map_; +}; + +} // namespace webrtc + +#endif // API_FIELD_TRIALS_H_ diff --git a/pkg/apm/webrtc/api/field_trials_registry.cc b/pkg/apm/webrtc/api/field_trials_registry.cc new file mode 100644 index 00000000..11259cc6 --- /dev/null +++ b/pkg/apm/webrtc/api/field_trials_registry.cc @@ -0,0 +1,37 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "api/field_trials_registry.h" + +#include + +#include "absl/strings/string_view.h" +// IWYU pragma: begin_keep +#include "absl/algorithm/container.h" +#include "experiments/registered_field_trials.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +// IWYU pragma: end_keep + +namespace webrtc { + +std::string FieldTrialsRegistry::Lookup(absl::string_view key) const { +#if WEBRTC_STRICT_FIELD_TRIALS == 1 + RTC_DCHECK(absl::c_linear_search(kRegisteredFieldTrials, key) || + test_keys_.contains(key)) + << key << " is not registered, see g3doc/field-trials.md."; +#elif WEBRTC_STRICT_FIELD_TRIALS == 2 + RTC_LOG_IF(LS_WARNING, !(absl::c_linear_search(kRegisteredFieldTrials, key) || + test_keys_.contains(key))) + << key << " is not registered, see g3doc/field-trials.md."; +#endif + return GetValue(key); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/field_trials_registry.h b/pkg/apm/webrtc/api/field_trials_registry.h new file mode 100644 index 00000000..dc7e8445 --- /dev/null +++ b/pkg/apm/webrtc/api/field_trials_registry.h @@ -0,0 +1,54 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef API_FIELD_TRIALS_REGISTRY_H_ +#define API_FIELD_TRIALS_REGISTRY_H_ + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/field_trials_view.h" +#include "rtc_base/containers/flat_set.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Abstract base class for a field trial registry that verifies that any looked +// up key has been pre-registered in accordance with `g3doc/field-trials.md`. +class RTC_EXPORT FieldTrialsRegistry : public FieldTrialsView { + public: + FieldTrialsRegistry() = default; + + FieldTrialsRegistry(const FieldTrialsRegistry&) = default; + FieldTrialsRegistry& operator=(const FieldTrialsRegistry&) = default; + + ~FieldTrialsRegistry() override = default; + + // Verifies that `key` is a registered field trial and then returns the + // configured value for `key` or an empty string if the field trial isn't + // configured. + std::string Lookup(absl::string_view key) const override; + + // Register additional `keys` for testing. This should only be used for + // imaginary keys that are never used outside test code. + void RegisterKeysForTesting(flat_set keys) { + test_keys_ = std::move(keys); + } + + private: + virtual std::string GetValue(absl::string_view key) const = 0; + + // Imaginary keys only used for testing. + flat_set test_keys_; +}; + +} // namespace webrtc + +#endif // API_FIELD_TRIALS_REGISTRY_H_ diff --git a/pkg/apm/webrtc/api/field_trials_view.h b/pkg/apm/webrtc/api/field_trials_view.h new file mode 100644 index 00000000..d8df8457 --- /dev/null +++ b/pkg/apm/webrtc/api/field_trials_view.h @@ -0,0 +1,46 @@ +/* + * 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_FIELD_TRIALS_VIEW_H_ +#define API_FIELD_TRIALS_VIEW_H_ + +#include + +#include "absl/strings/match.h" +#include "absl/strings/string_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// An interface that provides the means to access field trials. +// +// Note that there are no guarantess that the meaning of a particular key-value +// mapping will be preserved over time and no announcements will be made if they +// are changed. It's up to the library user to ensure that the behavior does not +// break. +class RTC_EXPORT FieldTrialsView { + public: + virtual ~FieldTrialsView() = default; + + // Returns the configured value for `key` or an empty string if the field + // trial isn't configured. + virtual std::string Lookup(absl::string_view key) const = 0; + + bool IsEnabled(absl::string_view key) const { + return absl::StartsWith(Lookup(key), "Enabled"); + } + + bool IsDisabled(absl::string_view key) const { + return absl::StartsWith(Lookup(key), "Disabled"); + } +}; + +} // namespace webrtc + +#endif // API_FIELD_TRIALS_VIEW_H_ diff --git a/pkg/apm/webrtc/api/frame_transformer_factory.h b/pkg/apm/webrtc/api/frame_transformer_factory.h new file mode 100644 index 00000000..fda61173 --- /dev/null +++ b/pkg/apm/webrtc/api/frame_transformer_factory.h @@ -0,0 +1,38 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_FRAME_TRANSFORMER_FACTORY_H_ +#define API_FRAME_TRANSFORMER_FACTORY_H_ + +#include + +#include "api/frame_transformer_interface.h" +#include "rtc_base/system/rtc_export.h" + +// This file contains EXPERIMENTAL functions to create video frames from +// either an old video frame or directly from parameters. +// These functions will be used in Chrome functionality to manipulate +// encoded frames from Javascript. +namespace webrtc { + +// TODO(bugs.webrtc.org/14708): Add the required parameters to these APIs. +std::unique_ptr CreateVideoSenderFrame(); +// TODO(bugs.webrtc.org/14708): Consider whether Receiver frames ever make sense +// to create. +std::unique_ptr CreateVideoReceiverFrame(); +// Creates a new frame with the same metadata as the original. +// The original can be a sender or receiver frame. +RTC_EXPORT std::unique_ptr CloneAudioFrame( + TransformableAudioFrameInterface* original); +RTC_EXPORT std::unique_ptr CloneVideoFrame( + TransformableVideoFrameInterface* original); +} // namespace webrtc + +#endif // API_FRAME_TRANSFORMER_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/frame_transformer_interface.h b/pkg/apm/webrtc/api/frame_transformer_interface.h new file mode 100644 index 00000000..c7375e38 --- /dev/null +++ b/pkg/apm/webrtc/api/frame_transformer_interface.h @@ -0,0 +1,213 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_FRAME_TRANSFORMER_INTERFACE_H_ +#define API_FRAME_TRANSFORMER_INTERFACE_H_ + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/ref_count.h" +#include "api/scoped_refptr.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "api/video/video_frame_metadata.h" +#include "modules/rtp_rtcp/source/rtp_video_header.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Owns the frame payload data. +class TransformableFrameInterface { + public: + // Only a known list of internal implementations of transformable frames are + // permitted to allow internal downcasting. This is enforced via the + // internally-constructable Passkey. + // TODO: bugs.webrtc.org/339815768 - Remove this passkey once the + // downcasts are removed. + class Passkey; + RTC_EXPORT explicit TransformableFrameInterface(Passkey); + + TransformableFrameInterface(TransformableFrameInterface&&) = default; + TransformableFrameInterface& operator=(TransformableFrameInterface&&) = + default; + + virtual ~TransformableFrameInterface() = default; + + // Returns the frame payload data. The data is valid until the next non-const + // method call. + virtual ArrayView GetData() const = 0; + + // Copies `data` into the owned frame payload data. + virtual void SetData(ArrayView data) = 0; + + virtual uint8_t GetPayloadType() const = 0; + virtual uint32_t GetSsrc() const = 0; + virtual uint32_t GetTimestamp() const = 0; + virtual void SetRTPTimestamp(uint32_t timestamp) = 0; + + // TODO(https://bugs.webrtc.org/373365537): Remove this once its usage is + // removed from blink. + [[deprecated( + "Use GetPresentationTimestamp instead")]] virtual std::optional + GetCaptureTimeIdentifier() const { + return std::nullopt; + } + + // TODO(https://bugs.webrtc.org/14878): Change this to pure virtual after it + // is implemented everywhere. + virtual std::optional GetPresentationTimestamp() const { + return std::nullopt; + } + + enum class Direction { + kUnknown, + kReceiver, + kSender, + }; + // TODO(crbug.com/1250638): Remove this distinction between receiver and + // sender frames to allow received frames to be directly re-transmitted on + // other PeerConnectionss. + virtual Direction GetDirection() const { return Direction::kUnknown; } + virtual std::string GetMimeType() const = 0; + + // Timestamp at which the packet has been first seen on the network interface. + // Only defined for received frames. + virtual std::optional ReceiveTime() const = 0; + + // Timestamp at which the frame was captured in the capturer system. + // The timestamp is expressed in the capturer system's clock relative to the + // NTP epoch (January 1st 1970 00:00 UTC) + // Accessible only if the absolute capture timestamp header extension is + // enabled. + virtual std::optional CaptureTime() const = 0; + + // Offset between the sender system's clock and the capturer system's clock. + // Can be used to express the capture time in the local system's clock as + // long as the local system can determine the offset between its local clock + // and the sender system's clock. + // Accessible only if the absolute capture timestamp header extension is + // enabled. + virtual std::optional SenderCaptureTimeOffset() const = 0; +}; + +class TransformableVideoFrameInterface : public TransformableFrameInterface { + public: + RTC_EXPORT explicit TransformableVideoFrameInterface(Passkey passkey); + virtual ~TransformableVideoFrameInterface() = default; + virtual bool IsKeyFrame() const = 0; + + virtual VideoFrameMetadata Metadata() const = 0; + + virtual void SetMetadata(const VideoFrameMetadata&) = 0; + + virtual const RTPVideoHeader& header () const = 0; +}; + +// Extends the TransformableFrameInterface to expose audio-specific information. +class TransformableAudioFrameInterface : public TransformableFrameInterface { + public: + RTC_EXPORT explicit TransformableAudioFrameInterface(Passkey passkey); + virtual ~TransformableAudioFrameInterface() = default; + + virtual ArrayView GetContributingSources() const = 0; + + virtual const std::optional SequenceNumber() const = 0; + + // TODO(crbug.com/391114797): Delete this function. + virtual std::optional AbsoluteCaptureTimestamp() const = 0; + + enum class FrameType { kEmptyFrame, kAudioFrameSpeech, kAudioFrameCN }; + + // TODO(crbug.com/1456628): Change this to pure virtual after it + // is implemented everywhere. + virtual FrameType Type() const { return FrameType::kEmptyFrame; } + + // Audio level in -dBov. Values range from 0 to 127, representing 0 to -127 + // dBov. 127 represents digital silence. Only present on remote frames if + // the audio level header extension was included. + virtual std::optional AudioLevel() const = 0; +}; + +// Objects implement this interface to be notified with the transformed frame. +class TransformedFrameCallback : public RefCountInterface { + public: + virtual void OnTransformedFrame( + std::unique_ptr frame) = 0; + + // Request to no longer be called on each frame, instead having frames be + // sent directly to OnTransformedFrame without additional work. + // TODO(crbug.com/1502781): Make pure virtual once all mocks have + // implementations. + virtual void StartShortCircuiting() {} + + protected: + ~TransformedFrameCallback() override = default; +}; + +// Transforms encoded frames. The transformed frame is sent in a callback using +// the TransformedFrameCallback interface (see above). +class FrameTransformerInterface : public RefCountInterface { + public: + // Transforms `frame` using the implementing class' processing logic. + virtual void Transform( + std::unique_ptr transformable_frame) = 0; + + virtual void RegisterTransformedFrameCallback( + scoped_refptr) {} + virtual void RegisterTransformedFrameSinkCallback( + scoped_refptr, + uint32_t /* ssrc */) {} + virtual void UnregisterTransformedFrameCallback() {} + virtual void UnregisterTransformedFrameSinkCallback(uint32_t /* ssrc */) {} + + protected: + ~FrameTransformerInterface() override = default; +}; + +// An interface implemented by classes that can host a transform. +// Currently this is implemented by the RTCRtpSender and RTCRtpReceiver. +class FrameTransformerHost { + public: + virtual ~FrameTransformerHost() {} + virtual void SetFrameTransformer( + scoped_refptr frame_transformer) = 0; + // TODO: bugs.webrtc.org/15929 - To be added: + // virtual AddIncomingMediaType(RtpCodec codec) = 0; + // virtual AddOutgoingMediaType(RtpCodec codec) = 0; +}; + +//------------------------------------------------------------------------------ +// Implementation details follow +//------------------------------------------------------------------------------ +class TransformableFrameInterface::Passkey { + public: + ~Passkey() = default; + + private: + // Explicit list of allowed internal implmentations of + // TransformableFrameInterface. + friend class TransformableOutgoingAudioFrame; + friend class TransformableIncomingAudioFrame; + friend class TransformableVideoSenderFrame; + friend class TransformableVideoReceiverFrame; + + friend class MockTransformableFrame; + friend class MockTransformableAudioFrame; + friend class MockTransformableVideoFrame; + Passkey() = default; +}; + +} // namespace webrtc + +#endif // API_FRAME_TRANSFORMER_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/function_view.h b/pkg/apm/webrtc/api/function_view.h new file mode 100644 index 00000000..f19a1d38 --- /dev/null +++ b/pkg/apm/webrtc/api/function_view.h @@ -0,0 +1,139 @@ +/* + * Copyright 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. + */ + +#ifndef API_FUNCTION_VIEW_H_ +#define API_FUNCTION_VIEW_H_ + +#include +#include +#include + +#include "rtc_base/checks.h" + +// Just like std::function, FunctionView will wrap any callable and hide its +// actual type, exposing only its signature. But unlike std::function, +// FunctionView doesn't own its callable---it just points to it. Thus, it's a +// good choice mainly as a function argument when the callable argument will +// not be called again once the function has returned. +// +// Its constructors are implicit, so that callers won't have to convert lambdas +// and other callables to FunctionView explicitly. This is +// safe because FunctionView is only a reference to the real callable. +// +// Example use: +// +// void SomeFunction(webrtc::FunctionView index_transform); +// ... +// SomeFunction([](int i) { return 2 * i + 1; }); +// +// Note: FunctionView is tiny (essentially just two pointers) and trivially +// copyable, so it's probably cheaper to pass it by value than by const +// reference. + +namespace webrtc { + +template +class FunctionView; // Undefined. + +template +class FunctionView final { + public: + // Constructor for lambdas and other callables; it accepts every type of + // argument except those noted in its enable_if call. + template < + typename F, + typename std::enable_if< + // Not for function pointers; we have another constructor for that + // below. + !std::is_function::type>::type>::value && + + // Not for nullptr; we have another constructor for that below. + !std::is_same::type>::value && + + // Not for FunctionView objects; we have another constructor for that + // (the implicitly declared copy constructor). + !std::is_same::type>::type>::value>::type* = nullptr> + FunctionView(F&& f) + : call_(CallVoidPtr::type>) { + f_.void_ptr = &f; + } + + // Constructor that accepts function pointers. If the argument is null, the + // result is an empty FunctionView. + template < + typename F, + typename std::enable_if::type>::type>::value>::type* = + nullptr> + FunctionView(F&& f) + : call_(f ? CallFunPtr::type> : nullptr) { + f_.fun_ptr = reinterpret_cast(f); + } + + // Constructor that accepts nullptr. It creates an empty FunctionView. + template ::type>::value>::type* = nullptr> + FunctionView(F&& /* f */) : call_(nullptr) {} + + // Default constructor. Creates an empty FunctionView. + FunctionView() : call_(nullptr) {} + + RetT operator()(ArgT... args) const { + RTC_DCHECK(call_); + return call_(f_, std::forward(args)...); + } + + // Returns true if we have a function, false if we don't (i.e., we're null). + explicit operator bool() const { return !!call_; } + + private: + union VoidUnion { + void* void_ptr; + void (*fun_ptr)(); + }; + + template + static RetT CallVoidPtr(VoidUnion vu, ArgT... args) { + return (*static_cast(vu.void_ptr))(std::forward(args)...); + } + template + static RetT CallFunPtr(VoidUnion vu, ArgT... args) { + return (reinterpret_cast::type>(vu.fun_ptr))( + std::forward(args)...); + } + + // A pointer to the callable thing, with type information erased. It's a + // union because we have to use separate types depending on if the callable + // thing is a function pointer or something else. + VoidUnion f_; + + // Pointer to a dispatch function that knows the type of the callable thing + // that's stored in f_, and how to call it. A FunctionView object is empty + // (null) iff call_ is null. + RetT (*call_)(VoidUnion, ArgT...); +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::FunctionView; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // API_FUNCTION_VIEW_H_ diff --git a/pkg/apm/webrtc/api/ice_transport_factory.h b/pkg/apm/webrtc/api/ice_transport_factory.h new file mode 100644 index 00000000..cdd9db1a --- /dev/null +++ b/pkg/apm/webrtc/api/ice_transport_factory.h @@ -0,0 +1,44 @@ +/* + * 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_ICE_TRANSPORT_FACTORY_H_ +#define API_ICE_TRANSPORT_FACTORY_H_ + +#include "api/ice_transport_interface.h" +#include "api/scoped_refptr.h" +#include "p2p/base/port_allocator.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Static factory for an IceTransport object that can be created +// without using a webrtc::PeerConnection. +// The returned object must be accessed and destroyed on the thread that +// created it. +// The PortAllocator must outlive the created IceTransportInterface object. +// TODO(steveanton): Remove in favor of the overload that takes +// IceTransportInit. +RTC_EXPORT scoped_refptr CreateIceTransport( + PortAllocator* port_allocator); + +// Static factory for an IceTransport object that can be created +// without using a webrtc::PeerConnection. +// The returned object must be accessed and destroyed on the thread that +// created it. +// `init.port_allocator()` is required and must outlive the created +// IceTransportInterface object. +// `init.async_resolver_factory()` and `init.event_log()` are optional, but if +// provided must outlive the created IceTransportInterface object. +RTC_EXPORT scoped_refptr CreateIceTransport( + IceTransportInit); + +} // namespace webrtc + +#endif // API_ICE_TRANSPORT_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/ice_transport_interface.h b/pkg/apm/webrtc/api/ice_transport_interface.h new file mode 100644 index 00000000..d859ed6a --- /dev/null +++ b/pkg/apm/webrtc/api/ice_transport_interface.h @@ -0,0 +1,131 @@ +/* + * 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_ICE_TRANSPORT_INTERFACE_H_ +#define API_ICE_TRANSPORT_INTERFACE_H_ + +#include + +#include "api/async_dns_resolver.h" +#include "api/ref_count.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +class ActiveIceControllerFactoryInterface; +class FieldTrialsView; +class IceControllerFactoryInterface; +class IceTransportInternal; +class PortAllocator; + +// An ICE transport, as represented to the outside world. +// This object is refcounted, and is therefore alive until the +// last holder has released it. +class IceTransportInterface : public RefCountInterface { + public: + // Accessor for the internal representation of an ICE transport. + // The returned object can only be safely used on the signalling thread. + // TODO(crbug.com/907849): Add API calls for the functions that have to + // be exposed to clients, and stop allowing access to the + // webrtc::IceTransportInternal API. + virtual IceTransportInternal* internal() = 0; +}; + +struct IceTransportInit final { + public: + IceTransportInit() = default; + IceTransportInit(const IceTransportInit&) = delete; + IceTransportInit(IceTransportInit&&) = default; + IceTransportInit& operator=(const IceTransportInit&) = delete; + IceTransportInit& operator=(IceTransportInit&&) = default; + + PortAllocator* port_allocator() { return port_allocator_; } + void set_port_allocator(PortAllocator* port_allocator) { + port_allocator_ = port_allocator; + } + + AsyncDnsResolverFactoryInterface* async_dns_resolver_factory() { + return async_dns_resolver_factory_; + } + void set_async_dns_resolver_factory( + AsyncDnsResolverFactoryInterface* async_dns_resolver_factory) { + async_dns_resolver_factory_ = async_dns_resolver_factory; + } + + RtcEventLog* event_log() { return event_log_; } + void set_event_log(RtcEventLog* event_log) { event_log_ = event_log; } + + void set_ice_controller_factory( + IceControllerFactoryInterface* ice_controller_factory) { + ice_controller_factory_ = ice_controller_factory; + } + IceControllerFactoryInterface* ice_controller_factory() { + return ice_controller_factory_; + } + + // An active ICE controller actively manages the connection used by an ICE + // transport, in contrast with a legacy ICE controller that only picks the + // best connection to use or ping, and lets the transport decide when and + // whether to switch. + // + // Which ICE controller is used is determined as follows: + // + // 1. If an active ICE controller factory is supplied, it is used and + // the legacy ICE controller factory is not used. + // 2. If not, a default active ICE controller is used, wrapping over the + // supplied or the default legacy ICE controller. + void set_active_ice_controller_factory( + ActiveIceControllerFactoryInterface* active_ice_controller_factory) { + active_ice_controller_factory_ = active_ice_controller_factory; + } + ActiveIceControllerFactoryInterface* active_ice_controller_factory() { + return active_ice_controller_factory_; + } + + const FieldTrialsView* field_trials() { return field_trials_; } + void set_field_trials(const FieldTrialsView* field_trials) { + field_trials_ = field_trials; + } + + private: + PortAllocator* port_allocator_ = nullptr; + AsyncDnsResolverFactoryInterface* async_dns_resolver_factory_ = nullptr; + RtcEventLog* event_log_ = nullptr; + IceControllerFactoryInterface* ice_controller_factory_ = nullptr; + ActiveIceControllerFactoryInterface* active_ice_controller_factory_ = nullptr; + const FieldTrialsView* field_trials_ = nullptr; + // TODO(https://crbug.com/webrtc/12657): Redesign to have const members. +}; + +// TODO(qingsi): The factory interface is defined in this file instead of its +// namesake file ice_transport_factory.h to avoid the extra dependency on p2p/ +// introduced there by the p2p/-dependent factory methods. Move the factory +// methods to a different file or rename it. +class IceTransportFactory { + public: + virtual ~IceTransportFactory() = default; + // As a refcounted object, the returned ICE transport may outlive the host + // construct into which its reference is given, e.g. a peer connection. As a + // result, the returned ICE transport should not hold references to any object + // that the transport does not own and that has a lifetime bound to the host + // construct. Also, assumptions on the thread safety of the returned transport + // should be clarified by implementations. For example, a peer connection + // requires the returned transport to be constructed and destroyed on the + // network thread and an ICE transport factory that intends to work with a + // peer connection should offer transports compatible with these assumptions. + virtual scoped_refptr CreateIceTransport( + const std::string& transport_name, + int component, + IceTransportInit init) = 0; +}; + +} // namespace webrtc +#endif // API_ICE_TRANSPORT_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/jsep.h b/pkg/apm/webrtc/api/jsep.h new file mode 100644 index 00000000..c5674e90 --- /dev/null +++ b/pkg/apm/webrtc/api/jsep.h @@ -0,0 +1,267 @@ +/* + * Copyright 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file contains declarations of interfaces that wrap SDP-related +// constructs; session descriptions and ICE candidates. The inner "webrtc::" +// objects shouldn't be accessed directly; the intention is that an application +// using the PeerConnection API only creates these objects from strings, and +// them passes them into the PeerConnection. +// +// Though in the future, we're planning to provide an SDP parsing API, with a +// structure more friendly than webrtc::SessionDescription. + +#ifndef API_JSEP_H_ +#define API_JSEP_H_ + +#include + +#include +#include +#include +#include + +#include "api/candidate.h" +#include "api/ref_count.h" +#include "api/rtc_error.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +class SessionDescription; + +struct SdpParseError { + public: + // The sdp line that causes the error. + std::string line; + // Explains the error. + std::string description; +}; + +// Class representation of an ICE candidate. +// +// An instance of this interface is supposed to be owned by one class at +// a time and is therefore not expected to be thread safe. +// +// An instance can be created by CreateIceCandidate. +class RTC_EXPORT IceCandidateInterface { + public: + virtual ~IceCandidateInterface() {} + // If present, this is the value of the "a=mid" attribute of the candidate's + // m= section in SDP, which identifies the m= section. + virtual std::string sdp_mid() const = 0; + // This indicates the index (starting at zero) of m= section this candidate + // is associated with. Needed when an endpoint doesn't support MIDs. + virtual int sdp_mline_index() const = 0; + // Only for use internally. + virtual const Candidate& candidate() const = 0; + // The URL of the ICE server which this candidate was gathered from. + // TODO(zhihuang): Remove the default implementation once the subclasses + // implement this method. + virtual std::string server_url() const; + // Creates a SDP-ized form of this candidate. + virtual bool ToString(std::string* out) const = 0; +}; + +// Creates a IceCandidateInterface based on SDP string. +// Returns null if the sdp string can't be parsed. +// `error` may be null. +RTC_EXPORT IceCandidateInterface* CreateIceCandidate(const std::string& sdp_mid, + int sdp_mline_index, + const std::string& sdp, + SdpParseError* error); + +// Creates an IceCandidateInterface based on a parsed candidate structure. +RTC_EXPORT std::unique_ptr CreateIceCandidate( + const std::string& sdp_mid, + int sdp_mline_index, + const Candidate& candidate); + +// This class represents a collection of candidates for a specific m= section. +// Used in SessionDescriptionInterface. +class IceCandidateCollection { + public: + virtual ~IceCandidateCollection() {} + virtual size_t count() const = 0; + // Returns true if an equivalent `candidate` exist in the collection. + virtual bool HasCandidate(const IceCandidateInterface* candidate) const = 0; + virtual const IceCandidateInterface* at(size_t index) const = 0; +}; + +// Enum that describes the type of the SessionDescriptionInterface. +// Corresponds to RTCSdpType in the WebRTC specification. +// https://w3c.github.io/webrtc-pc/#dom-rtcsdptype +enum class SdpType { + kOffer, // Description must be treated as an SDP offer. + kPrAnswer, // Description must be treated as an SDP answer, but not a final + // answer. + kAnswer, // Description must be treated as an SDP final answer, and the + // offer-answer exchange must be considered complete after + // receiving this. + kRollback // Resets any pending offers and sets signaling state back to + // stable. +}; + +// Returns the string form of the given SDP type. String forms are defined in +// SessionDescriptionInterface. +RTC_EXPORT const char* SdpTypeToString(SdpType type); + +// Returns the SdpType from its string form. The string form can be one of the +// constants defined in SessionDescriptionInterface. Passing in any other string +// results in nullopt. +RTC_EXPORT std::optional SdpTypeFromString( + const std::string& type_str); + +// Class representation of an SDP session description. +// +// An instance of this interface is supposed to be owned by one class at a time +// and is therefore not expected to be thread safe. +// +// An instance can be created by CreateSessionDescription. +class RTC_EXPORT SessionDescriptionInterface { + public: + // String representations of the supported SDP types. + static const char kOffer[]; + static const char kPrAnswer[]; + static const char kAnswer[]; + static const char kRollback[]; + + virtual ~SessionDescriptionInterface() {} + + // Create a new SessionDescriptionInterface object + // with the same values as the old object. + // TODO(bugs.webrtc.org:12215): Remove default implementation + virtual std::unique_ptr Clone() const { + return nullptr; + } + + // Only for use internally. + virtual SessionDescription* description() = 0; + virtual const SessionDescription* description() const = 0; + + // Get the session id and session version, which are defined based on + // RFC 4566 for the SDP o= line. + virtual std::string session_id() const = 0; + virtual std::string session_version() const = 0; + + // Returns the type of this session description as an SdpType. Descriptions of + // the various types are found in the SdpType documentation. + // TODO(steveanton): Remove default implementation once Chromium has been + // updated. + virtual SdpType GetType() const; + + // kOffer/kPrAnswer/kAnswer + // TODO(steveanton): Remove this in favor of `GetType` that returns SdpType. + virtual std::string type() const = 0; + + // Adds the specified candidate to the description. + // + // Ownership is not transferred. + // + // Returns false if the session description does not have a media section + // that corresponds to `candidate.sdp_mid()` or + // `candidate.sdp_mline_index()`. + virtual bool AddCandidate(const IceCandidateInterface* candidate) = 0; + + // Removes the candidates from the description, if found. + // + // Returns the number of candidates removed. + virtual size_t RemoveCandidates(const std::vector& candidates); + + // Returns the number of m= sections in the session description. + virtual size_t number_of_mediasections() const = 0; + + // Returns a collection of all candidates that belong to a certain m= + // section. + virtual const IceCandidateCollection* candidates( + size_t mediasection_index) const = 0; + + // Serializes the description to SDP. + virtual bool ToString(std::string* out) const = 0; + template + friend void AbslStringify(Sink& sink, const SessionDescriptionInterface& p) { + sink.Append("\n--- BEGIN SDP "); + sink.Append(SdpTypeToString(p.GetType())); + sink.Append(" ---\n"); + std::string temp; + if (p.ToString(&temp)) { + sink.Append(temp); + } else { + sink.Append("Error in ToString\n"); + } + sink.Append("--- END SDP ---\n"); + } +}; + +// Creates a SessionDescriptionInterface based on the SDP string and the type. +// Returns null if the sdp string can't be parsed or the type is unsupported. +// `error` may be null. +// TODO(https://issues.webrtc.org/360909068): This function is deprecated. +// Please use the functions below which take an SdpType enum instead. Remove +// this once it is no longer used. +[[deprecated("Use version with SdpType argument")]] RTC_EXPORT + SessionDescriptionInterface* + CreateSessionDescription(const std::string& type, + const std::string& sdp, + SdpParseError* error); + +// Creates a SessionDescriptionInterface based on the SDP string and the type. +// Returns null if the SDP string cannot be parsed. +// If using the signature with `error_out`, details of the parsing error may be +// written to `error_out` if it is not null. +RTC_EXPORT std::unique_ptr +CreateSessionDescription(SdpType type, const std::string& sdp); +RTC_EXPORT std::unique_ptr +CreateSessionDescription(SdpType type, + const std::string& sdp, + SdpParseError* error_out); + +// Creates a SessionDescriptionInterface based on a parsed SDP structure and the +// given type, ID and version. +std::unique_ptr CreateSessionDescription( + SdpType type, + const std::string& session_id, + const std::string& session_version, + std::unique_ptr description); + +// CreateOffer and CreateAnswer callback interface. +class RTC_EXPORT CreateSessionDescriptionObserver + : public webrtc::RefCountInterface { + public: + // This callback transfers the ownership of the `desc`. + // TODO(deadbeef): Make this take an std::unique_ptr<> to avoid confusion + // around ownership. + virtual void OnSuccess(SessionDescriptionInterface* desc) = 0; + // The OnFailure callback takes an RTCError, which consists of an + // error code and a string. + // RTCError is non-copyable, so it must be passed using std::move. + // Earlier versions of the API used a string argument. This version + // is removed; its functionality was the same as passing + // error.message. + virtual void OnFailure(RTCError error) = 0; + + protected: + ~CreateSessionDescriptionObserver() override = default; +}; + +// SetLocalDescription and SetRemoteDescription callback interface. +class RTC_EXPORT SetSessionDescriptionObserver + : public webrtc::RefCountInterface { + public: + virtual void OnSuccess() = 0; + // See description in CreateSessionDescriptionObserver for OnFailure. + virtual void OnFailure(RTCError error) = 0; + + protected: + ~SetSessionDescriptionObserver() override = default; +}; + +} // namespace webrtc + +#endif // API_JSEP_H_ diff --git a/pkg/apm/webrtc/api/jsep_ice_candidate.h b/pkg/apm/webrtc/api/jsep_ice_candidate.h new file mode 100644 index 00000000..63880818 --- /dev/null +++ b/pkg/apm/webrtc/api/jsep_ice_candidate.h @@ -0,0 +1,88 @@ +/* + * Copyright 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// TODO(deadbeef): Move this out of api/; it's an implementation detail and +// shouldn't be used externally. + +#ifndef API_JSEP_ICE_CANDIDATE_H_ +#define API_JSEP_ICE_CANDIDATE_H_ + +#include + +#include +#include +#include + +#include "api/candidate.h" +#include "api/jsep.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Implementation of IceCandidateInterface. +class RTC_EXPORT JsepIceCandidate : public IceCandidateInterface { + public: + JsepIceCandidate(const std::string& sdp_mid, int sdp_mline_index); + JsepIceCandidate(const std::string& sdp_mid, + int sdp_mline_index, + const Candidate& candidate); + JsepIceCandidate(const JsepIceCandidate&) = delete; + JsepIceCandidate& operator=(const JsepIceCandidate&) = delete; + ~JsepIceCandidate() override; + // `err` may be null. + bool Initialize(const std::string& sdp, SdpParseError* err); + void SetCandidate(const Candidate& candidate) { candidate_ = candidate; } + + std::string sdp_mid() const override; + int sdp_mline_index() const override; + const Candidate& candidate() const override; + + std::string server_url() const override; + + bool ToString(std::string* out) const override; + + private: + std::string sdp_mid_; + int sdp_mline_index_; + Candidate candidate_; +}; + +// Implementation of IceCandidateCollection which stores JsepIceCandidates. +class JsepCandidateCollection : public IceCandidateCollection { + public: + JsepCandidateCollection(); + // Move constructor is defined so that a vector of JsepCandidateCollections + // can be resized. + JsepCandidateCollection(JsepCandidateCollection&& o); + + JsepCandidateCollection(const JsepCandidateCollection&) = delete; + JsepCandidateCollection& operator=(const JsepCandidateCollection&) = delete; + + // Returns a copy of the candidate collection. + JsepCandidateCollection Clone() const; + size_t count() const override; + bool HasCandidate(const IceCandidateInterface* candidate) const override; + // Adds and takes ownership of the JsepIceCandidate. + // TODO(deadbeef): Make this use an std::unique_ptr<>, so ownership logic is + // more clear. + virtual void add(JsepIceCandidate* candidate); + const IceCandidateInterface* at(size_t index) const override; + // Removes the candidate that has a matching address and protocol. + // + // Returns the number of candidates that were removed. + size_t remove(const Candidate& candidate); + + private: + std::vector> candidates_; +}; + +} // namespace webrtc + +#endif // API_JSEP_ICE_CANDIDATE_H_ diff --git a/pkg/apm/webrtc/api/jsep_session_description.h b/pkg/apm/webrtc/api/jsep_session_description.h new file mode 100644 index 00000000..2b25bb1c --- /dev/null +++ b/pkg/apm/webrtc/api/jsep_session_description.h @@ -0,0 +1,83 @@ +/* + * Copyright 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// TODO(deadbeef): Move this out of api/; it's an implementation detail and +// shouldn't be used externally. + +#ifndef API_JSEP_SESSION_DESCRIPTION_H_ +#define API_JSEP_SESSION_DESCRIPTION_H_ + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/candidate.h" +#include "api/jsep.h" +#include "api/jsep_ice_candidate.h" + +namespace webrtc { + +class SessionDescription; + +// Implementation of SessionDescriptionInterface. +class JsepSessionDescription : public SessionDescriptionInterface { + public: + explicit JsepSessionDescription(SdpType type); + // TODO(steveanton): Remove this once callers have switched to SdpType. + explicit JsepSessionDescription(const std::string& type); + JsepSessionDescription(SdpType type, + std::unique_ptr description, + absl::string_view session_id, + absl::string_view session_version); + virtual ~JsepSessionDescription(); + + JsepSessionDescription(const JsepSessionDescription&) = delete; + JsepSessionDescription& operator=(const JsepSessionDescription&) = delete; + + // Takes ownership of `description`. + bool Initialize(std::unique_ptr description, + const std::string& session_id, + const std::string& session_version); + + virtual std::unique_ptr Clone() const; + + virtual SessionDescription* description() { return description_.get(); } + virtual const SessionDescription* description() const { + return description_.get(); + } + virtual std::string session_id() const { return session_id_; } + virtual std::string session_version() const { return session_version_; } + virtual SdpType GetType() const { return type_; } + virtual std::string type() const { return SdpTypeToString(type_); } + // Allows changing the type. Used for testing. + virtual bool AddCandidate(const IceCandidateInterface* candidate); + virtual size_t RemoveCandidates(const std::vector& candidates); + virtual size_t number_of_mediasections() const; + virtual const IceCandidateCollection* candidates( + size_t mediasection_index) const; + virtual bool ToString(std::string* out) const; + + private: + std::unique_ptr description_; + std::string session_id_; + std::string session_version_; + SdpType type_; + std::vector candidate_collection_; + + bool GetMediasectionIndex(const IceCandidateInterface* candidate, + size_t* index); + int GetMediasectionIndex(const Candidate& candidate); +}; + +} // namespace webrtc + +#endif // API_JSEP_SESSION_DESCRIPTION_H_ diff --git a/pkg/apm/webrtc/api/legacy_stats_types.h b/pkg/apm/webrtc/api/legacy_stats_types.h new file mode 100644 index 00000000..8d94541e --- /dev/null +++ b/pkg/apm/webrtc/api/legacy_stats_types.h @@ -0,0 +1,482 @@ +/* + * Copyright 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file contains structures used for retrieving statistics from an ongoing +// libjingle session. + +#ifndef API_LEGACY_STATS_TYPES_H_ +#define API_LEGACY_STATS_TYPES_H_ + +#include +#include + +#include +#include +#include +#include + +#include "api/ref_count.h" +#include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class RTC_EXPORT StatsReport { + public: + // Indicates whether a track is for sending or receiving. + // Used in reports for audio/video tracks. + enum Direction { + kSend = 0, + kReceive, + }; + + enum StatsType { + // StatsReport types. + // A StatsReport of `type` = "googSession" contains overall information + // about the thing libjingle calls a session (which may contain one + // or more RTP sessions. + kStatsReportTypeSession, + + // A StatsReport of `type` = "googTransport" contains information + // about a libjingle "transport". + kStatsReportTypeTransport, + + // A StatsReport of `type` = "googComponent" contains information + // about a libjingle "channel" (typically, RTP or RTCP for a transport). + // This is intended to be the same thing as an ICE "Component". + kStatsReportTypeComponent, + + // A StatsReport of `type` = "googCandidatePair" contains information + // about a libjingle "connection" - a single source/destination port pair. + // This is intended to be the same thing as an ICE "candidate pair". + kStatsReportTypeCandidatePair, + + // A StatsReport of `type` = "VideoBWE" is statistics for video Bandwidth + // Estimation, which is global per-session. The `id` field is "bweforvideo" + // (will probably change in the future). + kStatsReportTypeBwe, + + // A StatsReport of `type` = "ssrc" is statistics for a specific rtp stream. + // The `id` field is the SSRC in decimal form of the rtp stream. + kStatsReportTypeSsrc, + + // A StatsReport of `type` = "remoteSsrc" is statistics for a specific + // rtp stream, generated by the remote end of the connection. + kStatsReportTypeRemoteSsrc, + + // A StatsReport of `type` = "googTrack" is statistics for a specific media + // track. The `id` field is the track id. + kStatsReportTypeTrack, + + // A StatsReport of `type` = "localcandidate" or "remotecandidate" is + // attributes on a specific ICE Candidate. It links to its connection pair + // by candidate id. The string value is taken from + // http://w3c.github.io/webrtc-stats/#rtcstatstype-enum*. + kStatsReportTypeIceLocalCandidate, + kStatsReportTypeIceRemoteCandidate, + + // A StatsReport of `type` = "googCertificate" contains an SSL certificate + // transmitted by one of the endpoints of this connection. The `id` is + // controlled by the fingerprint, and is used to identify the certificate in + // the Channel stats (as "googLocalCertificateId" or + // "googRemoteCertificateId") and in any child certificates (as + // "googIssuerId"). + kStatsReportTypeCertificate, + + // A StatsReport of `type` = "datachannel" with statistics for a + // particular DataChannel. + kStatsReportTypeDataChannel, + }; + + enum StatsValueName { + kStatsValueNameActiveConnection, + kStatsValueNameAecDivergentFilterFraction, + kStatsValueNameAudioInputLevel, + kStatsValueNameAudioOutputLevel, + kStatsValueNameBytesReceived, + kStatsValueNameBytesSent, + kStatsValueNameCodecImplementationName, + kStatsValueNameConcealedSamples, + kStatsValueNameConcealmentEvents, + kStatsValueNameDataChannelId, + kStatsValueNameFramesDecoded, + kStatsValueNameFramesEncoded, + kStatsValueNameJitterBufferDelay, + kStatsValueNameMediaType, + kStatsValueNamePacketsLost, + kStatsValueNamePacketsReceived, + kStatsValueNamePacketsSent, + kStatsValueNameProtocol, + kStatsValueNameQpSum, + kStatsValueNameReceiving, + kStatsValueNameSelectedCandidatePairId, + kStatsValueNameSsrc, + kStatsValueNameState, + kStatsValueNameTotalAudioEnergy, + kStatsValueNameTotalSamplesDuration, + kStatsValueNameTotalSamplesReceived, + kStatsValueNameTransportId, + kStatsValueNameSentPingRequestsTotal, + kStatsValueNameSentPingRequestsBeforeFirstResponse, + kStatsValueNameSentPingResponses, + kStatsValueNameRecvPingRequests, + kStatsValueNameRecvPingResponses, + kStatsValueNameSentStunKeepaliveRequests, + kStatsValueNameRecvStunKeepaliveResponses, + kStatsValueNameStunKeepaliveRttTotal, + kStatsValueNameStunKeepaliveRttSquaredTotal, + + // Internal StatsValue names. + kStatsValueNameAccelerateRate, + kStatsValueNameActualEncBitrate, + kStatsValueNameAdaptationChanges, + kStatsValueNameAvailableReceiveBandwidth, + kStatsValueNameAvailableSendBandwidth, + kStatsValueNameAvgEncodeMs, + kStatsValueNameBandwidthLimitedResolution, + kStatsValueNameBucketDelay, + kStatsValueNameCaptureStartNtpTimeMs, + kStatsValueNameCandidateIPAddress, + kStatsValueNameCandidateNetworkType, + kStatsValueNameCandidatePortNumber, + kStatsValueNameCandidatePriority, + kStatsValueNameCandidateTransportType, + kStatsValueNameCandidateType, + kStatsValueNameChannelId, + kStatsValueNameCodecName, + kStatsValueNameComponent, + kStatsValueNameContentName, + kStatsValueNameContentType, + kStatsValueNameCpuLimitedResolution, + kStatsValueNameCurrentDelayMs, + kStatsValueNameDecodeMs, + kStatsValueNameDecodingCNG, + kStatsValueNameDecodingCTN, + kStatsValueNameDecodingCTSG, + kStatsValueNameDecodingMutedOutput, + kStatsValueNameDecodingNormal, + kStatsValueNameDecodingPLC, + kStatsValueNameDecodingCodecPLC, + kStatsValueNameDecodingPLCCNG, + kStatsValueNameDer, + kStatsValueNameDtlsCipher, + kStatsValueNameEchoDelayMedian, + kStatsValueNameEchoDelayStdDev, + kStatsValueNameEchoReturnLoss, + kStatsValueNameEchoReturnLossEnhancement, + kStatsValueNameEncodeUsagePercent, + kStatsValueNameExpandRate, + kStatsValueNameFingerprint, + kStatsValueNameFingerprintAlgorithm, + kStatsValueNameFirsReceived, + kStatsValueNameFirsSent, + kStatsValueNameFirstFrameReceivedToDecodedMs, + kStatsValueNameFrameHeightInput, + kStatsValueNameFrameHeightReceived, + kStatsValueNameFrameHeightSent, + kStatsValueNameFrameRateDecoded, + kStatsValueNameFrameRateInput, + kStatsValueNameFrameRateOutput, + kStatsValueNameFrameRateReceived, + kStatsValueNameFrameRateSent, + kStatsValueNameFrameWidthInput, + kStatsValueNameFrameWidthReceived, + kStatsValueNameFrameWidthSent, + kStatsValueNameHasEnteredLowResolution, + kStatsValueNameHugeFramesSent, + kStatsValueNameInitiator, + kStatsValueNameInterframeDelayMaxMs, // Max over last 10 seconds. + kStatsValueNameIssuerId, + kStatsValueNameJitterBufferMs, + kStatsValueNameJitterReceived, + kStatsValueNameLabel, + kStatsValueNameLocalAddress, + kStatsValueNameLocalCandidateId, + kStatsValueNameLocalCandidateType, + kStatsValueNameLocalCertificateId, + kStatsValueNameMaxDecodeMs, + kStatsValueNameMinPlayoutDelayMs, + kStatsValueNameNacksReceived, + kStatsValueNameNacksSent, + kStatsValueNamePlisReceived, + kStatsValueNamePlisSent, + kStatsValueNamePreemptiveExpandRate, + kStatsValueNamePreferredJitterBufferMs, + kStatsValueNameRemoteAddress, + kStatsValueNameRemoteCandidateId, + kStatsValueNameRemoteCandidateType, + kStatsValueNameRemoteCertificateId, + kStatsValueNameRenderDelayMs, + kStatsValueNameResidualEchoLikelihood, + kStatsValueNameResidualEchoLikelihoodRecentMax, + kStatsValueNameAnaBitrateActionCounter, + kStatsValueNameAnaChannelActionCounter, + kStatsValueNameAnaDtxActionCounter, + kStatsValueNameAnaFecActionCounter, + kStatsValueNameAnaFrameLengthIncreaseCounter, + kStatsValueNameAnaFrameLengthDecreaseCounter, + kStatsValueNameAnaUplinkPacketLossFraction, + kStatsValueNameRetransmitBitrate, + kStatsValueNameRtt, + kStatsValueNameSecondaryDecodedRate, + kStatsValueNameSecondaryDiscardedRate, + kStatsValueNameSendPacketsDiscarded, + kStatsValueNameSpeechExpandRate, + kStatsValueNameSrtpCipher, + kStatsValueNameTargetDelayMs, + kStatsValueNameTargetEncBitrate, + kStatsValueNameTimingFrameInfo, // Result of `TimingFrameInfo::ToString` + kStatsValueNameTrackId, + kStatsValueNameTransmitBitrate, + kStatsValueNameTransportType, + kStatsValueNameWritable, + kStatsValueNameAudioDeviceUnderrunCounter, + kStatsValueNameLocalCandidateRelayProtocol, + }; + + class RTC_EXPORT IdBase : public webrtc::RefCountInterface { + public: + ~IdBase() override; + StatsType type() const; + + // Users of IdBase will be using the Id typedef, which is compatible with + // this Equals() function. It simply calls the protected (and overridden) + // Equals() method. + bool Equals(const scoped_refptr& other) const { + return Equals(*other.get()); + } + + virtual std::string ToString() const = 0; + + protected: + // Protected since users of the IdBase type will be using the Id typedef. + virtual bool Equals(const IdBase& other) const; + + explicit IdBase(StatsType type); // Only meant for derived classes. + const StatsType type_; + + static const char kSeparator = '_'; + }; + + typedef scoped_refptr Id; + + struct RTC_EXPORT Value { + enum Type { + kInt, // int. + kInt64, // int64_t. + kFloat, // float. + kString, // std::string + kStaticString, // const char*. + kBool, // bool. + kId, // Id. + }; + + Value(StatsValueName name, int64_t value, Type int_type); + Value(StatsValueName name, float f); + Value(StatsValueName name, const std::string& value); + Value(StatsValueName name, const char* value); + Value(StatsValueName name, bool b); + Value(StatsValueName name, const Id& value); + + ~Value(); + + Value(const Value&) = delete; + Value& operator=(const Value&) = delete; + + // Support ref counting. Note that for performance reasons, we + // don't use thread safe operations. Therefore, all operations + // affecting the ref count (in practice, creation and copying of + // the Values mapping) must occur on webrtc's signalling thread. + int AddRef() const { + RTC_DCHECK_RUN_ON(&thread_checker_); + return ++ref_count_; + } + int Release() const { + RTC_DCHECK_RUN_ON(&thread_checker_); + int count = --ref_count_; + if (!count) + delete this; + return count; + } + + // TODO(tommi): This compares name as well as value... + // I think we should only need to compare the value part and + // move the name part into a hash map. + bool Equals(const Value& other) const; + + // Comparison operators. Return true iff the current instance is of the + // correct type and holds the same value. No conversion is performed so + // a string value of "123" is not equal to an int value of 123 and an int + // value of 123 is not equal to a float value of 123.0f. + // One exception to this is that types kInt and kInt64 can be compared and + // kString and kStaticString too. + bool operator==(const std::string& value) const; + bool operator==(const char* value) const; + bool operator==(int64_t value) const; + bool operator==(bool value) const; + bool operator==(float value) const; + bool operator==(const Id& value) const; + + // Getters that allow getting the native value directly. + // The caller must know the type beforehand or else hit a check. + int int_val() const; + int64_t int64_val() const; + float float_val() const; + const char* static_string_val() const; + const std::string& string_val() const; + bool bool_val() const; + const Id& id_val() const; + + // Returns the string representation of `name`. + const char* display_name() const; + + // Converts the native value to a string representation of the value. + std::string ToString() const; + + Type type() const { return type_; } + + // TODO(tommi): Move `name` and `display_name` out of the Value struct. + const StatsValueName name; + + protected: +#if RTC_DCHECK_IS_ON + friend class StatsReport; + void DetachSequenceChecker() { thread_checker_.Detach(); } + void AttachSequenceChecker() { RTC_DCHECK_RUN_ON(&thread_checker_); } +#endif + + private: + webrtc::SequenceChecker thread_checker_{webrtc::SequenceChecker::kDetached}; + mutable int ref_count_ RTC_GUARDED_BY(thread_checker_) = 0; + + const Type type_; + // TODO(tommi): Use C++ 11 union and make value_ const. + union InternalType { + int int_; + int64_t int64_; + float float_; + bool bool_; + std::string* string_; + const char* static_string_; + Id* id_; + } value_; + }; + + typedef scoped_refptr ValuePtr; + typedef std::map Values; + + // Ownership of `id` is passed to `this`. + explicit StatsReport(const Id& id); + ~StatsReport(); + + StatsReport(const StatsReport&) = delete; + StatsReport& operator=(const StatsReport&) = delete; + + // Factory functions for various types of stats IDs. + static Id NewBandwidthEstimationId(); + static Id NewTypedId(StatsType type, const std::string& id); + static Id NewTypedIntId(StatsType type, int id); + static Id NewIdWithDirection(StatsType type, + const std::string& id, + Direction direction); + static Id NewCandidateId(bool local, const std::string& id); + static Id NewComponentId(const std::string& content_name, int component); + static Id NewCandidatePairId(const std::string& content_name, + int component, + int index); + + const Id& id() const { return id_; } + StatsType type() const { return id_->type(); } + double timestamp() const { return timestamp_; } + void set_timestamp(double t) { timestamp_ = t; } + bool empty() const { return values_.empty(); } + const Values& values() const { return values_; } + + const char* TypeToString() const; + + void AddString(StatsValueName name, const std::string& value); + void AddString(StatsValueName name, const char* value); + void AddInt64(StatsValueName name, int64_t value); + void AddInt(StatsValueName name, int value); + void AddFloat(StatsValueName name, float value); + void AddBoolean(StatsValueName name, bool value); + void AddId(StatsValueName name, const Id& value); + + const Value* FindValue(StatsValueName name) const; + +#if RTC_DCHECK_IS_ON + void DetachSequenceCheckers() { + for (auto& v : values_) { + v.second->DetachSequenceChecker(); + } + } + void AttachSequenceCheckers() { + for (auto& v : values_) { + v.second->AttachSequenceChecker(); + } + } +#endif + + private: + // The unique identifier for this object. + // This is used as a key for this report in ordered containers, + // so it must never be changed. + const Id id_; + double timestamp_; // Time since 1970-01-01T00:00:00Z in milliseconds. + Values values_; +}; + +// Typedef for an array of const StatsReport pointers. +// Ownership of the pointers held by this implementation is assumed to lie +// elsewhere and lifetime guarantees are made by the implementation that uses +// this type. In the StatsCollector, object ownership lies with the +// StatsCollection class. +typedef std::vector StatsReports; + +// A map from the report id to the report. +// This class wraps an STL container and provides a limited set of +// functionality in order to keep things simple. +class StatsCollection { + public: + StatsCollection(); + ~StatsCollection(); + + typedef std::list Container; + typedef Container::iterator iterator; + typedef Container::const_iterator const_iterator; + + const_iterator begin() const; + const_iterator end() const; + size_t size() const; + + // Creates a new report object with `id` that does not already + // exist in the list of reports. + StatsReport* InsertNew(const StatsReport::Id& id); + StatsReport* FindOrAddNew(const StatsReport::Id& id); + StatsReport* ReplaceOrAddNew(const StatsReport::Id& id); + + Container DetachCollection(); + void MergeCollection(Container collection); + + // Looks for a report with the given `id`. If one is not found, null + // will be returned. + StatsReport* Find(const StatsReport::Id& id); + + private: + Container list_; + webrtc::SequenceChecker thread_checker_{SequenceChecker::kDetached}; +}; + +} // namespace webrtc + +#endif // API_LEGACY_STATS_TYPES_H_ diff --git a/pkg/apm/webrtc/api/location.h b/pkg/apm/webrtc/api/location.h new file mode 100644 index 00000000..81e9a15d --- /dev/null +++ b/pkg/apm/webrtc/api/location.h @@ -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_ diff --git a/pkg/apm/webrtc/api/make_ref_counted.h b/pkg/apm/webrtc/api/make_ref_counted.h new file mode 100644 index 00000000..b7781e9b --- /dev/null +++ b/pkg/apm/webrtc/api/make_ref_counted.h @@ -0,0 +1,132 @@ +/* + * Copyright 2022 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef API_MAKE_REF_COUNTED_H_ +#define API_MAKE_REF_COUNTED_H_ + +#include +#include + +#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 +class HasAddRefAndRelease { + private: + template ().AddRef())* = nullptr, + decltype(std::declval().Release())* = nullptr> + static int Test(int); + template + static char Test(...); + + public: + static constexpr bool value = std::is_same_v(0)), int>; +}; +} // namespace webrtc_make_ref_counted_internal + +// General utilities for constructing a reference counted class and the +// appropriate reference count implementation for that class. +// +// These utilities select either the `RefCountedObject` implementation or +// `FinalRefCountedObject` depending on whether the to-be-shared class is +// derived from the RefCountInterface interface or not (respectively). + +// `make_ref_counted`: +// +// Use this when you want to construct a reference counted object of type T and +// get a `scoped_refptr<>` back. Example: +// +// auto p = make_ref_counted("bar", 123); +// +// For a class that inherits from RefCountInterface, this is equivalent to: +// +// auto p = scoped_refptr(new RefCountedObject("bar", 123)); +// +// If the class does not inherit from RefCountInterface, but does have +// AddRef/Release methods (so a T* is convertible to webrtc::scoped_refptr), +// this is equivalent to just +// +// auto p = scoped_refptr(new Foo("bar", 123)); +// +// Otherwise, the example is equivalent to: +// +// auto p = scoped_refptr>( +// new FinalRefCountedObject("bar", 123)); +// +// In these cases, `make_ref_counted` reduces the amount of boilerplate code but +// also helps with the most commonly intended usage of RefCountedObject whereby +// methods for reference counting, are virtual and designed to satisfy the need +// of an interface. When such a need does not exist, it is more efficient to use +// the `FinalRefCountedObject` template, which does not add the vtable overhead. +// +// Note that in some cases, using RefCountedObject directly may still be what's +// needed. + +// `make_ref_counted` for abstract classes that are convertible to +// RefCountInterface. The is_abstract requirement rejects classes that inherit +// both RefCountInterface and RefCounted object, which is a a discouraged +// pattern, and would result in double inheritance of RefCountedObject if this +// template was applied. +template < + typename T, + typename... Args, + typename std::enable_if && + std::is_abstract_v, + T>::type* = nullptr> +absl_nonnull scoped_refptr make_ref_counted(Args&&... args) { + return scoped_refptr(new RefCountedObject(std::forward(args)...)); +} + +// `make_ref_counted` for complete classes that are not convertible to +// RefCountInterface and already carry a ref count. +template < + typename T, + typename... Args, + typename std::enable_if< + !std::is_convertible_v && + webrtc_make_ref_counted_internal::HasAddRefAndRelease::value, + T>::type* = nullptr> +absl_nonnull scoped_refptr make_ref_counted(Args&&... args) { + return scoped_refptr(new T(std::forward(args)...)); +} + +// `make_ref_counted` for complete classes that are not convertible to +// RefCountInterface and have no ref count of their own. +template < + typename T, + typename... Args, + typename std::enable_if< + !std::is_convertible_v && + !webrtc_make_ref_counted_internal::HasAddRefAndRelease::value, + + T>::type* = nullptr> +absl_nonnull scoped_refptr> make_ref_counted( + Args&&... args) { + return scoped_refptr>( + new FinalRefCountedObject(std::forward(args)...)); +} + +} // namespace webrtc + +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +// Backwards compatibe alias. +// TODO: bugs.webrtc.org/42225969 - deprecate and remove. +using ::webrtc::make_ref_counted; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // API_MAKE_REF_COUNTED_H_ diff --git a/pkg/apm/webrtc/api/metronome/metronome.h b/pkg/apm/webrtc/api/metronome/metronome.h new file mode 100644 index 00000000..fb079762 --- /dev/null +++ b/pkg/apm/webrtc/api/metronome/metronome.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_METRONOME_METRONOME_H_ +#define API_METRONOME_METRONOME_H_ + +#include "absl/functional/any_invocable.h" +#include "api/units/time_delta.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// The Metronome posts OnTick() calls requested with RequestCallOnNextTick. +// The API is designed to be fully used from a single task queue. Scheduled +// callbacks are executed on the same sequence as they were requested on. There +// are no features implemented for cancellation. When that's needed, use e.g. +// ScopedTaskSafety from the client. +// +// The metronome concept is still under experimentation, and may not be availble +// in all platforms or applications. See https://crbug.com/1253787 for more +// details. +// +// Metronome implementations must be thread-compatible. +class RTC_EXPORT Metronome { + public: + virtual ~Metronome() = default; + + // Requests a call to `callback` on the next tick. Scheduled callbacks are + // executed on the same sequence as they were requested on. There are no + // features for cancellation. When that's needed, use e.g. ScopedTaskSafety + // from the client. + virtual void RequestCallOnNextTick( + absl::AnyInvocable /* callback */) {} + + // Returns the current tick period of the metronome. + virtual TimeDelta TickPeriod() const = 0; +}; + +} // namespace webrtc + +#endif // API_METRONOME_METRONOME_H_ diff --git a/pkg/apm/webrtc/api/neteq/custom_neteq_factory.h b/pkg/apm/webrtc/api/neteq/custom_neteq_factory.h new file mode 100644 index 00000000..b77eb068 --- /dev/null +++ b/pkg/apm/webrtc/api/neteq/custom_neteq_factory.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_NETEQ_CUSTOM_NETEQ_FACTORY_H_ +#define API_NETEQ_CUSTOM_NETEQ_FACTORY_H_ + +#include + +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/environment/environment.h" +#include "api/neteq/neteq.h" +#include "api/neteq/neteq_controller_factory.h" +#include "api/neteq/neteq_factory.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +// This factory can be used to generate NetEq instances that make use of a +// custom NetEqControllerFactory. +class CustomNetEqFactory : public NetEqFactory { + public: + explicit CustomNetEqFactory( + std::unique_ptr controller_factory); + ~CustomNetEqFactory() override; + CustomNetEqFactory(const CustomNetEqFactory&) = delete; + CustomNetEqFactory& operator=(const CustomNetEqFactory&) = delete; + + std::unique_ptr Create( + const Environment& env, + const NetEq::Config& config, + scoped_refptr decoder_factory) const override; + + private: + std::unique_ptr controller_factory_; +}; + +} // namespace webrtc +#endif // API_NETEQ_CUSTOM_NETEQ_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/neteq/default_neteq_controller_factory.h b/pkg/apm/webrtc/api/neteq/default_neteq_controller_factory.h new file mode 100644 index 00000000..e0c6926f --- /dev/null +++ b/pkg/apm/webrtc/api/neteq/default_neteq_controller_factory.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_NETEQ_DEFAULT_NETEQ_CONTROLLER_FACTORY_H_ +#define API_NETEQ_DEFAULT_NETEQ_CONTROLLER_FACTORY_H_ + +#include + +#include "api/environment/environment.h" +#include "api/neteq/neteq_controller.h" +#include "api/neteq/neteq_controller_factory.h" + +namespace webrtc { + +// This NetEqControllerFactory will use WebRTC's built-in controller logic. +class DefaultNetEqControllerFactory : public NetEqControllerFactory { + public: + DefaultNetEqControllerFactory(); + ~DefaultNetEqControllerFactory() override; + DefaultNetEqControllerFactory(const DefaultNetEqControllerFactory&) = delete; + DefaultNetEqControllerFactory& operator=( + const DefaultNetEqControllerFactory&) = delete; + + std::unique_ptr Create( + const Environment& env, + const NetEqController::Config& config) const override; +}; + +} // namespace webrtc +#endif // API_NETEQ_DEFAULT_NETEQ_CONTROLLER_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/neteq/default_neteq_factory.h b/pkg/apm/webrtc/api/neteq/default_neteq_factory.h new file mode 100644 index 00000000..444d0ee3 --- /dev/null +++ b/pkg/apm/webrtc/api/neteq/default_neteq_factory.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_NETEQ_DEFAULT_NETEQ_FACTORY_H_ +#define API_NETEQ_DEFAULT_NETEQ_FACTORY_H_ + +#include + +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/environment/environment.h" +#include "api/neteq/default_neteq_controller_factory.h" +#include "api/neteq/neteq.h" +#include "api/neteq/neteq_factory.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +class DefaultNetEqFactory : public NetEqFactory { + public: + DefaultNetEqFactory(); + ~DefaultNetEqFactory() override; + DefaultNetEqFactory(const DefaultNetEqFactory&) = delete; + DefaultNetEqFactory& operator=(const DefaultNetEqFactory&) = delete; + + std::unique_ptr Create( + const Environment& env, + const NetEq::Config& config, + scoped_refptr decoder_factory) const override; + + private: + const DefaultNetEqControllerFactory controller_factory_; +}; + +} // namespace webrtc +#endif // API_NETEQ_DEFAULT_NETEQ_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/neteq/neteq.h b/pkg/apm/webrtc/api/neteq/neteq.h new file mode 100644 index 00000000..67a1a834 --- /dev/null +++ b/pkg/apm/webrtc/api/neteq/neteq.h @@ -0,0 +1,342 @@ +/* + * 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_NETEQ_NETEQ_H_ +#define API_NETEQ_NETEQ_H_ + +#include // Provide access to size_t. +#include + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_format.h" +#include "api/rtp_headers.h" +#include "api/rtp_packet_info.h" +#include "api/units/timestamp.h" + +namespace webrtc { + +// Forward declarations. +class AudioFrame; + +struct NetEqNetworkStatistics { + uint16_t current_buffer_size_ms; // Current jitter buffer size in ms. + uint16_t preferred_buffer_size_ms; // Target buffer size in ms. + uint16_t jitter_peaks_found; // 1 if adding extra delay due to peaky + // jitter; 0 otherwise. + uint16_t expand_rate; // Fraction (of original stream) of synthesized + // audio inserted through expansion (in Q14). + uint16_t speech_expand_rate; // Fraction (of original stream) of synthesized + // speech inserted through expansion (in Q14). + uint16_t preemptive_rate; // Fraction of data inserted through pre-emptive + // expansion (in Q14). + uint16_t accelerate_rate; // Fraction of data removed through acceleration + // (in Q14). + uint16_t secondary_decoded_rate; // Fraction of data coming from FEC/RED + // decoding (in Q14). + uint16_t secondary_discarded_rate; // Fraction of discarded FEC/RED data (in + // Q14). + // Statistics for packet waiting times, i.e., the time between a packet + // arrives until it is decoded. + int mean_waiting_time_ms; + int median_waiting_time_ms; + int min_waiting_time_ms; + int max_waiting_time_ms; +}; + +// NetEq statistics that persist over the lifetime of the class. +// These metrics are never reset. +struct NetEqLifetimeStatistics { + // Stats below correspond to similarly-named fields in the WebRTC stats spec. + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats + uint64_t total_samples_received = 0; + uint64_t concealed_samples = 0; + uint64_t concealment_events = 0; + uint64_t jitter_buffer_delay_ms = 0; + uint64_t jitter_buffer_emitted_count = 0; + uint64_t jitter_buffer_target_delay_ms = 0; + uint64_t jitter_buffer_minimum_delay_ms = 0; + uint64_t inserted_samples_for_deceleration = 0; + uint64_t removed_samples_for_acceleration = 0; + uint64_t silent_concealed_samples = 0; + uint64_t fec_packets_received = 0; + uint64_t fec_packets_discarded = 0; + uint64_t packets_discarded = 0; + // Below stats are not part of the spec. + uint64_t delayed_packet_outage_samples = 0; + uint64_t delayed_packet_outage_events = 0; + // This is sum of relative packet arrival delays of received packets so far. + // Since end-to-end delay of a packet is difficult to measure and is not + // necessarily useful for measuring jitter buffer performance, we report a + // relative packet arrival delay. The relative packet arrival delay of a + // packet is defined as the arrival delay compared to the first packet + // received, given that it had zero delay. To avoid clock drift, the "first" + // packet can be made dynamic. + uint64_t relative_packet_arrival_delay_ms = 0; + uint64_t jitter_buffer_packets_received = 0; + // An interruption is a loss-concealment event lasting at least 150 ms. The + // two stats below count the number os such events and the total duration of + // these events. + int32_t interruption_count = 0; + int32_t total_interruption_duration_ms = 0; + // Total number of comfort noise samples generated during DTX. + uint64_t generated_noise_samples = 0; + uint64_t total_processing_delay_us = 0; +}; + +// Metrics that describe the operations performed in NetEq, and the internal +// state. +struct NetEqOperationsAndState { + // These sample counters are cumulative, and don't reset. As a reference, the + // total number of output samples can be found in + // NetEqLifetimeStatistics::total_samples_received. + uint64_t preemptive_samples = 0; + uint64_t accelerate_samples = 0; + // Count of the number of buffer flushes. + uint64_t packet_buffer_flushes = 0; + // The statistics below are not cumulative. + // The waiting time of the last decoded packet. + uint64_t last_waiting_time_ms = 0; + // The sum of the packet and jitter buffer size in ms. + uint64_t current_buffer_size_ms = 0; + // The current frame size in ms. + uint64_t current_frame_size_ms = 0; + // Flag to indicate that the next packet is available. + bool next_packet_available = false; +}; + +// This is the interface class for NetEq. +class NetEq { + public: + struct Config { + Config(); + Config(const Config&); + Config(Config&&); + ~Config(); + Config& operator=(const Config&); + Config& operator=(Config&&); + + std::string ToString() const; + + int sample_rate_hz = 48000; // Initial value. Will change with input data. + size_t max_packets_in_buffer = 200; + int max_delay_ms = 0; + int min_delay_ms = 0; + bool enable_fast_accelerate = false; + bool enable_muted_state = false; + bool enable_rtx_handling = false; + std::optional codec_pair_id; + bool for_test_no_time_stretching = false; // Use only for testing. + }; + + enum ReturnCodes { kOK = 0, kFail = -1 }; + + enum class Operation { + kNormal, + kMerge, + kExpand, + kAccelerate, + kFastAccelerate, + kPreemptiveExpand, + kRfc3389Cng, + kRfc3389CngNoPacket, + kCodecInternalCng, + kDtmf, + kUndefined, + }; + + enum class Mode { + kNormal, + kExpand, + kMerge, + kAccelerateSuccess, + kAccelerateLowEnergy, + kAccelerateFail, + kPreemptiveExpandSuccess, + kPreemptiveExpandLowEnergy, + kPreemptiveExpandFail, + kRfc3389Cng, + kCodecInternalCng, + kCodecPlc, + kDtmf, + kError, + kUndefined, + }; + + // Return type for GetDecoderFormat. + struct DecoderFormat { + int payload_type; + int sample_rate_hz; + int num_channels; + SdpAudioFormat sdp_format; + }; + + virtual ~NetEq() {} + + virtual int InsertPacket(const RTPHeader& rtp_header, + ArrayView payload) { + return InsertPacket(rtp_header, payload, + /*receive_time=*/Timestamp::MinusInfinity()); + } + + // TODO: webrtc:343501093 - removed unused method. + virtual int InsertPacket(const RTPHeader& rtp_header, + ArrayView payload, + Timestamp receive_time) { + return InsertPacket(rtp_header, payload, + RtpPacketInfo(rtp_header, receive_time)); + } + + // Inserts a new packet into NetEq. + // Returns 0 on success, -1 on failure. + // TODO: webrtc:343501093 - Make this method pure virtual. + virtual int InsertPacket(const RTPHeader& rtp_header, + ArrayView payload, + const RtpPacketInfo& /* rtp_packet_info */) { + return InsertPacket(rtp_header, payload); + } + + // Lets NetEq know that a packet arrived with an empty payload. This typically + // happens when empty packets are used for probing the network channel, and + // these packets use RTP sequence numbers from the same series as the actual + // audio packets. + virtual void InsertEmptyPacket(const RTPHeader& rtp_header) = 0; + + // Instructs NetEq to deliver 10 ms of audio data. The data is written to + // `audio_frame`. All data in `audio_frame` is wiped; `data_`, `speech_type_`, + // `num_channels_`, `sample_rate_hz_` and `samples_per_channel_` are updated + // upon success. If an error is returned, some fields may not have been + // updated, or may contain inconsistent values. If muted state is enabled + // (through Config::enable_muted_state), `muted` may be set to true after a + // prolonged expand period. When this happens, the `data_` in `audio_frame` + // is not written, but should be interpreted as being all zeros. For testing + // purposes, an override can be supplied in the `action_override` argument, + // which will cause NetEq to take this action next, instead of the action it + // would normally choose. An optional output argument for fetching the current + // sample rate can be provided, which will return the same value as + // last_output_sample_rate_hz() but will avoid additional synchronization. + // Returns kOK on success, or kFail in case of an error. + virtual int GetAudio( + AudioFrame* audio_frame, + bool* muted = nullptr, + int* current_sample_rate_hz = nullptr, + std::optional action_override = std::nullopt) = 0; + + // Replaces the current set of decoders with the given one. + virtual void SetCodecs(const std::map& codecs) = 0; + + // Associates `rtp_payload_type` with the given codec, which NetEq will + // instantiate when it needs it. Returns true iff successful. + virtual bool RegisterPayloadType(int rtp_payload_type, + const SdpAudioFormat& audio_format) = 0; + + // Removes `rtp_payload_type` from the codec database. Returns 0 on success, + // -1 on failure. Removing a payload type that is not registered is ok and + // will not result in an error. + virtual int RemovePayloadType(uint8_t rtp_payload_type) = 0; + + // Removes all payload types from the codec database. + virtual void RemoveAllPayloadTypes() = 0; + + // Sets a minimum delay in millisecond for packet buffer. The minimum is + // maintained unless a higher latency is dictated by channel condition. + // Returns true if the minimum is successfully applied, otherwise false is + // returned. + virtual bool SetMinimumDelay(int delay_ms) = 0; + + // Sets a maximum delay in milliseconds for packet buffer. The latency will + // not exceed the given value, even required delay (given the channel + // conditions) is higher. Calling this method has the same effect as setting + // the `max_delay_ms` value in the NetEq::Config struct. + virtual bool SetMaximumDelay(int delay_ms) = 0; + + // Sets a base minimum delay in milliseconds for packet buffer. The minimum + // delay which is set via `SetMinimumDelay` can't be lower than base minimum + // delay. Calling this method is similar to setting the `min_delay_ms` value + // in the NetEq::Config struct. Returns true if the base minimum is + // successfully applied, otherwise false is returned. + virtual bool SetBaseMinimumDelayMs(int delay_ms) = 0; + + // Returns current value of base minimum delay in milliseconds. + virtual int GetBaseMinimumDelayMs() const = 0; + + // Returns the current target delay in ms. This includes any extra delay + // requested through SetMinimumDelay. + virtual int TargetDelayMs() const = 0; + + // Returns the current total delay (packet buffer and sync buffer) in ms, + // with smoothing applied to even out short-time fluctuations due to jitter. + // The packet buffer part of the delay is not updated during DTX/CNG periods. + virtual int FilteredCurrentDelayMs() const = 0; + + // Writes the current network statistics to `stats`. The statistics are reset + // after the call. + virtual int NetworkStatistics(NetEqNetworkStatistics* stats) = 0; + + // Current values only, not resetting any state. + virtual NetEqNetworkStatistics CurrentNetworkStatistics() const = 0; + + // Returns a copy of this class's lifetime statistics. These statistics are + // never reset. + virtual NetEqLifetimeStatistics GetLifetimeStatistics() const = 0; + + // Returns statistics about the performed operations and internal state. These + // statistics are never reset. + virtual NetEqOperationsAndState GetOperationsAndState() const = 0; + + // Returns the RTP timestamp for the last sample delivered by GetAudio(). + // The return value will be empty if no valid timestamp is available. + virtual std::optional GetPlayoutTimestamp() const = 0; + + // Returns the sample rate in Hz of the audio produced in the last GetAudio + // call. If GetAudio has not been called yet, the configured sample rate + // (Config::sample_rate_hz) is returned. + virtual int last_output_sample_rate_hz() const = 0; + + // Returns the decoder info for the given payload type. Returns empty if no + // such payload type was registered. + [[deprecated( + "Use GetCurrentDecoderFormat")]] virtual std::optional + GetDecoderFormat(int /* payload_type */) const { + return std::nullopt; + } + + // Returns info for the most recently used decoder. + virtual std::optional GetCurrentDecoderFormat() const { + return std::nullopt; + } + + // Flushes both the packet buffer and the sync buffer. + virtual void FlushBuffers() = 0; + + // Enables NACK and sets the maximum size of the NACK list, which should be + // positive and no larger than Nack::kNackListSizeLimit. If NACK is already + // enabled then the maximum NACK list size is modified accordingly. + virtual void EnableNack(size_t max_nack_list_size) = 0; + + virtual void DisableNack() = 0; + + // Returns a list of RTP sequence numbers corresponding to packets to be + // retransmitted, given an estimate of the round-trip time in milliseconds. + virtual std::vector GetNackList( + int64_t round_trip_time_ms) const = 0; + + // Returns the length of the audio yet to play in the sync buffer. + // Mainly intended for testing. + virtual int SyncBufferSizeMs() const = 0; +}; + +} // namespace webrtc +#endif // API_NETEQ_NETEQ_H_ diff --git a/pkg/apm/webrtc/api/neteq/neteq_controller.h b/pkg/apm/webrtc/api/neteq/neteq_controller.h new file mode 100644 index 00000000..b764263c --- /dev/null +++ b/pkg/apm/webrtc/api/neteq/neteq_controller.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_NETEQ_NETEQ_CONTROLLER_H_ +#define API_NETEQ_NETEQ_CONTROLLER_H_ + +#include +#include +#include + +#include "api/neteq/neteq.h" +#include "api/neteq/tick_timer.h" + +namespace webrtc { + +// Decides the actions that NetEq should take. This affects the behavior of the +// jitter buffer, and how it reacts to network conditions. +// This class will undergo substantial refactoring in the near future, and the +// API is expected to undergo significant changes. A target API is given below: +// +// class NetEqController { +// public: +// // Resets object to a clean state. +// void Reset(); +// // Given NetEq status, make a decision. +// Operation GetDecision(NetEqStatus neteq_status); +// // Register every packet received. +// void RegisterPacket(PacketInfo packet_info); +// // Register empty packet. +// void RegisterEmptyPacket(); +// // Register a codec switching. +// void CodecSwithed(); +// // Sets the sample rate. +// void SetSampleRate(int fs_hz); +// // Sets the packet length in samples. +// void SetPacketLengthSamples(); +// // Sets maximum delay. +// void SetMaximumDelay(int delay_ms); +// // Sets mininum delay. +// void SetMinimumDelay(int delay_ms); +// // Sets base mininum delay. +// void SetBaseMinimumDelay(int delay_ms); +// // Gets target buffer level. +// int GetTargetBufferLevelMs() const; +// // Gets filtered buffer level. +// int GetFilteredBufferLevel() const; +// // Gets base minimum delay. +// int GetBaseMinimumDelay() const; +// } + +class NetEqController { + public: + // This struct is used to create a NetEqController. + struct Config { + bool allow_time_stretching; + bool enable_rtx_handling; + int max_packets_in_buffer; + int base_min_delay_ms; + TickTimer* tick_timer; + }; + + struct PacketInfo { + uint32_t timestamp; + bool is_dtx; + bool is_cng; + }; + + struct PacketBufferInfo { + bool dtx_or_cng; + size_t num_samples; + size_t span_samples; + size_t span_samples_wait_time; + size_t num_packets; + }; + + struct NetEqStatus { + uint32_t target_timestamp; + int16_t expand_mutefactor; + size_t last_packet_samples; + std::optional next_packet; + NetEq::Mode last_mode; + bool play_dtmf; + size_t generated_noise_samples; + PacketBufferInfo packet_buffer_info; + size_t sync_buffer_samples; + }; + + struct PacketArrivedInfo { + size_t packet_length_samples; + uint32_t main_timestamp; + uint16_t main_sequence_number; + bool is_cng_or_dtmf; + bool is_dtx; + bool buffer_flush; + }; + + virtual ~NetEqController() = default; + + // Resets object to a clean state. + virtual void Reset() = 0; + + // Resets parts of the state. Typically done when switching codecs. + virtual void SoftReset() = 0; + + // Given info about the latest received packet, and current jitter buffer + // status, returns the operation. `target_timestamp` and `expand_mutefactor` + // are provided for reference. `last_packet_samples` is the number of samples + // obtained from the last decoded frame. If there is a packet available, it + // should be supplied in `packet`. The mode resulting from the last call to + // NetEqImpl::GetAudio is supplied in `last_mode`. If there is a DTMF event to + // play, `play_dtmf` should be set to true. The output variable + // `reset_decoder` will be set to true if a reset is required; otherwise it is + // left unchanged (i.e., it can remain true if it was true before the call). + virtual NetEq::Operation GetDecision(const NetEqStatus& status, + bool* reset_decoder) = 0; + + // Inform NetEqController that an empty packet has arrived. + virtual void RegisterEmptyPacket() = 0; + + // Sets the sample rate and the output block size. + virtual void SetSampleRate(int fs_hz, size_t output_size_samples) = 0; + + // Sets a minimum or maximum delay in millisecond. + // Returns true if the delay bound is successfully applied, otherwise false. + virtual bool SetMaximumDelay(int delay_ms) = 0; + virtual bool SetMinimumDelay(int delay_ms) = 0; + + // Sets a base minimum delay in milliseconds for packet buffer. The effective + // minimum delay can't be lower than base minimum delay, even if a lower value + // is set using SetMinimumDelay. + // Returns true if the base minimum is successfully applied, otherwise false. + virtual bool SetBaseMinimumDelay(int delay_ms) = 0; + virtual int GetBaseMinimumDelay() const = 0; + + // Reports back to DecisionLogic whether the decision to do expand remains or + // not. Note that this is necessary, since an expand decision can be changed + // to kNormal in NetEqImpl::GetDecision if there is still enough data in the + // sync buffer. + virtual void ExpandDecision(NetEq::Operation operation) = 0; + + // Adds `value` to `sample_memory_`. + virtual void AddSampleMemory(int32_t value) = 0; + + // Returns the target buffer level in ms. + virtual int TargetLevelMs() const = 0; + + // Returns the target buffer level in ms as it would be if no minimum or + // maximum delay was set. + // TODO(bugs.webrtc.org/14270): Make pure virtual once all implementations are + // updated. + virtual int UnlimitedTargetLevelMs() const { return 0; } + + // Notify the NetEqController that a packet has arrived. Returns the relative + // arrival delay, if it can be computed. + virtual std::optional PacketArrived(int fs_hz, + bool should_update_stats, + const PacketArrivedInfo& info) = 0; + + // Notify the NetEqController that we are currently in muted state. + // TODO(bugs.webrtc.org/14270): Make pure virtual when downstream is updated. + virtual void NotifyMutedState() {} + + // Returns true if a peak was found. + virtual bool PeakFound() const = 0; + + // Get the filtered buffer level in samples. + virtual int GetFilteredBufferLevel() const = 0; + + // Accessors and mutators. + virtual void set_sample_memory(int32_t value) = 0; + virtual size_t noise_fast_forward() const = 0; + virtual size_t packet_length_samples() const = 0; + virtual void set_packet_length_samples(size_t value) = 0; + virtual void set_prev_time_scale(bool value) = 0; +}; + +} // namespace webrtc +#endif // API_NETEQ_NETEQ_CONTROLLER_H_ diff --git a/pkg/apm/webrtc/api/neteq/neteq_controller_factory.h b/pkg/apm/webrtc/api/neteq/neteq_controller_factory.h new file mode 100644 index 00000000..28c1f0b1 --- /dev/null +++ b/pkg/apm/webrtc/api/neteq/neteq_controller_factory.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_NETEQ_NETEQ_CONTROLLER_FACTORY_H_ +#define API_NETEQ_NETEQ_CONTROLLER_FACTORY_H_ + +#include + +#include "api/environment/environment.h" +#include "api/neteq/neteq_controller.h" + +namespace webrtc { + +// Creates NetEqController instances using the settings provided in the config +// struct. +class NetEqControllerFactory { + public: + virtual ~NetEqControllerFactory() = default; + + // Creates a new NetEqController object, with parameters set in `config`. + virtual std::unique_ptr Create( + const Environment& env, + const NetEqController::Config& config) const = 0; +}; + +} // namespace webrtc +#endif // API_NETEQ_NETEQ_CONTROLLER_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/neteq/neteq_factory.h b/pkg/apm/webrtc/api/neteq/neteq_factory.h new file mode 100644 index 00000000..98165c98 --- /dev/null +++ b/pkg/apm/webrtc/api/neteq/neteq_factory.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_NETEQ_NETEQ_FACTORY_H_ +#define API_NETEQ_NETEQ_FACTORY_H_ + +#include + +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/environment/environment.h" +#include "api/neteq/neteq.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +// Creates NetEq instances using the settings provided in the config struct. +class NetEqFactory { + public: + virtual ~NetEqFactory() = default; + + // Creates a new NetEq object, with parameters set in `config`. The `config` + // object will only have to be valid for the duration of the call to this + // method. + virtual std::unique_ptr Create( + const Environment& env, + const NetEq::Config& config, + scoped_refptr decoder_factory) const = 0; +}; + +} // namespace webrtc +#endif // API_NETEQ_NETEQ_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/neteq/tick_timer.h b/pkg/apm/webrtc/api/neteq/tick_timer.h new file mode 100644 index 00000000..e3f54a45 --- /dev/null +++ b/pkg/apm/webrtc/api/neteq/tick_timer.h @@ -0,0 +1,112 @@ +/* + * 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. + */ + +#ifndef API_NETEQ_TICK_TIMER_H_ +#define API_NETEQ_TICK_TIMER_H_ + +#include + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +// Implements a time counter. The counter is advanced with the Increment() +// methods, and is queried with the ticks() accessor. It is assumed that one +// "tick" of the counter corresponds to 10 ms. +// A TickTimer object can provide two types of associated time-measuring +// objects: Stopwatch and Countdown. +class TickTimer { + public: + // Stopwatch measures time elapsed since it was started, by querying the + // associated TickTimer for the current time. The intended use is to request a + // new Stopwatch object from a TickTimer object with the GetNewStopwatch() + // method. Note: since the Stopwatch object contains a reference to the + // TickTimer it is associated with, it cannot outlive the TickTimer. + class Stopwatch { + public: + explicit Stopwatch(const TickTimer& ticktimer); + + uint64_t ElapsedTicks() const { return ticktimer_.ticks() - starttick_; } + + uint64_t ElapsedMs() const { + const uint64_t elapsed_ticks = ticktimer_.ticks() - starttick_; + const int ms_per_tick = ticktimer_.ms_per_tick(); + return elapsed_ticks < UINT64_MAX / ms_per_tick + ? elapsed_ticks * ms_per_tick + : UINT64_MAX; + } + + private: + const TickTimer& ticktimer_; + const uint64_t starttick_; + }; + + // Countdown counts down from a given start value with each tick of the + // associated TickTimer, until zero is reached. The Finished() method will + // return true if zero has been reached, false otherwise. The intended use is + // to request a new Countdown object from a TickTimer object with the + // GetNewCountdown() method. Note: since the Countdown object contains a + // reference to the TickTimer it is associated with, it cannot outlive the + // TickTimer. + class Countdown { + public: + Countdown(const TickTimer& ticktimer, uint64_t ticks_to_count); + + ~Countdown(); + + bool Finished() const { + return stopwatch_->ElapsedTicks() >= ticks_to_count_; + } + + private: + const std::unique_ptr stopwatch_; + const uint64_t ticks_to_count_; + }; + + TickTimer() : TickTimer(10) {} + explicit TickTimer(int ms_per_tick) : ms_per_tick_(ms_per_tick) { + RTC_DCHECK_GT(ms_per_tick_, 0); + } + + TickTimer(const TickTimer&) = delete; + TickTimer& operator=(const TickTimer&) = delete; + + void Increment() { ++ticks_; } + + // Mainly intended for testing. + void Increment(uint64_t x) { ticks_ += x; } + + uint64_t ticks() const { return ticks_; } + + int ms_per_tick() const { return ms_per_tick_; } + + // Returns a new Stopwatch object, based on the current TickTimer. Note that + // the new Stopwatch object contains a reference to the current TickTimer, + // and must therefore not outlive the TickTimer. + std::unique_ptr GetNewStopwatch() const { + return std::unique_ptr(new Stopwatch(*this)); + } + + // Returns a new Countdown object, based on the current TickTimer. Note that + // the new Countdown object contains a reference to the current TickTimer, + // and must therefore not outlive the TickTimer. + std::unique_ptr GetNewCountdown(uint64_t ticks_to_count) const { + return std::unique_ptr(new Countdown(*this, ticks_to_count)); + } + + private: + uint64_t ticks_ = 0; + const int ms_per_tick_; +}; + +} // namespace webrtc +#endif // API_NETEQ_TICK_TIMER_H_ diff --git a/pkg/apm/webrtc/api/network_state_predictor.h b/pkg/apm/webrtc/api/network_state_predictor.h new file mode 100644 index 00000000..d3063cea --- /dev/null +++ b/pkg/apm/webrtc/api/network_state_predictor.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_NETWORK_STATE_PREDICTOR_H_ +#define API_NETWORK_STATE_PREDICTOR_H_ + +#include +#include + +#include "api/transport/bandwidth_usage.h" + +namespace webrtc { + +// TODO(yinwa): work in progress. API in class NetworkStatePredictor should not +// be used by other users until this comment is removed. + +// NetworkStatePredictor predict network state based on current network metrics. +// Usage: +// Setup by calling Initialize. +// For each update, call Update. Update returns network state +// prediction. +class NetworkStatePredictor { + public: + virtual ~NetworkStatePredictor() {} + + // Returns current network state prediction. + // Inputs: send_time_ms - packet send time. + // arrival_time_ms - packet arrival time. + // network_state - computed network state. + virtual BandwidthUsage Update(int64_t send_time_ms, + int64_t arrival_time_ms, + BandwidthUsage network_state) = 0; +}; + +class NetworkStatePredictorFactoryInterface { + public: + virtual std::unique_ptr + CreateNetworkStatePredictor() = 0; + virtual ~NetworkStatePredictorFactoryInterface() = default; +}; + +} // namespace webrtc + +#endif // API_NETWORK_STATE_PREDICTOR_H_ diff --git a/pkg/apm/webrtc/api/notifier.h b/pkg/apm/webrtc/api/notifier.h new file mode 100644 index 00000000..d1070a96 --- /dev/null +++ b/pkg/apm/webrtc/api/notifier.h @@ -0,0 +1,70 @@ +/* + * 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_NOTIFIER_H_ +#define API_NOTIFIER_H_ + +#include + +#include "api/media_stream_interface.h" +#include "api/sequence_checker.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +// Implements a template version of a notifier. +// TODO(deadbeef): This is an implementation detail; move out of api/. +template +class Notifier : public T { + public: + Notifier() = default; + + virtual void RegisterObserver(ObserverInterface* observer) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + RTC_DCHECK(observer != nullptr); + observers_.push_back(observer); + } + + virtual void UnregisterObserver(ObserverInterface* observer) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + for (std::list::iterator it = observers_.begin(); + it != observers_.end(); it++) { + if (*it == observer) { + observers_.erase(it); + break; + } + } + } + + void FireOnChanged() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + // Copy the list of observers to avoid a crash if the observer object + // unregisters as a result of the OnChanged() call. If the same list is used + // UnregisterObserver will affect the list make the iterator invalid. + std::list observers = observers_; + for (std::list::iterator it = observers.begin(); + it != observers.end(); ++it) { + (*it)->OnChanged(); + } + } + + protected: + std::list observers_ RTC_GUARDED_BY(sequence_checker_); + + private: + RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_{ + SequenceChecker::kDetached}; +}; + +} // namespace webrtc + +#endif // API_NOTIFIER_H_ diff --git a/pkg/apm/webrtc/api/numerics/numerics.go b/pkg/apm/webrtc/api/numerics/numerics.go new file mode 100644 index 00000000..e61a6f66 --- /dev/null +++ b/pkg/apm/webrtc/api/numerics/numerics.go @@ -0,0 +1,10 @@ +//go:build console + +package numerics + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/api/numerics/samples_stats_counter.cc b/pkg/apm/webrtc/api/numerics/samples_stats_counter.cc new file mode 100644 index 00000000..0c4dba98 --- /dev/null +++ b/pkg/apm/webrtc/api/numerics/samples_stats_counter.cc @@ -0,0 +1,104 @@ +/* + * 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/numerics/samples_stats_counter.h" + +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "api/units/timestamp.h" +#include "rtc_base/checks.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +SamplesStatsCounter::SamplesStatsCounter() = default; +SamplesStatsCounter::SamplesStatsCounter(size_t expected_samples_count) { + samples_.reserve(expected_samples_count); +} + +SamplesStatsCounter::~SamplesStatsCounter() = default; +SamplesStatsCounter::SamplesStatsCounter(const SamplesStatsCounter&) = default; +SamplesStatsCounter& SamplesStatsCounter::operator=( + const SamplesStatsCounter&) = default; +SamplesStatsCounter::SamplesStatsCounter(SamplesStatsCounter&&) = default; +SamplesStatsCounter& SamplesStatsCounter::operator=(SamplesStatsCounter&&) = + default; + +void SamplesStatsCounter::AddSample(double value) { + AddSample(StatsSample{value, Timestamp::Micros(TimeMicros())}); +} + +void SamplesStatsCounter::AddSample(StatsSample sample) { + stats_.AddSample(sample.value); + samples_.push_back(sample); + sorted_ = false; +} + +void SamplesStatsCounter::AddSamples(const SamplesStatsCounter& other) { + stats_.MergeStatistics(other.stats_); + samples_.insert(samples_.end(), other.samples_.begin(), other.samples_.end()); + sorted_ = false; +} + +double SamplesStatsCounter::GetPercentile(double percentile) { + RTC_DCHECK(!IsEmpty()); + RTC_CHECK_GE(percentile, 0); + RTC_CHECK_LE(percentile, 1); + if (!sorted_) { + absl::c_sort(samples_, [](const StatsSample& a, const StatsSample& b) { + return a.value < b.value; + }); + sorted_ = true; + } + const double raw_rank = percentile * (samples_.size() - 1); + double int_part; + double fract_part = std::modf(raw_rank, &int_part); + size_t rank = static_cast(int_part); + if (fract_part >= 1.0) { + // It can happen due to floating point calculation error. + rank++; + fract_part -= 1.0; + } + + RTC_DCHECK_GE(rank, 0); + RTC_DCHECK_LT(rank, samples_.size()); + RTC_DCHECK_GE(fract_part, 0); + RTC_DCHECK_LT(fract_part, 1); + RTC_DCHECK(rank + fract_part == raw_rank); + + const double low = samples_[rank].value; + const double high = samples_[std::min(rank + 1, samples_.size() - 1)].value; + return low + fract_part * (high - low); +} + +SamplesStatsCounter operator*(const SamplesStatsCounter& counter, + double value) { + SamplesStatsCounter out; + for (const auto& sample : counter.GetTimedSamples()) { + out.AddSample( + SamplesStatsCounter::StatsSample{sample.value * value, sample.time}); + } + return out; +} + +SamplesStatsCounter operator/(const SamplesStatsCounter& counter, + double value) { + SamplesStatsCounter out; + for (const auto& sample : counter.GetTimedSamples()) { + out.AddSample( + SamplesStatsCounter::StatsSample{sample.value / value, sample.time}); + } + return out; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/numerics/samples_stats_counter.h b/pkg/apm/webrtc/api/numerics/samples_stats_counter.h new file mode 100644 index 00000000..d993effe --- /dev/null +++ b/pkg/apm/webrtc/api/numerics/samples_stats_counter.h @@ -0,0 +1,135 @@ +/* + * 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_NUMERICS_SAMPLES_STATS_COUNTER_H_ +#define API_NUMERICS_SAMPLES_STATS_COUNTER_H_ + +#include +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "api/units/timestamp.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/running_statistics.h" + +namespace webrtc { + +// This class extends RunningStatistics by providing GetPercentile() method, +// while slightly adapting the interface. +class SamplesStatsCounter { + public: + struct StatsSample { + double value; + Timestamp time; + // Sample's specific metadata. + std::map metadata; + }; + + SamplesStatsCounter(); + explicit SamplesStatsCounter(size_t expected_samples_count); + ~SamplesStatsCounter(); + SamplesStatsCounter(const SamplesStatsCounter&); + SamplesStatsCounter& operator=(const SamplesStatsCounter&); + SamplesStatsCounter(SamplesStatsCounter&&); + SamplesStatsCounter& operator=(SamplesStatsCounter&&); + + // Adds sample to the stats in amortized O(1) time. + void AddSample(double value); + void AddSample(StatsSample sample); + + // Adds samples from another counter. + void AddSamples(const SamplesStatsCounter& other); + + // Returns if there are any values in O(1) time. + bool IsEmpty() const { return samples_.empty(); } + // Returns the amount of samples added into counter in O(1) time. + int64_t NumSamples() const { return stats_.Size(); } + + // Returns min in O(1) time. This function may not be called if there are no + // samples. + double GetMin() const { + RTC_DCHECK(!IsEmpty()); + return *stats_.GetMin(); + } + // Returns max in O(1) time. This function may not be called if there are no + // samples. + double GetMax() const { + RTC_DCHECK(!IsEmpty()); + return *stats_.GetMax(); + } + // Returns sum in O(1) time. This function may not be called if there are + // no samples. + double GetSum() const { + RTC_DCHECK(!IsEmpty()); + return *stats_.GetSum(); + } + // Returns average in O(1) time. This function may not be called if there are + // no samples. + double GetAverage() const { + RTC_DCHECK(!IsEmpty()); + return *stats_.GetMean(); + } + // Returns variance in O(1) time. This function may not be called if there are + // no samples. + double GetVariance() const { + RTC_DCHECK(!IsEmpty()); + return *stats_.GetVariance(); + } + // Returns standard deviation in O(1) time. This function may not be called if + // there are no samples. + double GetStandardDeviation() const { + RTC_DCHECK(!IsEmpty()); + return *stats_.GetStandardDeviation(); + } + // Returns percentile in O(nlogn) on first call and in O(1) after, if no + // additions were done. This function may not be called if there are no + // samples. + // + // `percentile` has to be in [0; 1]. 0 percentile is the min in the array and + // 1 percentile is the max in the array. + double GetPercentile(double percentile); + // Returns array view with all samples added into counter. There are no + // guarantees of order, so samples can be in different order comparing to in + // which they were added into counter. Also return value will be invalidate + // after call to any non const method. + ArrayView GetTimedSamples() const { return samples_; } + std::vector GetSamples() const { + std::vector out; + out.reserve(samples_.size()); + for (const auto& sample : samples_) { + out.push_back(sample.value); + } + return out; + } + + private: + webrtc_impl::RunningStatistics stats_; + std::vector samples_; + bool sorted_ = false; +}; + +// Multiply all sample values on `value` and return new SamplesStatsCounter +// with resulted samples. Doesn't change origin SamplesStatsCounter. +SamplesStatsCounter operator*(const SamplesStatsCounter& counter, double value); +inline SamplesStatsCounter operator*(double value, + const SamplesStatsCounter& counter) { + return counter * value; +} +// Divide all sample values on `value` and return new SamplesStatsCounter with +// resulted samples. Doesn't change origin SamplesStatsCounter. +SamplesStatsCounter operator/(const SamplesStatsCounter& counter, double value); + +} // namespace webrtc + +#endif // API_NUMERICS_SAMPLES_STATS_COUNTER_H_ diff --git a/pkg/apm/webrtc/api/packet_socket_factory.h b/pkg/apm/webrtc/api/packet_socket_factory.h new file mode 100644 index 00000000..602eeaa9 --- /dev/null +++ b/pkg/apm/webrtc/api/packet_socket_factory.h @@ -0,0 +1,90 @@ +/* + * 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_PACKET_SOCKET_FACTORY_H_ +#define API_PACKET_SOCKET_FACTORY_H_ + +#include +#include +#include +#include + +#include "api/async_dns_resolver.h" +#include "rtc_base/async_packet_socket.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +struct PacketSocketTcpOptions { + PacketSocketTcpOptions() = default; + ~PacketSocketTcpOptions() = default; + + int opts = 0; + std::vector tls_alpn_protocols; + std::vector tls_elliptic_curves; + // An optional custom SSL certificate verifier that an API user can provide to + // inject their own certificate verification logic (not available to users + // outside of the WebRTC repo). + SSLCertificateVerifier* tls_cert_verifier = nullptr; +}; + +class RTC_EXPORT PacketSocketFactory { + public: + enum Options { + OPT_STUN = 0x04, + + // The TLS options below are mutually exclusive. + OPT_TLS = 0x02, // Real and secure TLS. + OPT_TLS_FAKE = 0x01, // Fake TLS with a dummy SSL handshake. + OPT_TLS_INSECURE = 0x08, // Insecure TLS without certificate validation. + + // Deprecated, use OPT_TLS_FAKE. + OPT_SSLTCP = OPT_TLS_FAKE, + }; + + PacketSocketFactory() = default; + virtual ~PacketSocketFactory() = default; + + virtual AsyncPacketSocket* CreateUdpSocket(const SocketAddress& address, + uint16_t min_port, + uint16_t max_port) = 0; + virtual AsyncListenSocket* CreateServerTcpSocket( + const SocketAddress& local_address, + uint16_t min_port, + uint16_t max_port, + int opts) = 0; + + virtual AsyncPacketSocket* CreateClientTcpSocket( + const SocketAddress& local_address, + const SocketAddress& remote_address, + const PacketSocketTcpOptions& tcp_options) = 0; + + virtual std::unique_ptr + CreateAsyncDnsResolver() = 0; + + private: + PacketSocketFactory(const PacketSocketFactory&) = delete; + PacketSocketFactory& operator=(const PacketSocketFactory&) = delete; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::PacketSocketFactory; +using ::webrtc::PacketSocketTcpOptions; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // API_PACKET_SOCKET_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/peer_connection_interface.h b/pkg/apm/webrtc/api/peer_connection_interface.h new file mode 100644 index 00000000..e5b3853e --- /dev/null +++ b/pkg/apm/webrtc/api/peer_connection_interface.h @@ -0,0 +1,1717 @@ +/* + * Copyright 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file contains the PeerConnection interface as defined in +// https://w3c.github.io/webrtc-pc/#peer-to-peer-connections +// +// The PeerConnectionFactory class provides factory methods to create +// PeerConnection, MediaStream and MediaStreamTrack objects. +// +// The following steps are needed to setup a typical call using WebRTC: +// +// 1. Create a PeerConnectionFactoryInterface. Check constructors for more +// information about input parameters. +// +// 2. Create a PeerConnection object. Provide a configuration struct which +// points to STUN and/or TURN servers used to generate ICE candidates, and +// provide an object that implements the PeerConnectionObserver interface, +// which is used to receive callbacks from the PeerConnection. +// +// 3. Create local MediaStreamTracks using the PeerConnectionFactory and add +// them to PeerConnection by calling AddTrack (or legacy method, AddStream). +// +// 4. Create an offer, call SetLocalDescription with it, serialize it, and send +// it to the remote peer +// +// 5. Once an ICE candidate has been gathered, the PeerConnection will call the +// observer function OnIceCandidate. The candidates must also be serialized and +// sent to the remote peer. +// +// 6. Once an answer is received from the remote peer, call +// SetRemoteDescription with the remote answer. +// +// 7. Once a remote candidate is received from the remote peer, provide it to +// the PeerConnection by calling AddIceCandidate. +// +// The receiver of a call (assuming the application is "call"-based) can decide +// to accept or reject the call; this decision will be taken by the application, +// not the PeerConnection. +// +// If the application decides to accept the call, it should: +// +// 1. Create PeerConnectionFactoryInterface if it doesn't exist. +// +// 2. Create a new PeerConnection. +// +// 3. Provide the remote offer to the new PeerConnection object by calling +// SetRemoteDescription. +// +// 4. Generate an answer to the remote offer by calling CreateAnswer and send it +// back to the remote peer. +// +// 5. Provide the local answer to the new PeerConnection by calling +// SetLocalDescription with the answer. +// +// 6. Provide the remote ICE candidates by calling AddIceCandidate. +// +// 7. Once a candidate has been gathered, the PeerConnection will call the +// observer function OnIceCandidate. Send these candidates to the remote peer. + +#ifndef API_PEER_CONNECTION_INTERFACE_H_ +#define API_PEER_CONNECTION_INTERFACE_H_ +// IWYU pragma: no_include "pc/media_factory.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/strings/string_view.h" +#include "api/adaptation/resource.h" +#include "api/async_dns_resolver.h" +#include "api/audio/audio_device.h" +#include "api/audio/audio_mixer.h" +#include "api/audio/audio_processing.h" +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/audio_codecs/audio_encoder_factory.h" +#include "api/audio_options.h" +#include "api/candidate.h" +#include "api/crypto/crypto_options.h" +#include "api/data_channel_event_observer_interface.h" +#include "api/data_channel_interface.h" +#include "api/dtls_transport_interface.h" +#include "api/fec_controller.h" +#include "api/field_trials_view.h" +#include "api/ice_transport_interface.h" +#include "api/jsep.h" +#include "api/legacy_stats_types.h" +#include "api/media_stream_interface.h" +#include "api/media_types.h" +#include "api/metronome/metronome.h" +#include "api/neteq/neteq_factory.h" +#include "api/network_state_predictor.h" +#include "api/packet_socket_factory.h" +#include "api/rtc_error.h" +#include "api/rtc_event_log/rtc_event_log_factory_interface.h" +#include "api/rtc_event_log_output.h" +#include "api/rtp_parameters.h" +#include "api/rtp_receiver_interface.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" +#include "api/sctp_transport_interface.h" +#include "api/set_local_description_observer_interface.h" +#include "api/set_remote_description_observer_interface.h" +#include "api/stats/rtc_stats_collector_callback.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/transport/bandwidth_estimation_settings.h" +#include "api/transport/bitrate_settings.h" +#include "api/transport/enums.h" +#include "api/transport/network_control.h" +#include "api/transport/sctp_transport_factory_interface.h" +#include "api/turn_customizer.h" +#include "api/video/video_bitrate_allocator_factory.h" +#include "api/video_codecs/video_decoder_factory.h" +#include "api/video_codecs/video_encoder_factory.h" +#include "call/rtp_transport_controller_send_factory_interface.h" +#include "media/base/media_config.h" +// TODO(bugs.webrtc.org/7447): We plan to provide a way to let applications +// inject a PacketSocketFactory and/or NetworkManager, and not expose +// PortAllocator in the PeerConnection api. +#include "api/audio/audio_frame_processor.h" +#include "api/ref_count.h" +#include "api/units/time_delta.h" +#include "p2p/base/port.h" +#include "p2p/base/port_allocator.h" +#include "rtc_base/checks.h" +#include "rtc_base/network.h" +#include "rtc_base/network_constants.h" +#include "rtc_base/network_monitor_factory.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/socket_factory.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread.h" + +namespace webrtc { +// IWYU pragma: begin_keep +// MediaFactory class definition is not part of the api. +class MediaFactory; + +// IWYU pragma: end_keep +// MediaStream container interface. +class StreamCollectionInterface : public webrtc::RefCountInterface { + public: + // TODO(ronghuawu): Update the function names to c++ style, e.g. find -> Find. + virtual size_t count() = 0; + virtual MediaStreamInterface* at(size_t index) = 0; + virtual MediaStreamInterface* find(const std::string& label) = 0; + virtual MediaStreamTrackInterface* FindAudioTrack(const std::string& id) = 0; + virtual MediaStreamTrackInterface* FindVideoTrack(const std::string& id) = 0; + + protected: + // Dtor protected as objects shouldn't be deleted via this interface. + ~StreamCollectionInterface() override = default; +}; + +class StatsObserver : public webrtc::RefCountInterface { + public: + virtual void OnComplete(const StatsReports& reports) = 0; + + protected: + ~StatsObserver() override = default; +}; + +enum class SdpSemantics { + // TODO(https://crbug.com/webrtc/13528): Remove support for kPlanB. + kPlanB_DEPRECATED, + kPlanB [[deprecated]] = kPlanB_DEPRECATED, + kUnifiedPlan, +}; + +class RTC_EXPORT PeerConnectionInterface : public webrtc::RefCountInterface { + public: + // See https://w3c.github.io/webrtc-pc/#dom-rtcsignalingstate + enum SignalingState { + kStable, + kHaveLocalOffer, + kHaveLocalPrAnswer, + kHaveRemoteOffer, + kHaveRemotePrAnswer, + kClosed, + }; + static constexpr absl::string_view AsString(SignalingState); + + // See https://w3c.github.io/webrtc-pc/#dom-rtcicegatheringstate + enum IceGatheringState { + kIceGatheringNew, + kIceGatheringGathering, + kIceGatheringComplete + }; + static constexpr absl::string_view AsString(IceGatheringState state); + + // See https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnectionstate + enum class PeerConnectionState { + kNew, + kConnecting, + kConnected, + kDisconnected, + kFailed, + kClosed, + }; + static constexpr absl::string_view AsString(PeerConnectionState state); + + // See https://w3c.github.io/webrtc-pc/#dom-rtciceconnectionstate + enum IceConnectionState { + kIceConnectionNew, + kIceConnectionChecking, + kIceConnectionConnected, + kIceConnectionCompleted, + kIceConnectionFailed, + kIceConnectionDisconnected, + kIceConnectionClosed, + kIceConnectionMax, + }; + static constexpr absl::string_view AsString(IceConnectionState state); + + // TLS certificate policy. + enum TlsCertPolicy { + // For TLS based protocols, ensure the connection is secure by not + // circumventing certificate validation. + kTlsCertPolicySecure, + // For TLS based protocols, disregard security completely by skipping + // certificate validation. This is insecure and should never be used unless + // security is irrelevant in that particular context. + kTlsCertPolicyInsecureNoCheck, + }; + + struct RTC_EXPORT IceServer { + IceServer(); + IceServer(const IceServer&); + ~IceServer(); + + // TODO(jbauch): Remove uri when all code using it has switched to urls. + // List of URIs associated with this server. Valid formats are described + // in RFC7064 and RFC7065, and more may be added in the future. The "host" + // part of the URI may contain either an IP address or a hostname. + std::string uri; + std::vector urls; + std::string username; + std::string password; + TlsCertPolicy tls_cert_policy = kTlsCertPolicySecure; + // If the URIs in `urls` only contain IP addresses, this field can be used + // to indicate the hostname, which may be necessary for TLS (using the SNI + // extension). If `urls` itself contains the hostname, this isn't + // necessary. + std::string hostname; + // List of protocols to be used in the TLS ALPN extension. + std::vector tls_alpn_protocols; + // List of elliptic curves to be used in the TLS elliptic curves extension. + std::vector tls_elliptic_curves; + + bool operator==(const IceServer& o) const { + return uri == o.uri && urls == o.urls && username == o.username && + password == o.password && tls_cert_policy == o.tls_cert_policy && + hostname == o.hostname && + tls_alpn_protocols == o.tls_alpn_protocols && + tls_elliptic_curves == o.tls_elliptic_curves; + } + bool operator!=(const IceServer& o) const { return !(*this == o); } + }; + typedef std::vector IceServers; + + enum IceTransportsType { + // TODO(pthatcher): Rename these kTransporTypeXXX, but update + // Chromium at the same time. + kNone, + kRelay, + kNoHost, + kAll + }; + + // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24#section-4.1.1 + enum BundlePolicy { + kBundlePolicyBalanced, + kBundlePolicyMaxBundle, + kBundlePolicyMaxCompat + }; + + // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24#section-4.1.1 + enum RtcpMuxPolicy { + kRtcpMuxPolicyNegotiate, + kRtcpMuxPolicyRequire, + }; + + enum TcpCandidatePolicy { + kTcpCandidatePolicyEnabled, + kTcpCandidatePolicyDisabled + }; + + enum CandidateNetworkPolicy { + kCandidateNetworkPolicyAll, + kCandidateNetworkPolicyLowCost + }; + + enum ContinualGatheringPolicy { GATHER_ONCE, GATHER_CONTINUALLY }; + + struct PortAllocatorConfig { + // For min_port and max_port, 0 means not specified. + int min_port = 0; + int max_port = 0; + uint32_t flags = 0; // Same as kDefaultPortAllocatorFlags. + }; + + enum class RTCConfigurationType { + // A configuration that is safer to use, despite not having the best + // performance. Currently this is the default configuration. + kSafe, + // An aggressive configuration that has better performance, although it + // may be riskier and may need extra support in the application. + kAggressive + }; + + // TODO(hbos): Change into class with private data and public getters. + // TODO(nisse): In particular, accessing fields directly from an + // application is brittle, since the organization mirrors the + // organization of the implementation, which isn't stable. So we + // need getters and setters at least for fields which applications + // are interested in. + struct RTC_EXPORT RTCConfiguration { + // This struct is subject to reorganization, both for naming + // consistency, and to group settings to match where they are used + // in the implementation. To do that, we need getter and setter + // methods for all settings which are of interest to applications, + // Chrome in particular. + + RTCConfiguration(); + RTCConfiguration(const RTCConfiguration&); + explicit RTCConfiguration(RTCConfigurationType type); + ~RTCConfiguration(); + + bool operator==(const RTCConfiguration& o) const; + bool operator!=(const RTCConfiguration& o) const; + + bool dscp() const { return media_config.enable_dscp; } + void set_dscp(bool enable) { media_config.enable_dscp = enable; } + + bool stats_timestamp_with_environment_clock() const { + return media_config.stats_timestamp_with_environment_clock; + } + void set_stats_timestamp_with_environment_clock(bool enable) { + media_config.stats_timestamp_with_environment_clock = enable; + } + + bool cpu_adaptation() const { + return media_config.video.enable_cpu_adaptation; + } + void set_cpu_adaptation(bool enable) { + media_config.video.enable_cpu_adaptation = enable; + } + + bool suspend_below_min_bitrate() const { + return media_config.video.suspend_below_min_bitrate; + } + void set_suspend_below_min_bitrate(bool enable) { + media_config.video.suspend_below_min_bitrate = enable; + } + + bool prerenderer_smoothing() const { + return media_config.video.enable_prerenderer_smoothing; + } + void set_prerenderer_smoothing(bool enable) { + media_config.video.enable_prerenderer_smoothing = enable; + } + + bool experiment_cpu_load_estimator() const { + return media_config.video.experiment_cpu_load_estimator; + } + void set_experiment_cpu_load_estimator(bool enable) { + media_config.video.experiment_cpu_load_estimator = enable; + } + + int audio_rtcp_report_interval_ms() const { + return media_config.audio.rtcp_report_interval_ms; + } + void set_audio_rtcp_report_interval_ms(int audio_rtcp_report_interval_ms) { + media_config.audio.rtcp_report_interval_ms = + audio_rtcp_report_interval_ms; + } + + int video_rtcp_report_interval_ms() const { + return media_config.video.rtcp_report_interval_ms; + } + void set_video_rtcp_report_interval_ms(int video_rtcp_report_interval_ms) { + media_config.video.rtcp_report_interval_ms = + video_rtcp_report_interval_ms; + } + + // Settings for the port allcoator. Applied only if the port allocator is + // created by PeerConnectionFactory, not if it is injected with + // PeerConnectionDependencies + int min_port() const { return port_allocator_config.min_port; } + void set_min_port(int port) { port_allocator_config.min_port = port; } + int max_port() const { return port_allocator_config.max_port; } + void set_max_port(int port) { port_allocator_config.max_port = port; } + uint32_t port_allocator_flags() { return port_allocator_config.flags; } + void set_port_allocator_flags(uint32_t flags) { + port_allocator_config.flags = flags; + } + + static const int kUndefined = -1; + // Default maximum number of packets in the audio jitter buffer. + static const int kAudioJitterBufferMaxPackets = 200; + // ICE connection receiving timeout for aggressive configuration. + static const int kAggressiveIceConnectionReceivingTimeout = 1000; + + //////////////////////////////////////////////////////////////////////// + // The below few fields mirror the standard RTCConfiguration dictionary: + // https://w3c.github.io/webrtc-pc/#rtcconfiguration-dictionary + //////////////////////////////////////////////////////////////////////// + + // TODO(pthatcher): Rename this ice_servers, but update Chromium + // at the same time. + IceServers servers; + // TODO(pthatcher): Rename this ice_transport_type, but update + // Chromium at the same time. + IceTransportsType type = kAll; + BundlePolicy bundle_policy = kBundlePolicyBalanced; + RtcpMuxPolicy rtcp_mux_policy = kRtcpMuxPolicyRequire; + std::vector> certificates; + int ice_candidate_pool_size = 0; + + ////////////////////////////////////////////////////////////////////////// + // The below fields correspond to constraints from the deprecated + // constraints interface for constructing a PeerConnection. + // + // std::optional fields can be "missing", in which case the implementation + // default will be used. + ////////////////////////////////////////////////////////////////////////// + + // If set to true, don't gather IPv6 ICE candidates on Wi-Fi. + // Only intended to be used on specific devices. Certain phones disable IPv6 + // when the screen is turned off and it would be better to just disable the + // IPv6 ICE candidates on Wi-Fi in those cases. + bool disable_ipv6_on_wifi = false; + + // By default, the PeerConnection will use a limited number of IPv6 network + // interfaces, in order to avoid too many ICE candidate pairs being created + // and delaying ICE completion. + // + // Can be set to INT_MAX to effectively disable the limit. + int max_ipv6_networks = kDefaultMaxIPv6Networks; + + // Exclude link-local network interfaces + // from consideration for gathering ICE candidates. + bool disable_link_local_networks = false; + + // Minimum bitrate at which screencast video tracks will be encoded at. + // This means adding padding bits up to this bitrate, which can help + // when switching from a static scene to one with motion. + std::optional screencast_min_bitrate; + + ///////////////////////////////////////////////// + // The below fields are not part of the standard. + ///////////////////////////////////////////////// + + // Can be used to disable TCP candidate generation. + TcpCandidatePolicy tcp_candidate_policy = kTcpCandidatePolicyEnabled; + + // Can be used to avoid gathering candidates for a "higher cost" network, + // if a lower cost one exists. For example, if both Wi-Fi and cellular + // interfaces are available, this could be used to avoid using the cellular + // interface. + CandidateNetworkPolicy candidate_network_policy = + kCandidateNetworkPolicyAll; + + // The maximum number of packets that can be stored in the NetEq audio + // jitter buffer. Can be reduced to lower tolerated audio latency. + int audio_jitter_buffer_max_packets = kAudioJitterBufferMaxPackets; + + // Whether to use the NetEq "fast mode" which will accelerate audio quicker + // if it falls behind. + bool audio_jitter_buffer_fast_accelerate = false; + + // The minimum delay in milliseconds for the audio jitter buffer. + int audio_jitter_buffer_min_delay_ms = 0; + + // Timeout in milliseconds before an ICE candidate pair is considered to be + // "not receiving", after which a lower priority candidate pair may be + // selected. + int ice_connection_receiving_timeout = kUndefined; + + // Interval in milliseconds at which an ICE "backup" candidate pair will be + // pinged. This is a candidate pair which is not actively in use, but may + // be switched to if the active candidate pair becomes unusable. + // + // This is relevant mainly to Wi-Fi/cell handoff; the application may not + // want this backup cellular candidate pair pinged frequently, since it + // consumes data/battery. + int ice_backup_candidate_pair_ping_interval = kUndefined; + + // Can be used to enable continual gathering, which means new candidates + // will be gathered as network interfaces change. Note that if continual + // gathering is used, the candidate removal API should also be used, to + // avoid an ever-growing list of candidates. + ContinualGatheringPolicy continual_gathering_policy = GATHER_ONCE; + + // If set to true, candidate pairs will be pinged in order of most likely + // to work (which means using a TURN server, generally), rather than in + // standard priority order. + bool prioritize_most_likely_ice_candidate_pairs = false; + + // Implementation defined settings. A public member only for the benefit of + // the implementation. Applications must not access it directly, and should + // instead use provided accessor methods, e.g., set_cpu_adaptation. + struct MediaConfig media_config; + + // If set to true, only one preferred TURN allocation will be used per + // network interface. UDP is preferred over TCP and IPv6 over IPv4. This + // can be used to cut down on the number of candidate pairings. + // Deprecated. TODO(webrtc:11026) Remove this flag once the downstream + // dependency is removed. + bool prune_turn_ports = false; + + // The policy used to prune turn port. + PortPrunePolicy turn_port_prune_policy = NO_PRUNE; + + PortPrunePolicy GetTurnPortPrunePolicy() const { + return prune_turn_ports ? PRUNE_BASED_ON_PRIORITY + : turn_port_prune_policy; + } + + // If set to true, this means the ICE transport should presume TURN-to-TURN + // candidate pairs will succeed, even before a binding response is received. + // This can be used to optimize the initial connection time, since the DTLS + // handshake can begin immediately. + bool presume_writable_when_fully_relayed = false; + + // If true, "renomination" will be added to the ice options in the transport + // description. + // See: https://tools.ietf.org/html/draft-thatcher-ice-renomination-00 + bool enable_ice_renomination = false; + + // If true, the ICE role is re-determined when the PeerConnection sets a + // local transport description that indicates an ICE restart. + // + // This is standard RFC5245 ICE behavior, but causes unnecessary role + // thrashing, so an application may wish to avoid it. This role + // re-determining was removed in ICEbis (ICE v2). + bool redetermine_role_on_ice_restart = true; + + // This flag is only effective when `continual_gathering_policy` is + // GATHER_CONTINUALLY. + // + // If true, after the ICE transport type is changed such that new types of + // ICE candidates are allowed by the new transport type, e.g. from + // IceTransportsType::kRelay to IceTransportsType::kAll, candidates that + // have been gathered by the ICE transport but not matching the previous + // transport type and as a result not observed by PeerConnectionObserver, + // will be surfaced to the observer. + bool surface_ice_candidates_on_ice_transport_type_changed = false; + + // The following fields define intervals in milliseconds at which ICE + // connectivity checks are sent. + // + // We consider ICE is "strongly connected" for an agent when there is at + // least one candidate pair that currently succeeds in connectivity check + // from its direction i.e. sending a STUN ping and receives a STUN ping + // response, AND all candidate pairs have sent a minimum number of pings for + // connectivity (this number is implementation-specific). Otherwise, ICE is + // considered in "weak connectivity". + // + // Note that the above notion of strong and weak connectivity is not defined + // in RFC 5245, and they apply to our current ICE implementation only. + // + // 1) ice_check_interval_strong_connectivity defines the interval applied to + // ALL candidate pairs when ICE is strongly connected, and it overrides the + // default value of this interval in the ICE implementation; + // 2) ice_check_interval_weak_connectivity defines the counterpart for ALL + // pairs when ICE is weakly connected, and it overrides the default value of + // this interval in the ICE implementation; + // 3) ice_check_min_interval defines the minimal interval (equivalently the + // maximum rate) that overrides the above two intervals when either of them + // is less. + std::optional ice_check_interval_strong_connectivity; + std::optional ice_check_interval_weak_connectivity; + std::optional ice_check_min_interval; + + // The min time period for which a candidate pair must wait for response to + // connectivity checks before it becomes unwritable. This parameter + // overrides the default value in the ICE implementation if set. + std::optional ice_unwritable_timeout; + + // The min number of connectivity checks that a candidate pair must sent + // without receiving response before it becomes unwritable. This parameter + // overrides the default value in the ICE implementation if set. + std::optional ice_unwritable_min_checks; + + // The min time period for which a candidate pair must wait for response to + // connectivity checks it becomes inactive. This parameter overrides the + // default value in the ICE implementation if set. + std::optional ice_inactive_timeout; + + // The interval in milliseconds at which STUN candidates will resend STUN + // binding requests to keep NAT bindings open. + std::optional stun_candidate_keepalive_interval; + + // Optional TurnCustomizer. + // With this class one can modify outgoing TURN messages. + // The object passed in must remain valid until PeerConnection::Close() is + // called. + webrtc::TurnCustomizer* turn_customizer = nullptr; + + // Preferred network interface. + // A candidate pair on a preferred network has a higher precedence in ICE + // than one on an un-preferred network, regardless of priority or network + // cost. + std::optional network_preference; + + // Configure the SDP semantics used by this PeerConnection. By default, this + // is Unified Plan which is compliant to the WebRTC 1.0 specification. It is + // possible to overrwite this to the deprecated Plan B SDP format, but note + // that kPlanB will be deleted at some future date, see + // https://crbug.com/webrtc/13528. + // + // kUnifiedPlan will cause the PeerConnection to create offers and answers + // with multiple m= sections where each m= section maps to one RtpSender and + // one RtpReceiver (an RtpTransceiver), either both audio or both video. + // This will also cause the PeerConnection to ignore all but the first + // a=ssrc lines that form a Plan B streams (if the PeerConnection is given + // Plan B SDP to process). + // + // kPlanB will cause the PeerConnection to create offers and answers with at + // most one audio and one video m= section with multiple RtpSenders and + // RtpReceivers specified as multiple a=ssrc lines within the section. This + // will also cause PeerConnection to ignore all but the first m= section of + // the same media type (if the PeerConnection is given Unified Plan SDP to + // process). + SdpSemantics sdp_semantics = SdpSemantics::kUnifiedPlan; + + // TODO(bugs.webrtc.org/9891) - Move to crypto_options or remove. + // Actively reset the SRTP parameters whenever the DTLS transports + // underneath are reset for every offer/answer negotiation. + // This is only intended to be a workaround for crbug.com/835958 + // WARNING: This would cause RTP/RTCP packets decryption failure if not used + // correctly. This flag will be deprecated soon. Do not rely on it. + bool active_reset_srtp_params = false; + + // Defines advanced optional cryptographic settings related to SRTP and + // frame encryption for native WebRTC. Setting this will overwrite any + // settings set in PeerConnectionFactory (which is deprecated). + std::optional crypto_options; + + // Configure if we should include the SDP attribute extmap-allow-mixed in + // our offer on session level. + bool offer_extmap_allow_mixed = true; + + // TURN logging identifier. + // This identifier is added to a TURN allocation + // and it intended to be used to be able to match client side + // logs with TURN server logs. It will not be added if it's an empty string. + std::string turn_logging_id; + + // Added to be able to control rollout of this feature. + bool enable_implicit_rollback = false; + + // The delay before doing a usage histogram report for long-lived + // PeerConnections. Used for testing only. + std::optional report_usage_pattern_delay_ms; + + // The ping interval (ms) when the connection is stable and writable. This + // parameter overrides the default value in the ICE implementation if set. + std::optional stable_writable_connection_ping_interval_ms; + + // Whether this PeerConnection will avoid VPNs (kAvoidVpn), prefer VPNs + // (kPreferVpn), only work over VPN (kOnlyUseVpn) or only work over non-VPN + // (kNeverUseVpn) interfaces. This controls which local interfaces the + // PeerConnection will prefer to connect over. Since VPN detection is not + // perfect, adherence to this preference cannot be guaranteed. + VpnPreference vpn_preference = VpnPreference::kDefault; + + // List of address/length subnets that should be treated like + // VPN (in case webrtc fails to auto detect them). + std::vector vpn_list; + + PortAllocatorConfig port_allocator_config; + + // The burst interval of the pacer, see TaskQueuePacedSender constructor. + std::optional pacer_burst_interval; + + // When this flag is set, ports not bound to any specific network interface + // will be used, in addition to normal ports bound to the enumerated + // interfaces. Without this flag, these "any address" ports would only be + // used when network enumeration fails or is disabled. But under certain + // conditions, these ports may succeed where others fail, so they may allow + // the application to work in a wider variety of environments, at the expense + // of having to allocate additional candidates. + bool enable_any_address_ports = false; + + // + // Don't forget to update operator== if adding something. + // + }; + + // See: https://www.w3.org/TR/webrtc/#idl-def-rtcofferansweroptions + struct RTCOfferAnswerOptions { + static const int kUndefined = -1; + static const int kMaxOfferToReceiveMedia = 1; + + // The default value for constraint offerToReceiveX:true. + static const int kOfferToReceiveMediaTrue = 1; + + // These options are left as backwards compatibility for clients who need + // "Plan B" semantics. Clients who have switched to "Unified Plan" semantics + // should use the RtpTransceiver API (AddTransceiver) instead. + // + // offer_to_receive_X set to 1 will cause a media description to be + // generated in the offer, even if no tracks of that type have been added. + // Values greater than 1 are treated the same. + // + // If set to 0, the generated directional attribute will not include the + // "recv" direction (meaning it will be "sendonly" or "inactive". + int offer_to_receive_video = kUndefined; + int offer_to_receive_audio = kUndefined; + + bool voice_activity_detection = true; + bool ice_restart = false; + + // If true, will offer to BUNDLE audio/video/data together. Not to be + // confused with RTCP mux (multiplexing RTP and RTCP together). + bool use_rtp_mux = true; + + // If true, "a=packetization: raw" attribute will be offered + // in the SDP for all video payload and accepted in the answer if offered. + bool raw_packetization_for_video = false; + + // This will apply to all video tracks with a Plan B SDP offer/answer. + int num_simulcast_layers = 1; + + // If true: Use SDP format from draft-ietf-mmusic-scdp-sdp-03 + // If false: Use SDP format from draft-ietf-mmusic-sdp-sdp-26 or later + bool use_obsolete_sctp_sdp = false; + + RTCOfferAnswerOptions() = default; + + RTCOfferAnswerOptions(int offer_to_receive_video, + int offer_to_receive_audio, + bool voice_activity_detection, + bool ice_restart, + bool use_rtp_mux) + : offer_to_receive_video(offer_to_receive_video), + offer_to_receive_audio(offer_to_receive_audio), + voice_activity_detection(voice_activity_detection), + ice_restart(ice_restart), + use_rtp_mux(use_rtp_mux) {} + }; + + // Used by GetStats to decide which stats to include in the stats reports. + // `kStatsOutputLevelStandard` includes the standard stats for Javascript API; + // `kStatsOutputLevelDebug` includes both the standard stats and additional + // stats for debugging purposes. + enum StatsOutputLevel { + kStatsOutputLevelStandard, + kStatsOutputLevelDebug, + }; + + // Accessor methods to active local streams. + // This method is not supported with kUnifiedPlan semantics. Please use + // GetSenders() instead. + virtual scoped_refptr local_streams() = 0; + + // Accessor methods to remote streams. + // This method is not supported with kUnifiedPlan semantics. Please use + // GetReceivers() instead. + virtual scoped_refptr remote_streams() = 0; + + // Add a new MediaStream to be sent on this PeerConnection. + // Note that a SessionDescription negotiation is needed before the + // remote peer can receive the stream. + // + // This has been removed from the standard in favor of a track-based API. So, + // this is equivalent to simply calling AddTrack for each track within the + // stream, with the one difference that if "stream->AddTrack(...)" is called + // later, the PeerConnection will automatically pick up the new track. Though + // this functionality will be deprecated in the future. + // + // This method is not supported with kUnifiedPlan semantics. Please use + // AddTrack instead. + virtual bool AddStream(MediaStreamInterface* stream) = 0; + + // Remove a MediaStream from this PeerConnection. + // Note that a SessionDescription negotiation is needed before the + // remote peer is notified. + // + // This method is not supported with kUnifiedPlan semantics. Please use + // RemoveTrack instead. + virtual void RemoveStream(MediaStreamInterface* stream) = 0; + + // Add a new MediaStreamTrack to be sent on this PeerConnection, and return + // the newly created RtpSender. The RtpSender will be associated with the + // streams specified in the `stream_ids` list. + // + // Errors: + // - INVALID_PARAMETER: `track` is null, has a kind other than audio or video, + // or a sender already exists for the track. + // - INVALID_STATE: The PeerConnection is closed. + virtual RTCErrorOr> AddTrack( + scoped_refptr track, + const std::vector& stream_ids) = 0; + + // Add a new MediaStreamTrack as above, but with an additional parameter, + // `init_send_encodings` : initial RtpEncodingParameters for RtpSender, + // similar to init_send_encodings in RtpTransceiverInit. + // Note that a new transceiver will always be created. + // + virtual RTCErrorOr> AddTrack( + scoped_refptr track, + const std::vector& stream_ids, + const std::vector& init_send_encodings) = 0; + + // Removes the connection between a MediaStreamTrack and the PeerConnection. + // Stops sending on the RtpSender and marks the + // corresponding RtpTransceiver direction as no longer sending. + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-removetrack + // + // Errors: + // - INVALID_PARAMETER: `sender` is null or (Plan B only) the sender is not + // associated with this PeerConnection. + // - INVALID_STATE: PeerConnection is closed. + // + // Plan B semantics: Removes the RtpSender from this PeerConnection. + // + // TODO(bugs.webrtc.org/9534): Rename to RemoveTrack once the other signature + // is removed; remove default implementation once upstream is updated. + virtual RTCError RemoveTrackOrError( + scoped_refptr /* sender */) { + RTC_CHECK_NOTREACHED(); + return RTCError(); + } + + // AddTransceiver creates a new RtpTransceiver and adds it to the set of + // transceivers. Adding a transceiver will cause future calls to CreateOffer + // to add a media description for the corresponding transceiver. + // + // The initial value of `mid` in the returned transceiver is null. Setting a + // new session description may change it to a non-null value. + // + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver + // + // Optionally, an RtpTransceiverInit structure can be specified to configure + // the transceiver from construction. If not specified, the transceiver will + // default to having a direction of kSendRecv and not be part of any streams. + // + // These methods are only available when Unified Plan is enabled (see + // RTCConfiguration). + // + // Common errors: + // - INTERNAL_ERROR: The configuration does not have Unified Plan enabled. + + // Adds a transceiver with a sender set to transmit the given track. The kind + // of the transceiver (and sender/receiver) will be derived from the kind of + // the track. + // Errors: + // - INVALID_PARAMETER: `track` is null. + virtual RTCErrorOr> AddTransceiver( + scoped_refptr track) = 0; + virtual RTCErrorOr> AddTransceiver( + scoped_refptr track, + const RtpTransceiverInit& init) = 0; + + // Adds a transceiver with the given kind. Can either be + // webrtc::MediaType::AUDIO or webrtc::MediaType::VIDEO. Errors: + // - INVALID_PARAMETER: `media_type` is not webrtc::MediaType::AUDIO or + // webrtc::MediaType::VIDEO. + virtual RTCErrorOr> AddTransceiver( + webrtc::MediaType media_type) = 0; + virtual RTCErrorOr> AddTransceiver( + webrtc::MediaType media_type, + const RtpTransceiverInit& init) = 0; + + // Creates a sender without a track. Can be used for "early media"/"warmup" + // use cases, where the application may want to negotiate video attributes + // before a track is available to send. + // + // The standard way to do this would be through "addTransceiver", but we + // don't support that API yet. + // + // `kind` must be "audio" or "video". + // + // `stream_id` is used to populate the msid attribute; if empty, one will + // be generated automatically. + // + // This method is not supported with kUnifiedPlan semantics. Please use + // AddTransceiver instead. + virtual scoped_refptr CreateSender( + const std::string& kind, + const std::string& stream_id) = 0; + + // If Plan B semantics are specified, gets all RtpSenders, created either + // through AddStream, AddTrack, or CreateSender. All senders of a specific + // media type share the same media description. + // + // If Unified Plan semantics are specified, gets the RtpSender for each + // RtpTransceiver. + virtual std::vector> GetSenders() const = 0; + + // If Plan B semantics are specified, gets all RtpReceivers created when a + // remote description is applied. All receivers of a specific media type share + // the same media description. It is also possible to have a media description + // with no associated RtpReceivers, if the directional attribute does not + // indicate that the remote peer is sending any media. + // + // If Unified Plan semantics are specified, gets the RtpReceiver for each + // RtpTransceiver. + virtual std::vector> GetReceivers() + const = 0; + + // Get all RtpTransceivers, created either through AddTransceiver, AddTrack or + // by a remote description applied with SetRemoteDescription. + // + // Note: This method is only available when Unified Plan is enabled (see + // RTCConfiguration). + virtual std::vector> GetTransceivers() + const = 0; + + // The legacy non-compliant GetStats() API. This correspond to the + // callback-based version of getStats() in JavaScript. The returned metrics + // are UNDOCUMENTED and many of them rely on implementation-specific details. + // The goal is to DELETE THIS VERSION but we can't today because it is heavily + // relied upon by third parties. See https://crbug.com/822696. + // + // This version is wired up into Chrome. Any stats implemented are + // automatically exposed to the Web Platform. This has BYPASSED the Chrome + // release processes for years and lead to cross-browser incompatibility + // issues and web application reliance on Chrome-only behavior. + // + // This API is in "maintenance mode", serious regressions should be fixed but + // adding new stats is highly discouraged. + // + // TODO(hbos): Deprecate and remove this when third parties have migrated to + // the spec-compliant GetStats() API. https://crbug.com/822696 + virtual bool GetStats(StatsObserver* observer, + MediaStreamTrackInterface* track, // Optional + StatsOutputLevel level) = 0; + // The spec-compliant GetStats() API. This correspond to the promise-based + // version of getStats() in JavaScript. Implementation status is described in + // api/stats/rtcstats_objects.h. For more details on stats, see spec: + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-getstats + // TODO(hbos): Takes shared ownership, use webrtc::scoped_refptr<> instead. + // This requires stop overriding the current version in third party or making + // third party calls explicit to avoid ambiguity during switch. Make the + // future version abstract as soon as third party projects implement it. + virtual void GetStats(RTCStatsCollectorCallback* callback) = 0; + // Spec-compliant getStats() performing the stats selection algorithm with the + // sender. https://w3c.github.io/webrtc-pc/#dom-rtcrtpsender-getstats + virtual void GetStats(scoped_refptr selector, + scoped_refptr callback) = 0; + // Spec-compliant getStats() performing the stats selection algorithm with the + // receiver. https://w3c.github.io/webrtc-pc/#dom-rtcrtpreceiver-getstats + virtual void GetStats(scoped_refptr selector, + scoped_refptr callback) = 0; + // Clear cached stats in the RTCStatsCollector. + virtual void ClearStatsCache() {} + + // Create a data channel with the provided config, or default config if none + // is provided. Note that an offer/answer negotiation is still necessary + // before the data channel can be used. + // + // Also, calling CreateDataChannel is the only way to get a data "m=" section + // in SDP, so it should be done before CreateOffer is called, if the + // application plans to use data channels. + virtual RTCErrorOr> + CreateDataChannelOrError(const std::string& /* label */, + const DataChannelInit* /* config */) { + return RTCError(RTCErrorType::INTERNAL_ERROR, "dummy function called"); + } + // TODO(crbug.com/788659): Remove "virtual" below and default implementation + // above once mock in Chrome is fixed. + ABSL_DEPRECATED("Use CreateDataChannelOrError") + virtual scoped_refptr CreateDataChannel( + const std::string& label, + const DataChannelInit* config) { + auto result = CreateDataChannelOrError(label, config); + if (!result.ok()) { + return nullptr; + } else { + return result.MoveValue(); + } + } + + // NOTE: For the following 6 methods, it's only safe to dereference the + // SessionDescriptionInterface on signaling_thread() (for example, calling + // ToString). + + // Returns the more recently applied description; "pending" if it exists, and + // otherwise "current". See below. + virtual const SessionDescriptionInterface* local_description() const = 0; + virtual const SessionDescriptionInterface* remote_description() const = 0; + + // A "current" description the one currently negotiated from a complete + // offer/answer exchange. + virtual const SessionDescriptionInterface* current_local_description() + const = 0; + virtual const SessionDescriptionInterface* current_remote_description() + const = 0; + + // A "pending" description is one that's part of an incomplete offer/answer + // exchange (thus, either an offer or a pranswer). Once the offer/answer + // exchange is finished, the "pending" description will become "current". + virtual const SessionDescriptionInterface* pending_local_description() + const = 0; + virtual const SessionDescriptionInterface* pending_remote_description() + const = 0; + + // Tells the PeerConnection that ICE should be restarted. This triggers a need + // for negotiation and subsequent CreateOffer() calls will act as if + // RTCOfferAnswerOptions::ice_restart is true. + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-restartice + virtual void RestartIce() = 0; + + // Create a new offer. + // The CreateSessionDescriptionObserver callback will be called when done. + virtual void CreateOffer(CreateSessionDescriptionObserver* observer, + const RTCOfferAnswerOptions& options) = 0; + + // Create an answer to an offer. + // The CreateSessionDescriptionObserver callback will be called when done. + virtual void CreateAnswer(CreateSessionDescriptionObserver* observer, + const RTCOfferAnswerOptions& options) = 0; + + // Sets the local session description. + // + // According to spec, the local session description MUST be the same as was + // returned by CreateOffer() or CreateAnswer() or else the operation should + // fail. Our implementation however allows some amount of "SDP munging", but + // please note that this is HIGHLY DISCOURAGED. If you do not intent to munge + // SDP, the method below that doesn't take `desc` as an argument will create + // the offer or answer for you. + // + // The observer is invoked as soon as the operation completes, which could be + // before or after the SetLocalDescription() method has exited. + virtual void SetLocalDescription( + std::unique_ptr /* desc */, + scoped_refptr /* observer */) {} + // Creates an offer or answer (depending on current signaling state) and sets + // it as the local session description. + // + // The observer is invoked as soon as the operation completes, which could be + // before or after the SetLocalDescription() method has exited. + virtual void SetLocalDescription( + scoped_refptr /* observer */) {} + // Like SetLocalDescription() above, but the observer is invoked with a delay + // after the operation completes. This helps avoid recursive calls by the + // observer but also makes it possible for states to change in-between the + // operation completing and the observer getting called. This makes them racy + // for synchronizing peer connection states to the application. + // TODO(https://crbug.com/webrtc/11798): Delete these methods in favor of the + // ones taking SetLocalDescriptionObserverInterface as argument. + virtual void SetLocalDescription(SetSessionDescriptionObserver* observer, + SessionDescriptionInterface* desc) = 0; + virtual void SetLocalDescription( + SetSessionDescriptionObserver* /* observer */) {} + + // Sets the remote session description. + // + // (Unlike "SDP munging" before SetLocalDescription(), modifying a remote + // offer or answer is allowed by the spec.) + // + // The observer is invoked as soon as the operation completes, which could be + // before or after the SetRemoteDescription() method has exited. + virtual void SetRemoteDescription( + std::unique_ptr desc, + scoped_refptr observer) = 0; + // Like SetRemoteDescription() above, but the observer is invoked with a delay + // after the operation completes. This helps avoid recursive calls by the + // observer but also makes it possible for states to change in-between the + // operation completing and the observer getting called. This makes them racy + // for synchronizing peer connection states to the application. + // TODO(https://crbug.com/webrtc/11798): Delete this method in favor of the + // ones taking SetRemoteDescriptionObserverInterface as argument. + virtual void SetRemoteDescription( + SetSessionDescriptionObserver* /* observer */, + SessionDescriptionInterface* /* desc */) {} + + // According to spec, we must only fire "negotiationneeded" if the Operations + // Chain is empty. This method takes care of validating an event previously + // generated with PeerConnectionObserver::OnNegotiationNeededEvent() to make + // sure that even if there was a delay (e.g. due to a PostTask) between the + // event being generated and the time of firing, the Operations Chain is empty + // and the event is still valid to be fired. + virtual bool ShouldFireNegotiationNeededEvent(uint32_t event_id) = 0; + + virtual PeerConnectionInterface::RTCConfiguration GetConfiguration() = 0; + + // Sets the PeerConnection's global configuration to `config`. + // + // The members of `config` that may be changed are `type`, `servers`, + // `ice_candidate_pool_size` and `prune_turn_ports` (though the candidate + // pool size can't be changed after the first call to SetLocalDescription). + // Note that this means the BUNDLE and RTCP-multiplexing policies cannot be + // changed with this method. + // + // Any changes to STUN/TURN servers or ICE candidate policy will affect the + // next gathering phase, and cause the next call to createOffer to generate + // new ICE credentials, as described in JSEP. This also occurs when + // `prune_turn_ports` changes, for the same reasoning. + // + // If an error occurs, returns false and populates `error` if non-null: + // - INVALID_MODIFICATION if `config` contains a modified parameter other + // than one of the parameters listed above. + // - INVALID_RANGE if `ice_candidate_pool_size` is out of range. + // - SYNTAX_ERROR if parsing an ICE server URL failed. + // - INVALID_PARAMETER if a TURN server is missing `username` or `password`. + // - INTERNAL_ERROR if an unexpected error occurred. + virtual RTCError SetConfiguration( + const PeerConnectionInterface::RTCConfiguration& config) = 0; + + // Provides a remote candidate to the ICE Agent. + // A copy of the `candidate` will be created and added to the remote + // description. So the caller of this method still has the ownership of the + // `candidate`. + // TODO(hbos): The spec mandates chaining this operation onto the operations + // chain; deprecate and remove this version in favor of the callback-based + // signature. + virtual bool AddIceCandidate(const IceCandidateInterface* candidate) = 0; + // TODO(hbos): Remove default implementation once implemented by downstream + // projects. + virtual void AddIceCandidate( + std::unique_ptr /* candidate */, + std::function /* callback */) {} + + // Removes a group of remote candidates from the ICE agent. Needed mainly for + // continual gathering, to avoid an ever-growing list of candidates as + // networks come and go. Note that the candidates' transport_name must be set + // to the MID of the m= section that generated the candidate. + // TODO(bugs.webrtc.org/8395): Use IceCandidateInterface instead of + // webrtc::Candidate, which would avoid the transport_name oddity. + virtual bool RemoveIceCandidates( + const std::vector& candidates) = 0; + + // SetBitrate limits the bandwidth allocated for all RTP streams sent by + // this PeerConnection. Other limitations might affect these limits and + // are respected (for example "b=AS" in SDP). + // + // Setting `current_bitrate_bps` will reset the current bitrate estimate + // to the provided value. + virtual RTCError SetBitrate(const BitrateSettings& bitrate) = 0; + + // Allows an application to reconfigure bandwidth estimation. + // The method can be called both before and after estimation has started. + // Estimation starts when the first RTP packet is sent. + // Estimation will be restarted if already started. + virtual void ReconfigureBandwidthEstimation( + const BandwidthEstimationSettings& settings) = 0; + + // Enable/disable playout of received audio streams. Enabled by default. Note + // that even if playout is enabled, streams will only be played out if the + // appropriate SDP is also applied. Setting `playout` to false will stop + // playout of the underlying audio device but starts a task which will poll + // for audio data every 10ms to ensure that audio processing happens and the + // audio statistics are updated. + virtual void SetAudioPlayout(bool playout) = 0; + + // Enable/disable recording of transmitted audio streams. Enabled by default. + // Note that even if recording is enabled, streams will only be recorded if + // the appropriate SDP is also applied. + virtual void SetAudioRecording(bool recording) = 0; + + // Looks up the DtlsTransport associated with a MID value. + // In the Javascript API, DtlsTransport is a property of a sender, but + // because the PeerConnection owns the DtlsTransport in this implementation, + // it is better to look them up on the PeerConnection. + virtual scoped_refptr LookupDtlsTransportByMid( + const std::string& mid) = 0; + + // Returns the SCTP transport, if any. + virtual scoped_refptr GetSctpTransport() const = 0; + + // Returns the current SignalingState. + virtual SignalingState signaling_state() = 0; + + // Returns an aggregate state of all ICE *and* DTLS transports. + // This is left in place to avoid breaking native clients who expect our old, + // nonstandard behavior. + // TODO(jonasolsson): deprecate and remove this. + virtual IceConnectionState ice_connection_state() = 0; + + // Returns an aggregated state of all ICE transports. + virtual IceConnectionState standardized_ice_connection_state() = 0; + + // Returns an aggregated state of all ICE and DTLS transports. + virtual PeerConnectionState peer_connection_state() = 0; + + virtual IceGatheringState ice_gathering_state() = 0; + + // Returns the current state of canTrickleIceCandidates per + // https://w3c.github.io/webrtc-pc/#attributes-1 + virtual std::optional can_trickle_ice_candidates() = 0; + + // When a resource is overused, the PeerConnection will try to reduce the load + // on the sysem, for example by reducing the resolution or frame rate of + // encoded streams. The Resource API allows injecting platform-specific usage + // measurements. The conditions to trigger kOveruse or kUnderuse are up to the + // implementation. + virtual void AddAdaptationResource(scoped_refptr resource) = 0; + + // Start RtcEventLog using an existing output-sink. Takes ownership of + // `output` and passes it on to Call, which will take the ownership. If + // the operation fails the output will be closed and deallocated. The + // event log will send serialized events to the output object every + // `output_period_ms`. Applications using the event log should generally + // make their own trade-off regarding the output period. A long period is + // generally more efficient, with potential drawbacks being more bursty + // thread usage, and more events lost in case the application crashes. If + // the `output_period_ms` argument is omitted, webrtc selects a default + // deemed to be workable in most cases. + virtual bool StartRtcEventLog(std::unique_ptr output, + int64_t output_period_ms) = 0; + virtual bool StartRtcEventLog(std::unique_ptr output) = 0; + + // Stops logging the RtcEventLog. + virtual void StopRtcEventLog() = 0; + + virtual void SetDataChannelEventObserver( + std::unique_ptr observer) = 0; + + // Terminates all media, closes the transports, and in general releases any + // resources used by the PeerConnection. This is an irreversible operation. + // + // Note that after this method completes, the PeerConnection will no longer + // use the PeerConnectionObserver interface passed in on construction, and + // thus the observer object can be safely destroyed. + virtual void Close() = 0; + + // The thread on which all PeerConnectionObserver callbacks will be invoked, + // as well as callbacks for other classes such as DataChannelObserver. + // + // Also the only thread on which it's safe to use SessionDescriptionInterface + // pointers. + virtual Thread* signaling_thread() const = 0; + + // NetworkController instance being used by this PeerConnection, to be used + // to identify instances when using a custom NetworkControllerFactory. + virtual NetworkControllerInterface* GetNetworkController() = 0; + + protected: + // Dtor protected as objects shouldn't be deleted via this interface. + ~PeerConnectionInterface() override = default; +}; + +// PeerConnection callback interface, used for RTCPeerConnection events. +// Application should implement these methods. +class PeerConnectionObserver { + public: + virtual ~PeerConnectionObserver() = default; + + // Triggered when the SignalingState changed. + virtual void OnSignalingChange( + PeerConnectionInterface::SignalingState new_state) = 0; + + // Triggered when media is received on a new stream from remote peer. + virtual void OnAddStream(scoped_refptr /* stream */) {} + + // Triggered when a remote peer closes a stream. + virtual void OnRemoveStream( + scoped_refptr /* stream */) {} + + // Triggered when a remote peer opens a data channel. + virtual void OnDataChannel( + scoped_refptr data_channel) = 0; + + // Triggered when renegotiation is needed. For example, an ICE restart + // has begun. + // TODO(hbos): Delete in favor of OnNegotiationNeededEvent() when downstream + // projects have migrated. + virtual void OnRenegotiationNeeded() {} + // Used to fire spec-compliant onnegotiationneeded events, which should only + // fire when the Operations Chain is empty. The observer is responsible for + // queuing a task (e.g. Chromium: jump to main thread) to maybe fire the + // event. The event identified using `event_id` must only fire if + // PeerConnection::ShouldFireNegotiationNeededEvent() returns true since it is + // possible for the event to become invalidated by operations subsequently + // chained. + virtual void OnNegotiationNeededEvent(uint32_t /* event_id */) {} + + // Called any time the legacy IceConnectionState changes. + // + // Note that our ICE states lag behind the standard slightly. The most + // notable differences include the fact that "failed" occurs after 15 + // seconds, not 30, and this actually represents a combination ICE + DTLS + // state, so it may be "failed" if DTLS fails while ICE succeeds. + // + // TODO(jonasolsson): deprecate and remove this. + virtual void OnIceConnectionChange( + PeerConnectionInterface::IceConnectionState /* new_state */) {} + + // Called any time the standards-compliant IceConnectionState changes. + virtual void OnStandardizedIceConnectionChange( + PeerConnectionInterface::IceConnectionState /* new_state */) {} + + // Called any time the PeerConnectionState changes. + virtual void OnConnectionChange( + PeerConnectionInterface::PeerConnectionState /* new_state */) {} + + // Called any time the IceGatheringState changes. + virtual void OnIceGatheringChange( + PeerConnectionInterface::IceGatheringState new_state) = 0; + + // A new ICE candidate has been gathered. + virtual void OnIceCandidate(const IceCandidateInterface* candidate) = 0; + + // Gathering of an ICE candidate failed. + // See https://w3c.github.io/webrtc-pc/#event-icecandidateerror + virtual void OnIceCandidateError(const std::string& /* address */, + int /* port */, + const std::string& /* url */, + int /* error_code */, + const std::string& /* error_text */) {} + + // Ice candidates have been removed. + // TODO(honghaiz): Make this a pure virtual method when all its subclasses + // implement it. + virtual void OnIceCandidatesRemoved( + const std::vector& /* candidates */) {} + + // Called when the ICE connection receiving status changes. + virtual void OnIceConnectionReceivingChange(bool /* receiving */) {} + + // Called when the selected candidate pair for the ICE connection changes. + virtual void OnIceSelectedCandidatePairChanged( + const CandidatePairChangeEvent& /* event */) {} + + // This is called when a receiver and its track are created. + // TODO(zhihuang): Make this pure virtual when all subclasses implement it. + // Note: This is called with both Plan B and Unified Plan semantics. Unified + // Plan users should prefer OnTrack, OnAddTrack is only called as backwards + // compatibility (and is called in the exact same situations as OnTrack). + virtual void OnAddTrack( + scoped_refptr /* receiver */, + const std::vector>& /* streams */) {} + + // This is called when signaling indicates a transceiver will be receiving + // media from the remote endpoint. This is fired during a call to + // SetRemoteDescription. The receiving track can be accessed by: + // `transceiver->receiver()->track()` and its associated streams by + // `transceiver->receiver()->streams()`. + // Note: This will only be called if Unified Plan semantics are specified. + // This behavior is specified in section 2.2.8.2.5 of the "Set the + // RTCSessionDescription" algorithm: + // https://w3c.github.io/webrtc-pc/#set-description + virtual void OnTrack( + scoped_refptr /* transceiver */) {} + + // Called when signaling indicates that media will no longer be received on a + // track. + // With Plan B semantics, the given receiver will have been removed from the + // PeerConnection and the track muted. + // With Unified Plan semantics, the receiver will remain but the transceiver + // will have changed direction to either sendonly or inactive. + // https://w3c.github.io/webrtc-pc/#process-remote-track-removal + // TODO(hbos,deadbeef): Make pure virtual when all subclasses implement it. + virtual void OnRemoveTrack( + scoped_refptr /* receiver */) {} + + // Called when an interesting usage is detected by WebRTC. + // An appropriate action is to add information about the context of the + // PeerConnection and write the event to some kind of "interesting events" + // log function. + // The heuristics for defining what constitutes "interesting" are + // implementation-defined. + virtual void OnInterestingUsage(int /* usage_pattern */) {} +}; + +// PeerConnectionDependencies holds all of PeerConnections dependencies. +// A dependency is distinct from a configuration as it defines significant +// executable code that can be provided by a user of the API. +// +// All new dependencies should be added as a unique_ptr to allow the +// PeerConnection object to be the definitive owner of the dependencies +// lifetime making injection safer. +struct RTC_EXPORT PeerConnectionDependencies final { + explicit PeerConnectionDependencies(PeerConnectionObserver* observer_in); + // This object is not copyable or assignable. + PeerConnectionDependencies(const PeerConnectionDependencies&) = delete; + PeerConnectionDependencies& operator=(const PeerConnectionDependencies&) = + delete; + // This object is only moveable. + PeerConnectionDependencies(PeerConnectionDependencies&&); + PeerConnectionDependencies& operator=(PeerConnectionDependencies&&) = default; + ~PeerConnectionDependencies(); + // Mandatory dependencies + PeerConnectionObserver* observer = nullptr; + // Optional dependencies + // TODO(bugs.webrtc.org/7447): remove port allocator once downstream is + // updated. The recommended way to inject networking components is to pass a + // PacketSocketFactory when creating the PeerConnectionFactory. + std::unique_ptr allocator; + // Factory for creating resolvers that look up hostnames in DNS + std::unique_ptr + async_dns_resolver_factory; + std::unique_ptr ice_transport_factory; + std::unique_ptr cert_generator; + std::unique_ptr tls_cert_verifier; + std::unique_ptr + video_bitrate_allocator_factory; + // Optional network controller factory to use. + // Overrides that set in PeerConnectionFactoryDependencies. + std::unique_ptr network_controller_factory; + + // Optional field trials to use. + // Overrides those from PeerConnectionFactoryDependencies. + std::unique_ptr trials; +}; + +// PeerConnectionFactoryDependencies holds all of the PeerConnectionFactory +// dependencies. All new dependencies should be added here instead of +// overloading the function. This simplifies dependency injection and makes it +// clear which are mandatory and optional. If possible please allow the peer +// connection factory to take ownership of the dependency by adding a unique_ptr +// to this structure. +struct RTC_EXPORT PeerConnectionFactoryDependencies final { + PeerConnectionFactoryDependencies(); + // This object is not copyable or assignable. + PeerConnectionFactoryDependencies(const PeerConnectionFactoryDependencies&) = + delete; + PeerConnectionFactoryDependencies& operator=( + const PeerConnectionFactoryDependencies&) = delete; + // This object is only moveable. + PeerConnectionFactoryDependencies(PeerConnectionFactoryDependencies&&); + PeerConnectionFactoryDependencies& operator=( + PeerConnectionFactoryDependencies&&) = default; + ~PeerConnectionFactoryDependencies(); + + // Optional dependencies + Thread* network_thread = nullptr; + Thread* worker_thread = nullptr; + Thread* signaling_thread = nullptr; + SocketFactory* socket_factory = nullptr; + // The `packet_socket_factory` will only be used if CreatePeerConnection is + // called without a `port_allocator`. + std::unique_ptr packet_socket_factory; + std::unique_ptr task_queue_factory; + std::unique_ptr event_log_factory; + std::unique_ptr fec_controller_factory; + std::unique_ptr + network_state_predictor_factory; + std::unique_ptr network_controller_factory; + // The `network_manager` will only be used if CreatePeerConnection is called + // without a `port_allocator`, causing the default allocator and network + // manager to be used. + std::unique_ptr network_manager; + // The `network_monitor_factory` will only be used if CreatePeerConnection is + // called without a `port_allocator`, and the above `network_manager' is null. + std::unique_ptr network_monitor_factory; + std::unique_ptr neteq_factory; + std::unique_ptr sctp_factory; + std::unique_ptr trials; + std::unique_ptr + transport_controller_send_factory; + // Metronome used for decoding, must be called on the worker thread. + std::unique_ptr decode_metronome; + // Metronome used for encoding, must be called on the worker thread. + // TODO(b/304158952): Consider merging into a single metronome for all codec + // usage. + std::unique_ptr encode_metronome; + + // Media specific dependencies. Unused when `media_factory == nullptr`. + scoped_refptr adm; + scoped_refptr audio_encoder_factory; + scoped_refptr audio_decoder_factory; + scoped_refptr audio_mixer; + // TODO: bugs.webrtc.org/369904700 - Delete `audio_processing` in favor + // of `audio_processing_builder`. + [[deprecated]] scoped_refptr audio_processing; + std::unique_ptr audio_processing_builder; + std::unique_ptr audio_frame_processor; + std::unique_ptr video_encoder_factory; + std::unique_ptr video_decoder_factory; + + // The `media_factory` members allows webrtc to be optionally built without + // media support (i.e., if only being used for data channels). + // By default media is disabled. To enable media call + // `EnableMedia(PeerConnectionFactoryDependencies&)`. Definition of the + // `MediaFactory` interface is a webrtc implementation detail. + std::unique_ptr media_factory; +}; + +// PeerConnectionFactoryInterface is the factory interface used for creating +// PeerConnection, MediaStream and MediaStreamTrack objects. +// +// The simplest method for obtaiing one, CreatePeerConnectionFactory will +// create the required libjingle threads, socket and network manager factory +// classes for networking if none are provided, though it requires that the +// application runs a message loop on the thread that called the method (see +// explanation below) +// +// If an application decides to provide its own threads and/or implementation +// of networking classes, it should use the alternate +// CreatePeerConnectionFactory method which accepts threads as input, and use +// the CreatePeerConnection version that takes a PortAllocator as an argument. +class RTC_EXPORT PeerConnectionFactoryInterface + : public webrtc::RefCountInterface { + public: + class Options { + public: + Options() {} + + // If set to true, created PeerConnections won't enforce any SRTP + // requirement, allowing unsecured media. Should only be used for + // testing/debugging. + bool disable_encryption = false; + + // If set to true, any platform-supported network monitoring capability + // won't be used, and instead networks will only be updated via polling. + // + // This only has an effect if a PeerConnection is created with the default + // PortAllocator implementation. + bool disable_network_monitor = false; + + // Sets the network types to ignore. For instance, calling this with + // ADAPTER_TYPE_ETHERNET | ADAPTER_TYPE_LOOPBACK will ignore Ethernet and + // loopback interfaces. + int network_ignore_mask = kDefaultNetworkIgnoreMask; + + // Sets the maximum supported protocol version. The highest version + // supported by both ends will be used for the connection, i.e. if one + // party supports DTLS 1.0 and the other DTLS 1.2, DTLS 1.0 will be used. + SSLProtocolVersion ssl_max_version = SSL_PROTOCOL_DTLS_12; + + // Sets crypto related options, e.g. enabled cipher suites. + CryptoOptions crypto_options = {}; + }; + + // Set the options to be used for subsequently created PeerConnections. + virtual void SetOptions(const Options& options) = 0; + + // The preferred way to create a new peer connection. Simply provide the + // configuration and a PeerConnectionDependencies structure. + virtual RTCErrorOr> + CreatePeerConnectionOrError( + const PeerConnectionInterface::RTCConfiguration& configuration, + PeerConnectionDependencies dependencies) = 0; + + // Returns the capabilities of an RTP sender of type `kind`. + // If for some reason you pass in webrtc::MediaType::DATA, returns an empty + // structure. + virtual RtpCapabilities GetRtpSenderCapabilities( + webrtc::MediaType kind) const = 0; + + // Returns the capabilities of an RTP receiver of type `kind`. + // If for some reason you pass in webrtc::MediaType::DATA, returns an empty + // structure. + virtual RtpCapabilities GetRtpReceiverCapabilities( + webrtc::MediaType kind) const = 0; + + virtual scoped_refptr CreateLocalMediaStream( + const std::string& stream_id) = 0; + + // Creates an AudioSourceInterface. + // `options` decides audio processing settings. + virtual scoped_refptr CreateAudioSource( + const AudioOptions& options) = 0; + + // Creates a new local VideoTrack. The same `source` can be used in several + // tracks. + virtual scoped_refptr CreateVideoTrack( + scoped_refptr source, + absl::string_view label) = 0; + ABSL_DEPRECATED("Use version with scoped_refptr") + virtual scoped_refptr CreateVideoTrack( + const std::string& label, + VideoTrackSourceInterface* source) { + return CreateVideoTrack(scoped_refptr(source), + label); + } + + // Creates an new AudioTrack. At the moment `source` can be null. + virtual scoped_refptr CreateAudioTrack( + const std::string& label, + AudioSourceInterface* source) = 0; + + // Starts AEC dump using existing file. Takes ownership of `file` and passes + // it on to VoiceEngine (via other objects) immediately, which will take + // the ownerhip. If the operation fails, the file will be closed. + // A maximum file size in bytes can be specified. When the file size limit is + // reached, logging is stopped automatically. If max_size_bytes is set to a + // value <= 0, no limit will be used, and logging will continue until the + // StopAecDump function is called. + // TODO(webrtc:6463): Delete default implementation when downstream mocks + // classes are updated. + virtual bool StartAecDump(FILE* /* file */, int64_t /* max_size_bytes */) { + return false; + } + + // Stops logging the AEC dump. + virtual void StopAecDump() = 0; + + protected: + // Dtor and ctor protected as objects shouldn't be created or deleted via + // this interface. + PeerConnectionFactoryInterface() {} + ~PeerConnectionFactoryInterface() override = default; +}; + +// CreateModularPeerConnectionFactory is implemented in the "peerconnection" +// build target, which doesn't pull in the implementations of every module +// webrtc may use. +// +// If an application knows it will only require certain modules, it can reduce +// webrtc's impact on its binary size by depending only on the "peerconnection" +// target and the modules the application requires, using +// CreateModularPeerConnectionFactory. For example, if an application +// only uses WebRTC for audio, it can pass in null pointers for the +// video-specific interfaces, and omit the corresponding modules from its +// build. +// +// If `network_thread` or `worker_thread` are null, the PeerConnectionFactory +// will create the necessary thread internally. If `signaling_thread` is null, +// the PeerConnectionFactory will use the thread on which this method is called +// as the signaling thread, wrapping it in an webrtc::Thread object if needed. +RTC_EXPORT scoped_refptr +CreateModularPeerConnectionFactory( + PeerConnectionFactoryDependencies dependencies); + +// https://w3c.github.io/webrtc-pc/#dom-rtcsignalingstate +inline constexpr absl::string_view PeerConnectionInterface::AsString( + SignalingState state) { + switch (state) { + case SignalingState::kStable: + return "stable"; + case SignalingState::kHaveLocalOffer: + return "have-local-offer"; + case SignalingState::kHaveLocalPrAnswer: + return "have-local-pranswer"; + case SignalingState::kHaveRemoteOffer: + return "have-remote-offer"; + case SignalingState::kHaveRemotePrAnswer: + return "have-remote-pranswer"; + case SignalingState::kClosed: + return "closed"; + } + // This cannot happen. + // Not using "RTC_CHECK_NOTREACHED()" because AsString() is constexpr. + return ""; +} + +// https://w3c.github.io/webrtc-pc/#dom-rtcicegatheringstate +inline constexpr absl::string_view PeerConnectionInterface::AsString( + IceGatheringState state) { + switch (state) { + case IceGatheringState::kIceGatheringNew: + return "new"; + case IceGatheringState::kIceGatheringGathering: + return "gathering"; + case IceGatheringState::kIceGatheringComplete: + return "complete"; + } + // This cannot happen. + // Not using "RTC_CHECK_NOTREACHED()" because AsString() is constexpr. + return ""; +} + +// https://w3c.github.io/webrtc-pc/#dom-rtciceconnectionstate +inline constexpr absl::string_view PeerConnectionInterface::AsString( + PeerConnectionState state) { + switch (state) { + case PeerConnectionState::kNew: + return "new"; + case PeerConnectionState::kConnecting: + return "connecting"; + case PeerConnectionState::kConnected: + return "connected"; + case PeerConnectionState::kDisconnected: + return "disconnected"; + case PeerConnectionState::kFailed: + return "failed"; + case PeerConnectionState::kClosed: + return "closed"; + } + // This cannot happen. + // Not using "RTC_CHECK_NOTREACHED()" because AsString() is constexpr. + return ""; +} + +inline constexpr absl::string_view PeerConnectionInterface::AsString( + IceConnectionState state) { + switch (state) { + case kIceConnectionNew: + return "new"; + case kIceConnectionChecking: + return "checking"; + case kIceConnectionConnected: + return "connected"; + case kIceConnectionCompleted: + return "completed"; + case kIceConnectionFailed: + return "failed"; + case kIceConnectionDisconnected: + return "disconnected"; + case kIceConnectionClosed: + return "closed"; + case kIceConnectionMax: + // This cannot happen. + // Not using "RTC_CHECK_NOTREACHED()" because AsString() is constexpr. + return ""; + } + // This cannot happen. + // Not using "RTC_CHECK_NOTREACHED()" because AsString() is constexpr. + return ""; +} + +} // namespace webrtc + +#endif // API_PEER_CONNECTION_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/priority.h b/pkg/apm/webrtc/api/priority.h new file mode 100644 index 00000000..2735c2c2 --- /dev/null +++ b/pkg/apm/webrtc/api/priority.h @@ -0,0 +1,56 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_PRIORITY_H_ +#define API_PRIORITY_H_ + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/strong_alias.h" + +namespace webrtc { + +// GENERATED_JAVA_ENUM_PACKAGE: org.webrtc +enum class Priority { + kVeryLow, + kLow, + kMedium, + kHigh, +}; + +class PriorityValue + : public webrtc::StrongAlias { + public: + explicit PriorityValue(Priority priority) { + switch (priority) { + case Priority::kVeryLow: + value_ = 128; + break; + case Priority::kLow: + value_ = 256; + break; + case Priority::kMedium: + value_ = 512; + break; + case Priority::kHigh: + value_ = 1024; + break; + default: + RTC_CHECK_NOTREACHED(); + } + } + + explicit PriorityValue(uint16_t priority) : StrongAlias(priority) {} +}; + +} // namespace webrtc + +#endif // API_PRIORITY_H_ diff --git a/pkg/apm/webrtc/api/ref_count.h b/pkg/apm/webrtc/api/ref_count.h new file mode 100644 index 00000000..b3fb7630 --- /dev/null +++ b/pkg/apm/webrtc/api/ref_count.h @@ -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 webrtc::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 webrtc::scoped_refptr to manage all pointers to reference counted +// objects. Note that webrtc::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_ diff --git a/pkg/apm/webrtc/api/ref_counted_base.h b/pkg/apm/webrtc/api/ref_counted_base.h new file mode 100644 index 00000000..8862ebdc --- /dev/null +++ b/pkg/apm/webrtc/api/ref_counted_base.h @@ -0,0 +1,109 @@ +/* + * 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_REF_COUNTED_BASE_H_ +#define API_REF_COUNTED_BASE_H_ + +#include + +#include "api/ref_count.h" +#include "rtc_base/ref_counter.h" + +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(); + if (status == RefCountReleaseStatus::kDroppedLastRef) { + delete 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(); } + + virtual ~RefCountedBase() = default; + + private: + mutable webrtc::webrtc_impl::RefCounter ref_count_{0}; +}; + +// 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 webrtc::RefCountedNonVirtual { +// int foo_ = 0; +// }; +// +// webrtc::scoped_refptr my_int(new MyInt()); +// +// sizeof(MyInt) on a 32 bit system would then be 8, int + refcount and no +// vtable generated. +template +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 `webrtc::RefCountedObject`. + static_assert(!std::is_polymorphic::value, + "T has virtual methods. RefCountedBase is a better fit."); + const auto status = ref_count_.DecRef(); + if (status == RefCountReleaseStatus::kDroppedLastRef) { + delete static_cast(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. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using RefCountedBase = webrtc::RefCountedBase; +template +using RefCountedNonVirtual = webrtc::RefCountedNonVirtual; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // API_REF_COUNTED_BASE_H_ diff --git a/pkg/apm/webrtc/api/rtc_error.h b/pkg/apm/webrtc/api/rtc_error.h new file mode 100644 index 00000000..2ed33c41 --- /dev/null +++ b/pkg/apm/webrtc/api/rtc_error.h @@ -0,0 +1,352 @@ +/* + * 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_RTC_ERROR_H_ +#define API_RTC_ERROR_H_ + +#include + +#include +#include +#include +#include // For std::move. + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Enumeration to represent distinct classes of errors that an application +// may wish to act upon differently. These roughly map to DOMExceptions or +// RTCError "errorDetailEnum" values in the web API, as described in the +// comments below. +enum class RTCErrorType { + // No error. + NONE, + + // An operation is valid, but currently unsupported. + // Maps to OperationError DOMException. + UNSUPPORTED_OPERATION, + + // A supplied parameter is valid, but currently unsupported. + // Maps to OperationError DOMException. + UNSUPPORTED_PARAMETER, + + // General error indicating that a supplied parameter is invalid. + // Maps to InvalidAccessError or TypeError DOMException depending on context. + INVALID_PARAMETER, + + // Slightly more specific than INVALID_PARAMETER; a parameter's value was + // outside the allowed range. + // Maps to RangeError DOMException. + INVALID_RANGE, + + // Slightly more specific than INVALID_PARAMETER; an error occurred while + // parsing string input. + // Maps to SyntaxError DOMException. + SYNTAX_ERROR, + + // The object does not support this operation in its current state. + // Maps to InvalidStateError DOMException. + INVALID_STATE, + + // An attempt was made to modify the object in an invalid way. + // Maps to InvalidModificationError DOMException. + INVALID_MODIFICATION, + + // An error occurred within an underlying network protocol. + // Maps to NetworkError DOMException. + NETWORK_ERROR, + + // Some resource has been exhausted; file handles, hardware resources, ports, + // etc. + // Maps to OperationError DOMException. + RESOURCE_EXHAUSTED, + + // The operation failed due to an internal error. + // Maps to OperationError DOMException. + INTERNAL_ERROR, + + // An error occured that has additional data. + // The additional data is specified in + // https://w3c.github.io/webrtc-pc/#rtcerror-interface + // Maps to RTCError DOMException. + OPERATION_ERROR_WITH_DATA, +}; + +// Detail information, showing what further information should be present. +// https://w3c.github.io/webrtc-pc/#rtcerrordetailtype-enum +enum class RTCErrorDetailType { + NONE, + DATA_CHANNEL_FAILURE, + DTLS_FAILURE, + FINGERPRINT_FAILURE, + SCTP_FAILURE, + SDP_SYNTAX_ERROR, + HARDWARE_ENCODER_NOT_AVAILABLE, + HARDWARE_ENCODER_ERROR, +}; + +// Outputs the error as a friendly string. Update this method when adding a new +// error type. +// +// Only intended to be used for logging/diagnostics. The returned char* points +// to literal strings that live for the whole duration of the program. +RTC_EXPORT absl::string_view ToString(RTCErrorType error); +RTC_EXPORT absl::string_view ToString(RTCErrorDetailType error); + +template +void AbslStringify(Sink& sink, RTCErrorType error) { + sink.Append(ToString(error)); +} + +template +void AbslStringify(Sink& sink, RTCErrorDetailType error_detail) { + sink.Append(ToString(error_detail)); +} + +// Roughly corresponds to RTCError in the web api. Holds an error type, a +// message, and possibly additional information specific to that error. +// +// Doesn't contain anything beyond a type and message now, but will in the +// future as more errors are implemented. +class RTC_EXPORT RTCError { + public: + // Constructors. + + // Creates a "no error" error. + RTCError() {} + explicit RTCError(RTCErrorType type) : type_(type) {} + + RTCError(RTCErrorType type, absl::string_view message) + : type_(type), message_(message) {} + + // In many use cases, it is better to use move than copy, + // but copy and assignment are provided for those cases that need it. + // Note that this has extra overhead because it copies strings. + RTCError(const RTCError& other) = default; + RTCError(RTCError&&) = default; + RTCError& operator=(const RTCError& other) = default; + RTCError& operator=(RTCError&&) = default; + + // Identical to default constructed error. + // + // Preferred over the default constructor for code readability. + static RTCError OK(); + + // Error type. + RTCErrorType type() const { return type_; } + void set_type(RTCErrorType type) { type_ = type; } + + // Human-readable message describing the error. Shouldn't be used for + // anything but logging/diagnostics, since messages are not guaranteed to be + // stable. + const char* message() const; + + void set_message(absl::string_view message); + + RTCErrorDetailType error_detail() const { return error_detail_; } + void set_error_detail(RTCErrorDetailType detail) { error_detail_ = detail; } + std::optional sctp_cause_code() const { return sctp_cause_code_; } + void set_sctp_cause_code(uint16_t cause_code) { + sctp_cause_code_ = cause_code; + } + + // Convenience method for situations where you only care whether or not an + // error occurred. + bool ok() const { return type_ == RTCErrorType::NONE; } + + template + friend void AbslStringify(Sink& sink, const RTCError& error) { + sink.Append(ToString(error.type_)); + if (!error.message_.empty()) { + sink.Append(" with message: \""); + sink.Append(error.message_); + sink.Append("\""); + } + } + + private: + RTCErrorType type_ = RTCErrorType::NONE; + std::string message_; + RTCErrorDetailType error_detail_ = RTCErrorDetailType::NONE; + std::optional sctp_cause_code_; +}; + +// Helper macro that can be used by implementations to create an error with a +// message and log it. `message` should be a string literal or movable +// std::string. +#define LOG_AND_RETURN_ERROR_EX(type, message, severity) \ + { \ + RTC_DCHECK(type != RTCErrorType::NONE); \ + RTC_LOG(severity) << message << " (" << ::webrtc::ToString(type) << ")"; \ + return ::webrtc::RTCError(type, message); \ + } + +#define LOG_AND_RETURN_ERROR(type, message) \ + LOG_AND_RETURN_ERROR_EX(type, message, LS_ERROR) + +// RTCErrorOr is the union of an RTCError object and a T object. RTCErrorOr +// models the concept of an object that is either a usable value, or an error +// Status explaining why such a value is not present. To this end RTCErrorOr +// does not allow its RTCErrorType value to be RTCErrorType::NONE. This is +// enforced by a debug check in most cases. +// +// The primary use-case for RTCErrorOr is as the return value of a function +// which may fail. For example, CreateRtpSender will fail if the parameters +// could not be successfully applied at the media engine level, but if +// successful will return a unique_ptr to an RtpSender. +// +// Example client usage for a RTCErrorOr>: +// +// RTCErrorOr> result = FooFactory::MakeNewFoo(arg); +// if (result.ok()) { +// std::unique_ptr foo = result.ConsumeValue(); +// foo->DoSomethingCool(); +// } else { +// RTC_LOG(LS_ERROR) << result.error(); +// } +// +// Example factory implementation returning RTCErrorOr>: +// +// RTCErrorOr> FooFactory::MakeNewFoo(int arg) { +// if (arg <= 0) { +// return RTCError(RTCErrorType::INVALID_RANGE, "Arg must be positive"); +// } else { +// return std::unique_ptr(new Foo(arg)); +// } +// } +// +template +class RTCErrorOr { + // Used to convert between RTCErrorOr/RtcErrorOr, when an implicit + // conversion from Foo to Bar exists. + template + friend class RTCErrorOr; + + public: + typedef T element_type; + + // Constructs a new RTCErrorOr with RTCErrorType::INTERNAL_ERROR error. This + // is marked 'explicit' to try to catch cases like 'return {};', where people + // think RTCErrorOr> will be initialized with an empty + // vector, instead of a RTCErrorType::INTERNAL_ERROR error. + RTCErrorOr() : error_(RTCErrorType::INTERNAL_ERROR) {} + + // Constructs a new RTCErrorOr with the given non-ok error. After calling + // this constructor, calls to value() will DCHECK-fail. + // + // NOTE: Not explicit - we want to use RTCErrorOr as a return + // value, so it is convenient and sensible to be able to do 'return + // RTCError(...)' when the return type is RTCErrorOr. + // + // REQUIRES: !error.ok(). This requirement is DCHECKed. + RTCErrorOr(RTCError&& error) : error_(std::move(error)) { // NOLINT + RTC_DCHECK(!error_.ok()); + } + + // Constructs a new RTCErrorOr with the given value. After calling this + // constructor, calls to value() will succeed, and calls to error() will + // return a default-constructed RTCError. + // + // NOTE: Not explicit - we want to use RTCErrorOr as a return type + // so it is convenient and sensible to be able to do 'return T()' + // when the return type is RTCErrorOr. + RTCErrorOr(const T& value) : value_(value) {} // NOLINT + RTCErrorOr(T&& value) : value_(std::move(value)) {} // NOLINT + + // Delete the copy constructor and assignment operator; there aren't any use + // cases where you should need to copy an RTCErrorOr, as opposed to moving + // it. Can revisit this decision if use cases arise in the future. + RTCErrorOr(const RTCErrorOr& other) = delete; + RTCErrorOr& operator=(const RTCErrorOr& other) = delete; + + // Move constructor and move-assignment operator. + // + // Visual Studio doesn't support "= default" with move constructors or + // assignment operators (even though they compile, they segfault), so define + // them explicitly. + RTCErrorOr(RTCErrorOr&& other) + : error_(std::move(other.error_)), value_(std::move(other.value_)) {} + RTCErrorOr& operator=(RTCErrorOr&& other) { + error_ = std::move(other.error_); + value_ = std::move(other.value_); + return *this; + } + + // Conversion constructor and assignment operator; T must be copy or move + // constructible from U. + template + RTCErrorOr(RTCErrorOr other) // NOLINT + : error_(std::move(other.error_)), value_(std::move(other.value_)) {} + template + RTCErrorOr& operator=(RTCErrorOr other) { + error_ = std::move(other.error_); + value_ = std::move(other.value_); + return *this; + } + + // Returns a reference to our error. If this contains a T, then returns + // default-constructed RTCError. + const RTCError& error() const { return error_; } + + // Moves the error. Can be useful if, say "CreateFoo" returns an + // RTCErrorOr, and internally calls "CreateBar" which returns an + // RTCErrorOr, and wants to forward the error up the stack. + RTCError MoveError() { return std::move(error_); } + + // Returns this->error().ok() + bool ok() const { return error_.ok(); } + + // Returns a reference to our current value, or DCHECK-fails if !this->ok(). + // + // Can be convenient for the implementation; for example, a method may want + // to access the value in some way before returning it to the next method on + // the stack. + const T& value() const { + RTC_DCHECK(ok()); + return *value_; + } + T& value() { + RTC_DCHECK(ok()); + return *value_; + } + + // Moves our current value out of this object and returns it, or DCHECK-fails + // if !this->ok(). + T MoveValue() { + RTC_DCHECK(ok()); + return std::move(*value_); + } + + template + friend void AbslStringify(Sink& sink, const RTCErrorOr& error_or) { + if (error_or.ok()) { + sink.Append("OK"); + if constexpr (std::is_convertible_v) { + sink.Append(" with value: "); + sink.Append(absl::StrCat(error_or.value())); + } + } else { + sink.Append(absl::StrCat(error_or.error())); + } + } + + private: + RTCError error_; + std::optional value_; +}; + +} // namespace webrtc + +#endif // API_RTC_ERROR_H_ diff --git a/pkg/apm/webrtc/api/rtc_event_log/rtc_event_log.h b/pkg/apm/webrtc/api/rtc_event_log/rtc_event_log.h new file mode 100644 index 00000000..1e70f4aa --- /dev/null +++ b/pkg/apm/webrtc/api/rtc_event_log/rtc_event_log.h @@ -0,0 +1,19 @@ +#ifndef API_RTC_EVENT_LOG_RTC_EVENT_LOG_H_ +#define API_RTC_EVENT_LOG_RTC_EVENT_LOG_H_ + +#include + +namespace webrtc { + +class RtcEventLog { + public: + virtual ~RtcEventLog() = default; +}; + +class RtcEventLogNull : public RtcEventLog { + public: + ~RtcEventLogNull() override = default; +}; + +} // namespace webrtc +#endif // API_RTC_EVENT_LOG_RTC_EVENT_LOG_H_ diff --git a/pkg/apm/webrtc/api/rtp_headers.h b/pkg/apm/webrtc/api/rtp_headers.h new file mode 100644 index 00000000..108d3b78 --- /dev/null +++ b/pkg/apm/webrtc/api/rtp_headers.h @@ -0,0 +1,22 @@ +#ifndef API_RTP_HEADERS_H_ +#define API_RTP_HEADERS_H_ + +#include +#include + +namespace webrtc { + +struct RTPHeader { + bool markerBit = false; + uint8_t payloadType = 0; + uint16_t sequenceNumber = 0; + uint32_t timestamp = 0; + uint32_t ssrc = 0; + uint8_t numCSRCs = 0; + uint32_t arrOfCSRCs[15] = {}; + size_t paddingLength = 0; + size_t headerLength = 0; +}; + +} // namespace webrtc +#endif // API_RTP_HEADERS_H_ diff --git a/pkg/apm/webrtc/api/rtp_packet_info.h b/pkg/apm/webrtc/api/rtp_packet_info.h new file mode 100644 index 00000000..988d3ffe --- /dev/null +++ b/pkg/apm/webrtc/api/rtp_packet_info.h @@ -0,0 +1,19 @@ +#ifndef API_RTP_PACKET_INFO_H_ +#define API_RTP_PACKET_INFO_H_ + +#include +#include "api/rtp_headers.h" + +namespace webrtc { + +struct AbsoluteCaptureTime { + uint64_t absolute_capture_timestamp = 0; + int64_t estimated_capture_clock_offset = 0; +}; + +struct RtpPacketInfo { + uint32_t ssrc() const { return 0; } +}; + +} // namespace webrtc +#endif // API_RTP_PACKET_INFO_H_ diff --git a/pkg/apm/webrtc/api/rtp_packet_infos.h b/pkg/apm/webrtc/api/rtp_packet_infos.h new file mode 100644 index 00000000..e5e106c6 --- /dev/null +++ b/pkg/apm/webrtc/api/rtp_packet_infos.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_RTP_PACKET_INFOS_H_ +#define API_RTP_PACKET_INFOS_H_ + +#include +#include + +#include "api/make_ref_counted.h" +#include "api/ref_counted_base.h" +#include "api/rtp_packet_info.h" +#include "api/scoped_refptr.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Semi-immutable structure to hold information about packets used to assemble +// an audio or video frame. Uses internal reference counting to make it very +// cheap to copy. +// +// We should ideally just use `std::vector` 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 { + public: + using vector_type = std::vector; + + using value_type = vector_type::value_type; + using size_type = vector_type::size_type; + using difference_type = vector_type::difference_type; + using const_reference = vector_type::const_reference; + using const_pointer = vector_type::const_pointer; + using const_iterator = vector_type::const_iterator; + using const_reverse_iterator = vector_type::const_reverse_iterator; + + using reference = const_reference; + using pointer = const_pointer; + using iterator = const_iterator; + using reverse_iterator = const_reverse_iterator; + + RtpPacketInfos() {} + explicit RtpPacketInfos(const vector_type& entries) + : data_(Data::Create(entries)) {} + + explicit RtpPacketInfos(vector_type&& entries) + : data_(Data::Create(std::move(entries))) {} + + RtpPacketInfos(const RtpPacketInfos& other) = default; + RtpPacketInfos(RtpPacketInfos&& other) = default; + RtpPacketInfos& operator=(const RtpPacketInfos& other) = default; + RtpPacketInfos& operator=(RtpPacketInfos&& other) = default; + + const_reference operator[](size_type pos) const { return entries()[pos]; } + + const_reference at(size_type pos) const { return entries().at(pos); } + const_reference front() const { return entries().front(); } + const_reference back() const { return entries().back(); } + + const_iterator begin() const { return entries().begin(); } + const_iterator end() const { return entries().end(); } + const_reverse_iterator rbegin() const { return entries().rbegin(); } + const_reverse_iterator rend() const { return entries().rend(); } + + const_iterator cbegin() const { return entries().cbegin(); } + const_iterator cend() const { return entries().cend(); } + const_reverse_iterator crbegin() const { return entries().crbegin(); } + const_reverse_iterator crend() const { return entries().crend(); } + + bool empty() const { return entries().empty(); } + size_type size() const { return entries().size(); } + + private: + class Data final : public RefCountedNonVirtual { + public: + static scoped_refptr Create(const vector_type& entries) { + // Performance optimization for the empty case. + if (entries.empty()) { + return nullptr; + } + + return make_ref_counted(entries); + } + + static scoped_refptr Create(vector_type&& entries) { + // Performance optimization for the empty case. + if (entries.empty()) { + return nullptr; + } + + return make_ref_counted(std::move(entries)); + } + + const vector_type& entries() const { return entries_; } + + explicit Data(const vector_type& entries) : entries_(entries) {} + explicit Data(vector_type&& entries) : entries_(std::move(entries)) {} + ~Data() = default; + + private: + const vector_type entries_; + }; + + static const vector_type& empty_entries() { + static const vector_type& value = *new vector_type(); + return value; + } + + const vector_type& entries() const { + if (data_ != nullptr) { + return data_->entries(); + } else { + return empty_entries(); + } + } + + scoped_refptr data_; +}; + +} // namespace webrtc + +#endif // API_RTP_PACKET_INFOS_H_ diff --git a/pkg/apm/webrtc/api/rtp_packet_sender.h b/pkg/apm/webrtc/api/rtp_packet_sender.h new file mode 100644 index 00000000..92220a1f --- /dev/null +++ b/pkg/apm/webrtc/api/rtp_packet_sender.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_RTP_PACKET_SENDER_H_ +#define API_RTP_PACKET_SENDER_H_ + +#include +#include +#include + +namespace webrtc { + +class RtpPacketToSend; + +class RtpPacketSender { + public: + virtual ~RtpPacketSender() = default; + + // Insert a set of packets into queue, for eventual transmission. Based on the + // type of packets, they will be prioritized and scheduled relative to other + // packets and the current target send rate. + virtual void EnqueuePackets( + std::vector> packets) = 0; + + // Clear any pending packets with the given SSRC from the queue. + // TODO(crbug.com/1395081): Make pure virtual when downstream code has been + // updated. + virtual void RemovePacketsForSsrc(uint32_t /* ssrc */) {} +}; + +} // namespace webrtc + +#endif // API_RTP_PACKET_SENDER_H_ diff --git a/pkg/apm/webrtc/api/rtp_parameters.h b/pkg/apm/webrtc/api/rtp_parameters.h new file mode 100644 index 00000000..57d77f2f --- /dev/null +++ b/pkg/apm/webrtc/api/rtp_parameters.h @@ -0,0 +1,687 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_RTP_PARAMETERS_H_ +#define API_RTP_PARAMETERS_H_ + +#include + +#include +#include +#include +#include + +#include "absl/container/inlined_vector.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "api/media_types.h" +#include "api/priority.h" +#include "api/rtp_transceiver_direction.h" +#include "api/video/resolution.h" +#include "api/video_codecs/scalability_mode.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +using CodecParameterMap = std::map; + +// These structures are intended to mirror those defined by: +// http://draft.ortc.org/#rtcrtpdictionaries* +// Contains everything specified as of 2017 Jan 24. +// +// They are used when retrieving or modifying the parameters of an +// RtpSender/RtpReceiver, or retrieving capabilities. +// +// Note on conventions: Where ORTC may use "octet", "short" and "unsigned" +// types, we typically use "int", in keeping with our style guidelines. The +// parameter's actual valid range will be enforced when the parameters are set, +// rather than when the parameters struct is built. An exception is made for +// SSRCs, since they use the full unsigned 32-bit range, and aren't expected to +// be used for any numeric comparisons/operations. +// +// Additionally, where ORTC uses strings, we may use enums for things that have +// a fixed number of supported values. However, for things that can be extended +// (such as codecs, by providing an external encoder factory), a string +// identifier is used. + +enum class FecMechanism { + RED, + RED_AND_ULPFEC, + FLEXFEC, +}; + +// Used in RtcpFeedback struct. +enum class RtcpFeedbackType { + CCM, + LNTF, // "goog-lntf" + NACK, + REMB, // "goog-remb" + TRANSPORT_CC, +}; + +// Used in RtcpFeedback struct when type is NACK or CCM. +enum class RtcpFeedbackMessageType { + // Equivalent to {type: "nack", parameter: undefined} in ORTC. + GENERIC_NACK, + PLI, // Usable with NACK. + FIR, // Usable with CCM. +}; + +enum class DtxStatus { + DISABLED, + ENABLED, +}; + +// Based on the spec in +// https://w3c.github.io/webrtc-pc/#idl-def-rtcdegradationpreference. +// These options are enforced on a best-effort basis. For instance, all of +// these options may suffer some frame drops in order to avoid queuing. +// TODO(sprang): Look into possibility of more strictly enforcing the +// maintain-framerate option. +// TODO(deadbeef): Default to "balanced", as the spec indicates? +enum class DegradationPreference { + // Don't take any actions based on over-utilization signals. Not part of the + // web API. + DISABLED, + // On over-use, request lower resolution, possibly causing down-scaling. + MAINTAIN_FRAMERATE, + // On over-use, request lower frame rate, possibly causing frame drops. + MAINTAIN_RESOLUTION, + // Try to strike a "pleasing" balance between frame rate or resolution. + BALANCED, +}; + +RTC_EXPORT const char* DegradationPreferenceToString( + DegradationPreference degradation_preference); + +RTC_EXPORT extern const double kDefaultBitratePriority; + +struct RTC_EXPORT RtcpFeedback { + RtcpFeedbackType type = RtcpFeedbackType::CCM; + + // Equivalent to ORTC "parameter" field with slight differences: + // 1. It's an enum instead of a string. + // 2. Generic NACK feedback is represented by a GENERIC_NACK message type, + // rather than an unset "parameter" value. + std::optional message_type; + + // Constructors for convenience. + RtcpFeedback(); + explicit RtcpFeedback(RtcpFeedbackType type); + RtcpFeedback(RtcpFeedbackType type, RtcpFeedbackMessageType message_type); + RtcpFeedback(const RtcpFeedback&); + ~RtcpFeedback(); + + bool operator==(const RtcpFeedback& o) const { + return type == o.type && message_type == o.message_type; + } + bool operator!=(const RtcpFeedback& o) const { return !(*this == o); } +}; + +struct RTC_EXPORT RtpCodec { + RtpCodec(); + RtpCodec(const RtpCodec&); + virtual ~RtpCodec(); + + // Build MIME "type/subtype" string from `name` and `kind`. + std::string mime_type() const { return MediaTypeToString(kind) + "/" + name; } + + // Used to identify the codec. Equivalent to MIME subtype. + std::string name; + + // The media type of this codec. Equivalent to MIME top-level type. + webrtc::MediaType kind = webrtc::MediaType::AUDIO; + + // If unset, the implementation default is used. + std::optional clock_rate; + + // The number of audio channels used. Unset for video codecs. If unset for + // audio, the implementation default is used. + // TODO(deadbeef): The "implementation default" part isn't fully implemented. + // Only defaults to 1, even though some codecs (such as opus) should really + // default to 2. + std::optional num_channels; + + // Feedback mechanisms to be used for this codec. + // TODO(deadbeef): Not implemented with PeerConnection senders/receivers. + std::vector rtcp_feedback; + + // Codec-specific parameters that must be signaled to the remote party. + // + // Corresponds to "a=fmtp" parameters in SDP. + // + // Contrary to ORTC, these parameters are named using all lowercase strings. + // This helps make the mapping to SDP simpler, if an application is using SDP. + // Boolean values are represented by the string "1". + std::map parameters; + + bool operator==(const RtpCodec& o) const { + return name == o.name && kind == o.kind && clock_rate == o.clock_rate && + num_channels == o.num_channels && rtcp_feedback == o.rtcp_feedback && + parameters == o.parameters; + } + bool operator!=(const RtpCodec& o) const { return !(*this == o); } + bool IsResiliencyCodec() const; + bool IsMediaCodec() const; +}; + +// RtpCodecCapability is to RtpCodecParameters as RtpCapabilities is to +// RtpParameters. This represents the static capabilities of an endpoint's +// implementation of a codec. +struct RTC_EXPORT RtpCodecCapability : public RtpCodec { + RtpCodecCapability(); + virtual ~RtpCodecCapability(); + + // Default payload type for this codec. Mainly needed for codecs that have + // statically assigned payload types. + std::optional preferred_payload_type; + + // List of scalability modes supported by the video codec. + absl::InlinedVector scalability_modes; + + bool operator==(const RtpCodecCapability& o) const { + return RtpCodec::operator==(o) && + preferred_payload_type == o.preferred_payload_type && + scalability_modes == o.scalability_modes; + } + bool operator!=(const RtpCodecCapability& o) const { return !(*this == o); } + + template + friend void AbslStringify(Sink& sink, const RtpCodecCapability& cap) { + if (cap.kind == webrtc::MediaType::AUDIO) { + absl::Format(&sink, "[audio/%s/%d/%d]", cap.name, + cap.clock_rate.value_or(0), cap.num_channels.value_or(1)); + } else { + absl::Format(&sink, "[video/%s]", cap.name); + } + } +}; + +// Used in RtpCapabilities and RtpTransceiverInterface's header extensions query +// and setup methods; represents the capabilities/preferences of an +// implementation for a header extension. +// +// Just called "RtpHeaderExtension" in ORTC, but the "Capability" suffix was +// added here for consistency and to avoid confusion with +// RtpHeaderExtensionParameters. +// +// Note that ORTC includes a "kind" field, but we omit this because it's +// redundant; if you call +// "RtpReceiver::GetCapabilities(webrtc::MediaType::AUDIO)", you know you're +// getting audio capabilities. +struct RTC_EXPORT RtpHeaderExtensionCapability { + // URI of this extension, as defined in RFC8285. + std::string uri; + + // Preferred value of ID that goes in the packet. + std::optional preferred_id; + + // If true, it's preferred that the value in the header is encrypted. + bool preferred_encrypt = false; + + // The direction of the extension. The kStopped value is only used with + // RtpTransceiverInterface::SetHeaderExtensionsToNegotiate() and + // SetHeaderExtensionsToNegotiate(). + RtpTransceiverDirection direction = RtpTransceiverDirection::kSendRecv; + + // Constructors for convenience. + RtpHeaderExtensionCapability(); + explicit RtpHeaderExtensionCapability(absl::string_view uri); + RtpHeaderExtensionCapability(absl::string_view uri, int preferred_id); + RtpHeaderExtensionCapability(absl::string_view uri, + int preferred_id, + RtpTransceiverDirection direction); + RtpHeaderExtensionCapability(absl::string_view uri, + int preferred_id, + bool preferred_encrypt, + RtpTransceiverDirection direction); + ~RtpHeaderExtensionCapability(); + + bool operator==(const RtpHeaderExtensionCapability& o) const { + return uri == o.uri && preferred_id == o.preferred_id && + preferred_encrypt == o.preferred_encrypt && direction == o.direction; + } + bool operator!=(const RtpHeaderExtensionCapability& o) const { + return !(*this == o); + } +}; + +// RTP header extension, see RFC8285. +struct RTC_EXPORT RtpExtension { + enum Filter { + // Encrypted extensions will be ignored and only non-encrypted extensions + // will be considered. + kDiscardEncryptedExtension, + // Encrypted extensions will be preferred but will fall back to + // non-encrypted extensions if necessary. + kPreferEncryptedExtension, + // Encrypted extensions will be required, so any non-encrypted extensions + // will be discarded. + kRequireEncryptedExtension, + }; + + RtpExtension(); + RtpExtension(absl::string_view uri, int id); + RtpExtension(absl::string_view uri, int id, bool encrypt); + ~RtpExtension(); + + std::string ToString() const; + bool operator==(const RtpExtension& rhs) const { + return uri == rhs.uri && id == rhs.id && encrypt == rhs.encrypt; + } + static bool IsSupportedForAudio(absl::string_view uri); + static bool IsSupportedForVideo(absl::string_view uri); + // Return "true" if the given RTP header extension URI may be encrypted. + static bool IsEncryptionSupported(absl::string_view uri); + + // Returns the header extension with the given URI or nullptr if not found. + static const RtpExtension* FindHeaderExtensionByUri( + const std::vector& extensions, + absl::string_view uri, + Filter filter); + + // Returns the header extension with the given URI and encrypt parameter, + // if found, otherwise nullptr. + static const RtpExtension* FindHeaderExtensionByUriAndEncryption( + const std::vector& extensions, + absl::string_view uri, + bool encrypt); + + // Returns a list of extensions where any extension URI is unique. + // The returned list will be sorted by uri first, then encrypt and id last. + // Having the list sorted allows the caller fo compare filtered lists for + // equality to detect when changes have been made. + static const std::vector DeduplicateHeaderExtensions( + const std::vector& extensions, + Filter filter); + + // Encryption of Header Extensions, see RFC 6904 for details: + // https://tools.ietf.org/html/rfc6904 + static constexpr char kEncryptHeaderExtensionsUri[] = + "urn:ietf:params:rtp-hdrext:encrypt"; + + // Header extension for audio levels, as defined in: + // https://tools.ietf.org/html/rfc6464 + static constexpr char kAudioLevelUri[] = + "urn:ietf:params:rtp-hdrext:ssrc-audio-level"; + + // Header extension for RTP timestamp offset, see RFC 5450 for details: + // http://tools.ietf.org/html/rfc5450 + static constexpr char kTimestampOffsetUri[] = + "urn:ietf:params:rtp-hdrext:toffset"; + + // Header extension for absolute send time, see url for details: + // http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time + static constexpr char kAbsSendTimeUri[] = + "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"; + + // Header extension for absolute capture time, see url for details: + // http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time + static constexpr char kAbsoluteCaptureTimeUri[] = + "http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time"; + + // Header extension for coordination of video orientation, see url for + // details: + // http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/12.07.00_60/ts_126114v120700p.pdf + static constexpr char kVideoRotationUri[] = "urn:3gpp:video-orientation"; + + // Header extension for video content type. E.g. default or screenshare. + static constexpr char kVideoContentTypeUri[] = + "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type"; + + // Header extension for video timing. + static constexpr char kVideoTimingUri[] = + "http://www.webrtc.org/experiments/rtp-hdrext/video-timing"; + + // Experimental codec agnostic frame descriptor. + static constexpr char kGenericFrameDescriptorUri00[] = + "http://www.webrtc.org/experiments/rtp-hdrext/" + "generic-frame-descriptor-00"; + static constexpr char kDependencyDescriptorUri[] = + "https://aomediacodec.github.io/av1-rtp-spec/" + "#dependency-descriptor-rtp-header-extension"; + + // Experimental extension for signalling target bitrate per layer. + static constexpr char kVideoLayersAllocationUri[] = + "http://www.webrtc.org/experiments/rtp-hdrext/video-layers-allocation00"; + + // Header extension for transport sequence number, see url for details: + // http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions + static constexpr char kTransportSequenceNumberUri[] = + "http://www.ietf.org/id/" + "draft-holmer-rmcat-transport-wide-cc-extensions-01"; + static constexpr char kTransportSequenceNumberV2Uri[] = + "http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02"; + + // This extension allows applications to adaptively limit the playout delay + // on frames as per the current needs. For example, a gaming application + // has very different needs on end-to-end delay compared to a video-conference + // application. + static constexpr char kPlayoutDelayUri[] = + "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay"; + + // Header extension for color space information. + static constexpr char kColorSpaceUri[] = + "http://www.webrtc.org/experiments/rtp-hdrext/color-space"; + + // Header extension for identifying media section within a transport. + // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-49#section-15 + static constexpr char kMidUri[] = "urn:ietf:params:rtp-hdrext:sdes:mid"; + + // Header extension for RIDs and Repaired RIDs + // https://tools.ietf.org/html/draft-ietf-avtext-rid-09 + // https://tools.ietf.org/html/draft-ietf-mmusic-rid-15 + static constexpr char kRidUri[] = + "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"; + static constexpr char kRepairedRidUri[] = + "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"; + + // Header extension to propagate webrtc::VideoFrame id field + static constexpr char kVideoFrameTrackingIdUri[] = + "http://www.webrtc.org/experiments/rtp-hdrext/video-frame-tracking-id"; + + // Header extension for Mixer-to-Client audio levels per CSRC as defined in + // https://tools.ietf.org/html/rfc6465 + static constexpr char kCsrcAudioLevelsUri[] = + "urn:ietf:params:rtp-hdrext:csrc-audio-level"; + + // Header extension for automatic corruption detection. + static constexpr char kCorruptionDetectionUri[] = + "http://www.webrtc.org/experiments/rtp-hdrext/corruption-detection"; + + // Inclusive min and max IDs for two-byte header extensions and one-byte + // header extensions, per RFC8285 Section 4.2-4.3. + static constexpr int kMinId = 1; + static constexpr int kMaxId = 255; + static constexpr int kMaxValueSize = 255; + static constexpr int kOneByteHeaderExtensionMaxId = 14; + static constexpr int kOneByteHeaderExtensionMaxValueSize = 16; + + std::string uri; + int id = 0; + bool encrypt = false; + + template + friend void AbslStringify(Sink& sink, const RtpExtension& extension) { + if (extension.encrypt) { + absl::Format(&sink, "[%d %s (encrypted)]", extension.id, extension.uri); + } else { + absl::Format(&sink, "[%d %s]", extension.id, extension.uri); + } + } +}; + +struct RTC_EXPORT RtpFecParameters { + // If unset, a value is chosen by the implementation. + // Works just like RtpEncodingParameters::ssrc. + std::optional ssrc; + + FecMechanism mechanism = FecMechanism::RED; + + // Constructors for convenience. + RtpFecParameters(); + explicit RtpFecParameters(FecMechanism mechanism); + RtpFecParameters(FecMechanism mechanism, uint32_t ssrc); + RtpFecParameters(const RtpFecParameters&); + ~RtpFecParameters(); + + bool operator==(const RtpFecParameters& o) const { + return ssrc == o.ssrc && mechanism == o.mechanism; + } + bool operator!=(const RtpFecParameters& o) const { return !(*this == o); } +}; + +struct RTC_EXPORT RtpRtxParameters { + // If unset, a value is chosen by the implementation. + // Works just like RtpEncodingParameters::ssrc. + std::optional ssrc; + + // Constructors for convenience. + RtpRtxParameters(); + explicit RtpRtxParameters(uint32_t ssrc); + RtpRtxParameters(const RtpRtxParameters&); + ~RtpRtxParameters(); + + bool operator==(const RtpRtxParameters& o) const { return ssrc == o.ssrc; } + bool operator!=(const RtpRtxParameters& o) const { return !(*this == o); } +}; + +struct RTC_EXPORT RtpEncodingParameters { + RtpEncodingParameters(); + RtpEncodingParameters(const RtpEncodingParameters&); + ~RtpEncodingParameters(); + + // If unset, a value is chosen by the implementation. + // + // Note that the chosen value is NOT returned by GetParameters, because it + // may change due to an SSRC conflict, in which case the conflict is handled + // internally without any event. Another way of looking at this is that an + // unset SSRC acts as a "wildcard" SSRC. + std::optional ssrc; + + // The relative bitrate priority of this encoding. Currently this is + // implemented for the entire rtp sender by using the value of the first + // encoding parameter. + // See: https://w3c.github.io/webrtc-priority/#enumdef-rtcprioritytype + // "very-low" = 0.5 + // "low" = 1.0 + // "medium" = 2.0 + // "high" = 4.0 + // TODO(webrtc.bugs.org/8630): Implement this per encoding parameter. + // Currently there is logic for how bitrate is distributed per simulcast layer + // in the VideoBitrateAllocator. This must be updated to incorporate relative + // bitrate priority. + double bitrate_priority = kDefaultBitratePriority; + + // The relative DiffServ Code Point priority for this encoding, allowing + // packets to be marked relatively higher or lower without affecting + // bandwidth allocations. See https://w3c.github.io/webrtc-dscp-exp/ . + // TODO(http://crbug.com/webrtc/8630): Implement this per encoding parameter. + // TODO(http://crbug.com/webrtc/11379): TCP connections should use a single + // DSCP value even if shared by multiple senders; this is not implemented. + Priority network_priority = Priority::kLow; + + // If set, this represents the Transport Independent Application Specific + // maximum bandwidth defined in RFC3890. If unset, there is no maximum + // bitrate. Currently this is implemented for the entire rtp sender by using + // the value of the first encoding parameter. + // + // Just called "maxBitrate" in ORTC spec. + // + // TODO(deadbeef): With ORTC RtpSenders, this currently sets the total + // bandwidth for the entire bandwidth estimator (audio and video). This is + // just always how "b=AS" was handled, but it's not correct and should be + // fixed. + std::optional max_bitrate_bps; + + // Specifies the minimum bitrate in bps for video. + std::optional min_bitrate_bps; + + // Specifies the maximum framerate in fps for video. + std::optional max_framerate; + + // Specifies the number of temporal layers for video (if the feature is + // supported by the codec implementation). + // Screencast support is experimental. + std::optional num_temporal_layers; + + // For video, scale the resolution down by this factor. + std::optional scale_resolution_down_by; + + // https://w3c.github.io/webrtc-svc/#rtcrtpencodingparameters + std::optional scalability_mode; + + // This is an alternative API to `scale_resolution_down_by` but expressed in + // absolute terms (max width and max height) as opposed to relative terms (a + // scaling factor that is relative to the input frame size). + // + // If both `scale_resolution_down_by` and `scale_resolution_down_to` are + // specified, the "scale by" value is ignored. + // + // See spec: + // https://w3c.github.io/webrtc-extensions/#dom-rtcrtpencodingparameters-scaleresolutiondownto + std::optional scale_resolution_down_to; + + // For an RtpSender, set to true to cause this encoding to be encoded and + // sent, and false for it not to be encoded and sent. This allows control + // across multiple encodings of a sender for turning simulcast layers on and + // off. + // TODO(webrtc.bugs.org/8807): Updating this parameter will trigger an encoder + // reset, but this isn't necessarily required. + bool active = true; + + // Value to use for RID RTP header extension. + // Called "encodingId" in ORTC. + std::string rid; + bool request_key_frame = false; + + // Allow dynamic frame length changes for audio: + // https://w3c.github.io/webrtc-extensions/#dom-rtcrtpencodingparameters-adaptiveptime + bool adaptive_ptime = false; + + // Allow changing the used codec for this encoding. + std::optional codec; + + bool operator==(const RtpEncodingParameters& o) const { + return ssrc == o.ssrc && bitrate_priority == o.bitrate_priority && + network_priority == o.network_priority && + max_bitrate_bps == o.max_bitrate_bps && + min_bitrate_bps == o.min_bitrate_bps && + max_framerate == o.max_framerate && + num_temporal_layers == o.num_temporal_layers && + scale_resolution_down_by == o.scale_resolution_down_by && + active == o.active && rid == o.rid && + adaptive_ptime == o.adaptive_ptime && + scale_resolution_down_to == o.scale_resolution_down_to && + codec == o.codec; + } + bool operator!=(const RtpEncodingParameters& o) const { + return !(*this == o); + } +}; + +struct RTC_EXPORT RtpCodecParameters : public RtpCodec { + RtpCodecParameters(); + RtpCodecParameters(const RtpCodecParameters&); + virtual ~RtpCodecParameters(); + + // Payload type used to identify this codec in RTP packets. + // This must always be present, and must be unique across all codecs using + // the same transport. + int payload_type = 0; + + bool operator==(const RtpCodecParameters& o) const { + return RtpCodec::operator==(o) && payload_type == o.payload_type; + } + bool operator!=(const RtpCodecParameters& o) const { return !(*this == o); } +}; + +// RtpCapabilities is used to represent the static capabilities of an endpoint. +// An application can use these capabilities to construct an RtpParameters. +struct RTC_EXPORT RtpCapabilities { + RtpCapabilities(); + ~RtpCapabilities(); + + // Supported codecs. + std::vector codecs; + + // Supported RTP header extensions. + std::vector header_extensions; + + // Supported Forward Error Correction (FEC) mechanisms. Note that the RED, + // ulpfec and flexfec codecs used by these mechanisms will still appear in + // `codecs`. + std::vector fec; + + bool operator==(const RtpCapabilities& o) const { + return codecs == o.codecs && header_extensions == o.header_extensions && + fec == o.fec; + } + bool operator!=(const RtpCapabilities& o) const { return !(*this == o); } +}; + +struct RtcpParameters final { + RtcpParameters(); + RtcpParameters(const RtcpParameters&); + ~RtcpParameters(); + + // The SSRC to be used in the "SSRC of packet sender" field. If not set, one + // will be chosen by the implementation. + // TODO(deadbeef): Not implemented. + std::optional ssrc; + + // The Canonical Name (CNAME) used by RTCP (e.g. in SDES messages). + // + // If empty in the construction of the RtpTransport, one will be generated by + // the implementation, and returned in GetRtcpParameters. Multiple + // RtpTransports created by the same OrtcFactory will use the same generated + // CNAME. + // + // If empty when passed into SetParameters, the CNAME simply won't be + // modified. + std::string cname; + + // Send reduced-size RTCP? + bool reduced_size = false; + + // Send RTCP multiplexed on the RTP transport? + // Not used with PeerConnection senders/receivers + bool mux = true; + + bool operator==(const RtcpParameters& o) const { + return ssrc == o.ssrc && cname == o.cname && + reduced_size == o.reduced_size && mux == o.mux; + } + bool operator!=(const RtcpParameters& o) const { return !(*this == o); } +}; + +struct RTC_EXPORT RtpParameters { + RtpParameters(); + RtpParameters(const RtpParameters&); + ~RtpParameters(); + + // Used when calling getParameters/setParameters with a PeerConnection + // RtpSender, to ensure that outdated parameters are not unintentionally + // applied successfully. + std::string transaction_id; + + // Value to use for MID RTP header extension. + // Called "muxId" in ORTC. + // TODO(deadbeef): Not implemented. + std::string mid; + + std::vector codecs; + + std::vector header_extensions; + + std::vector encodings; + + // Only available with a Peerconnection RtpSender. + // In ORTC, our API includes an additional "RtpTransport" + // abstraction on which RTCP parameters are set. + RtcpParameters rtcp; + + // When bandwidth is constrained and the RtpSender needs to choose between + // degrading resolution or degrading framerate, degradationPreference + // indicates which is preferred. Only for video tracks. + std::optional degradation_preference; + + bool operator==(const RtpParameters& o) const { + return mid == o.mid && codecs == o.codecs && + header_extensions == o.header_extensions && + encodings == o.encodings && rtcp == o.rtcp && + degradation_preference == o.degradation_preference; + } + bool operator!=(const RtpParameters& o) const { return !(*this == o); } +}; + +} // namespace webrtc + +#endif // API_RTP_PARAMETERS_H_ diff --git a/pkg/apm/webrtc/api/rtp_receiver_interface.h b/pkg/apm/webrtc/api/rtp_receiver_interface.h new file mode 100644 index 00000000..29df678e --- /dev/null +++ b/pkg/apm/webrtc/api/rtp_receiver_interface.h @@ -0,0 +1,137 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file contains interfaces for RtpReceivers +// http://w3c.github.io/webrtc-pc/#rtcrtpreceiver-interface + +#ifndef API_RTP_RECEIVER_INTERFACE_H_ +#define API_RTP_RECEIVER_INTERFACE_H_ + +#include +#include +#include +#include + +#include "api/crypto/frame_decryptor_interface.h" +#include "api/dtls_transport_interface.h" +#include "api/frame_transformer_interface.h" +#include "api/media_stream_interface.h" +#include "api/media_types.h" +#include "api/ref_count.h" +#include "api/rtp_parameters.h" +#include "api/scoped_refptr.h" +#include "api/transport/rtp/rtp_source.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +class RtpReceiverObserverInterface { + public: + // Note: Currently if there are multiple RtpReceivers of the same media type, + // they will all call OnFirstPacketReceived at once. + // + // In the future, it's likely that an RtpReceiver will only call + // OnFirstPacketReceived when a packet is received specifically for its + // SSRC/mid. + virtual void OnFirstPacketReceived(webrtc::MediaType media_type) = 0; + + protected: + virtual ~RtpReceiverObserverInterface() {} +}; + +class RTC_EXPORT RtpReceiverInterface : public webrtc::RefCountInterface, + public FrameTransformerHost { + public: + virtual scoped_refptr track() const = 0; + + // The dtlsTransport attribute exposes the DTLS transport on which the + // media is received. It may be null. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtpreceiver-transport + // TODO(https://bugs.webrtc.org/907849) remove default implementation + virtual scoped_refptr dtls_transport() const; + + // The list of streams that `track` is associated with. This is the same as + // the [[AssociatedRemoteMediaStreams]] internal slot in the spec. + // https://w3c.github.io/webrtc-pc/#dfn-associatedremotemediastreams + // TODO(hbos): Make pure virtual as soon as Chromium's mock implements this. + // TODO(https://crbug.com/webrtc/9480): Remove streams() in favor of + // stream_ids() as soon as downstream projects are no longer dependent on + // stream objects. + virtual std::vector stream_ids() const; + virtual std::vector> streams() const; + + // Audio or video receiver? + virtual webrtc::MediaType media_type() const = 0; + + // Not to be confused with "mid", this is a field we can temporarily use + // to uniquely identify a receiver until we implement Unified Plan SDP. + virtual std::string id() const = 0; + + // The WebRTC specification only defines RTCRtpParameters in terms of senders, + // but this API also applies them to receivers, similar to ORTC: + // http://ortc.org/wp-content/uploads/2016/03/ortc.html#rtcrtpparameters*. + virtual RtpParameters GetParameters() const = 0; + // TODO(dinosaurav): Delete SetParameters entirely after rolling to Chromium. + // Currently, doesn't support changing any parameters. + virtual bool SetParameters(const RtpParameters& /* parameters */) { + return false; + } + + // Does not take ownership of observer. + // Must call SetObserver(nullptr) before the observer is destroyed. + virtual void SetObserver(RtpReceiverObserverInterface* observer) = 0; + + // Sets the jitter buffer minimum delay until media playout. Actual observed + // delay may differ depending on the congestion control. `delay_seconds` is a + // positive value including 0.0 measured in seconds. `nullopt` means default + // value must be used. + virtual void SetJitterBufferMinimumDelay( + std::optional delay_seconds) = 0; + + // TODO(zhihuang): Remove the default implementation once the subclasses + // implement this. Currently, the only relevant subclass is the + // content::FakeRtpReceiver in Chromium. + virtual std::vector GetSources() const; + + // Sets a user defined frame decryptor that will decrypt the entire frame + // before it is sent across the network. This will decrypt the entire frame + // using the user provided decryption mechanism regardless of whether SRTP is + // enabled or not. + // TODO(bugs.webrtc.org/12772): Remove. + virtual void SetFrameDecryptor( + scoped_refptr frame_decryptor); + + // Returns a pointer to the frame decryptor set previously by the + // user. This can be used to update the state of the object. + // TODO(bugs.webrtc.org/12772): Remove. + virtual scoped_refptr GetFrameDecryptor() const; + + // Sets a frame transformer between the depacketizer and the decoder to enable + // client code to transform received frames according to their own processing + // logic. + // TODO: bugs.webrtc.org/15929 - add [[deprecated("Use SetFrameTransformer")]] + // when usage in Chrome is removed + virtual void SetDepacketizerToDecoderFrameTransformer( + scoped_refptr frame_transformer) { + SetFrameTransformer(std::move(frame_transformer)); + } + + // Default implementation of SetFrameTransformer. + // TODO: bugs.webrtc.org/15929 - Make pure virtual. + void SetFrameTransformer( + scoped_refptr frame_transformer) override; + + protected: + ~RtpReceiverInterface() override = default; +}; + +} // namespace webrtc + +#endif // API_RTP_RECEIVER_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/rtp_sender_interface.h b/pkg/apm/webrtc/api/rtp_sender_interface.h new file mode 100644 index 00000000..478e15d5 --- /dev/null +++ b/pkg/apm/webrtc/api/rtp_sender_interface.h @@ -0,0 +1,145 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file contains interfaces for RtpSenders +// http://w3c.github.io/webrtc-pc/#rtcrtpsender-interface + +#ifndef API_RTP_SENDER_INTERFACE_H_ +#define API_RTP_SENDER_INTERFACE_H_ + +#include +#include +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "api/crypto/frame_encryptor_interface.h" +#include "api/dtls_transport_interface.h" +#include "api/dtmf_sender_interface.h" +#include "api/frame_transformer_interface.h" +#include "api/media_stream_interface.h" +#include "api/media_types.h" +#include "api/ref_count.h" +#include "api/rtc_error.h" +#include "api/rtp_parameters.h" +#include "api/scoped_refptr.h" +#include "api/video_codecs/video_encoder_factory.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +class RtpSenderObserverInterface { + public: + // The observer is called when the first media packet is sent for the observed + // sender. It is called immediately if the first packet was already sent. + virtual void OnFirstPacketSent(webrtc::MediaType media_type) = 0; + + protected: + virtual ~RtpSenderObserverInterface() {} +}; + +using SetParametersCallback = absl::AnyInvocable; + +class RTC_EXPORT RtpSenderInterface : public webrtc::RefCountInterface, + public FrameTransformerHost { + public: + // Returns true if successful in setting the track. + // Fails if an audio track is set on a video RtpSender, or vice-versa. + virtual bool SetTrack(MediaStreamTrackInterface* track) = 0; + virtual scoped_refptr track() const = 0; + + // The dtlsTransport attribute exposes the DTLS transport on which the + // media is sent. It may be null. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtpsender-transport + virtual scoped_refptr dtls_transport() const = 0; + + // Returns primary SSRC used by this sender for sending media. + // Returns 0 if not yet determined. + // TODO(deadbeef): Change to std::optional. + // TODO(deadbeef): Remove? With GetParameters this should be redundant. + virtual uint32_t ssrc() const = 0; + + // Audio or video sender? + virtual webrtc::MediaType media_type() const = 0; + + // Not to be confused with "mid", this is a field we can temporarily use + // to uniquely identify a receiver until we implement Unified Plan SDP. + virtual std::string id() const = 0; + + // Returns a list of media stream ids associated with this sender's track. + // These are signalled in the SDP so that the remote side can associate + // tracks. + virtual std::vector stream_ids() const = 0; + + // Sets the IDs of the media streams associated with this sender's track. + // These are signalled in the SDP so that the remote side can associate + // tracks. + virtual void SetStreams(const std::vector& stream_ids) = 0; + + // Returns the list of encoding parameters that will be applied when the SDP + // local description is set. These initial encoding parameters can be set by + // PeerConnection::AddTransceiver, and later updated with Get/SetParameters. + // TODO(orphis): Make it pure virtual once Chrome has updated + virtual std::vector init_send_encodings() const = 0; + + virtual RtpParameters GetParameters() const = 0; + // Note that only a subset of the parameters can currently be changed. See + // rtpparameters.h + // The encodings are in increasing quality order for simulcast. + virtual RTCError SetParameters(const RtpParameters& parameters) = 0; + virtual void SetParametersAsync(const RtpParameters& parameters, + SetParametersCallback callback); + + // Sets an observer which gets a callback when the first media packet is sent + // for this sender. + // Does not take ownership of observer. + // Must call SetObserver(nullptr) before the observer is destroyed. + virtual void SetObserver(RtpSenderObserverInterface* /* observer */) {} + + // Returns null for a video sender. + virtual scoped_refptr GetDtmfSender() const = 0; + + // Sets a user defined frame encryptor that will encrypt the entire frame + // before it is sent across the network. This will encrypt the entire frame + // using the user provided encryption mechanism regardless of whether SRTP is + // enabled or not. + virtual void SetFrameEncryptor( + scoped_refptr frame_encryptor) = 0; + + // Returns a pointer to the frame encryptor set previously by the + // user. This can be used to update the state of the object. + virtual scoped_refptr GetFrameEncryptor() const = 0; + + // TODO: bugs.webrtc.org/15929 - add [[deprecated("Use SetFrameTransformer")]] + // when usage in Chrome is removed + virtual void SetEncoderToPacketizerFrameTransformer( + scoped_refptr frame_transformer) { + SetFrameTransformer(std::move(frame_transformer)); + } + + // Sets a user defined encoder selector. + // Overrides selector that is (optionally) provided by VideoEncoderFactory. + virtual void SetEncoderSelector( + std::unique_ptr + encoder_selector) = 0; + + // Default implementation of SetFrameTransformer. + // TODO: bugs.webrtc.org/15929 - remove when all implementations are good + void SetFrameTransformer(scoped_refptr + /* frame_transformer */) override {} + + protected: + ~RtpSenderInterface() override = default; +}; + +} // namespace webrtc + +#endif // API_RTP_SENDER_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/rtp_transceiver_direction.h b/pkg/apm/webrtc/api/rtp_transceiver_direction.h new file mode 100644 index 00000000..3c7d4cb0 --- /dev/null +++ b/pkg/apm/webrtc/api/rtp_transceiver_direction.h @@ -0,0 +1,27 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_RTP_TRANSCEIVER_DIRECTION_H_ +#define API_RTP_TRANSCEIVER_DIRECTION_H_ + +namespace webrtc { + +// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiverdirection +enum class RtpTransceiverDirection { + kSendRecv, + kSendOnly, + kRecvOnly, + kInactive, + kStopped, +}; + +} // namespace webrtc + +#endif // API_RTP_TRANSCEIVER_DIRECTION_H_ diff --git a/pkg/apm/webrtc/api/rtp_transceiver_interface.h b/pkg/apm/webrtc/api/rtp_transceiver_interface.h new file mode 100644 index 00000000..e35c5b72 --- /dev/null +++ b/pkg/apm/webrtc/api/rtp_transceiver_interface.h @@ -0,0 +1,177 @@ +/* + * 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_RTP_TRANSCEIVER_INTERFACE_H_ +#define API_RTP_TRANSCEIVER_INTERFACE_H_ + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "api/array_view.h" +#include "api/media_types.h" +#include "api/ref_count.h" +#include "api/rtc_error.h" +#include "api/rtp_parameters.h" +#include "api/rtp_receiver_interface.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_direction.h" +#include "api/scoped_refptr.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Structure for initializing an RtpTransceiver in a call to +// PeerConnectionInterface::AddTransceiver. +// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiverinit +struct RTC_EXPORT RtpTransceiverInit final { + RtpTransceiverInit(); + RtpTransceiverInit(const RtpTransceiverInit&); + ~RtpTransceiverInit(); + // Direction of the RtpTransceiver. See RtpTransceiverInterface::direction(). + RtpTransceiverDirection direction = RtpTransceiverDirection::kSendRecv; + + // The added RtpTransceiver will be added to these streams. + std::vector stream_ids; + + std::vector send_encodings; +}; + +// The RtpTransceiverInterface maps to the RTCRtpTransceiver defined by the +// WebRTC specification. A transceiver represents a combination of an RtpSender +// and an RtpReceiver than share a common mid. As defined in JSEP, an +// RtpTransceiver is said to be associated with a media description if its mid +// property is non-null; otherwise, it is said to be disassociated. +// JSEP: https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24 +// +// Note that RtpTransceivers are only supported when using PeerConnection with +// Unified Plan SDP. +// +// This class is thread-safe. +// +// WebRTC specification for RTCRtpTransceiver, the JavaScript analog: +// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver +class RTC_EXPORT RtpTransceiverInterface : public webrtc::RefCountInterface { + public: + // Media type of the transceiver. Any sender(s)/receiver(s) will have this + // type as well. + virtual webrtc::MediaType media_type() const = 0; + + // The mid attribute is the mid negotiated and present in the local and + // remote descriptions. Before negotiation is complete, the mid value may be + // null. After rollbacks, the value may change from a non-null value to null. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-mid + virtual std::optional mid() const = 0; + + // The sender attribute exposes the RtpSender corresponding to the RTP media + // that may be sent with the transceiver's mid. The sender is always present, + // regardless of the direction of media. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-sender + virtual scoped_refptr sender() const = 0; + + // The receiver attribute exposes the RtpReceiver corresponding to the RTP + // media that may be received with the transceiver's mid. The receiver is + // always present, regardless of the direction of media. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-receiver + virtual scoped_refptr receiver() const = 0; + + // The stopped attribute indicates that the sender of this transceiver will no + // longer send, and that the receiver will no longer receive. It is true if + // either stop has been called or if setting the local or remote description + // has caused the RtpTransceiver to be stopped. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-stopped + virtual bool stopped() const = 0; + + // The stopping attribute indicates that the user has indicated that the + // sender of this transceiver will stop sending, and that the receiver will + // no longer receive. It is always true if stopped() is true. + // If stopping() is true and stopped() is false, it means that the + // transceiver's stop() method has been called, but the negotiation with + // the other end for shutting down the transceiver is not yet done. + // https://w3c.github.io/webrtc-pc/#dfn-stopping-0 + virtual bool stopping() const = 0; + + // The direction attribute indicates the preferred direction of this + // transceiver, which will be used in calls to CreateOffer and CreateAnswer. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-direction + virtual RtpTransceiverDirection direction() const = 0; + + // Sets the preferred direction of this transceiver. An update of + // directionality does not take effect immediately. Instead, future calls to + // CreateOffer and CreateAnswer mark the corresponding media descriptions as + // sendrecv, sendonly, recvonly, or inactive. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-direction + // TODO(hta): Deprecate SetDirection without error and rename + // SetDirectionWithError to SetDirection, remove default implementations. + ABSL_DEPRECATED("Use SetDirectionWithError instead") + virtual void SetDirection(RtpTransceiverDirection new_direction); + virtual RTCError SetDirectionWithError(RtpTransceiverDirection new_direction); + + // The current_direction attribute indicates the current direction negotiated + // for this transceiver. If this transceiver has never been represented in an + // offer/answer exchange, or if the transceiver is stopped, the value is null. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-currentdirection + virtual std::optional current_direction() const = 0; + + // An internal slot designating for which direction the relevant + // PeerConnection events have been fired. This is to ensure that events like + // OnAddTrack only get fired once even if the same session description is + // applied again. + // Exposed in the public interface for use by Chromium. + virtual std::optional fired_direction() const; + + // Initiates a stop of the transceiver. + // The stop is complete when stopped() returns true. + // A stopped transceiver can be reused for a different track. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-stop + // TODO(hta): Rename to Stop() when users of the non-standard Stop() are + // updated. + virtual RTCError StopStandard(); + + // Stops a transceiver immediately, without waiting for signalling. + // This is an internal function, and is exposed for historical reasons. + // https://w3c.github.io/webrtc-pc/#dfn-stop-the-rtcrtptransceiver + virtual void StopInternal(); + ABSL_DEPRECATED("Use StopStandard instead") virtual void Stop(); + + // The SetCodecPreferences method overrides the default codec preferences used + // by WebRTC for this transceiver. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-setcodecpreferences + virtual RTCError SetCodecPreferences( + ArrayView codecs) = 0; + virtual std::vector codec_preferences() const = 0; + + // Returns the set of header extensions that was set + // with SetHeaderExtensionsToNegotiate, or a default set if it has not been + // called. + // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface + virtual std::vector + GetHeaderExtensionsToNegotiate() const = 0; + + // Returns either the empty set if negotation has not yet + // happened, or a vector of the negotiated header extensions. + // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface + virtual std::vector + GetNegotiatedHeaderExtensions() const = 0; + + // The SetHeaderExtensionsToNegotiate method modifies the next SDP negotiation + // so that it negotiates use of header extensions which are not kStopped. + // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface + virtual webrtc::RTCError SetHeaderExtensionsToNegotiate( + ArrayView header_extensions) = 0; + + protected: + ~RtpTransceiverInterface() override = default; +}; + +} // namespace webrtc + +#endif // API_RTP_TRANSCEIVER_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/scoped_refptr.h b/pkg/apm/webrtc/api/scoped_refptr.h new file mode 100644 index 00000000..f5336704 --- /dev/null +++ b/pkg/apm/webrtc/api/scoped_refptr.h @@ -0,0 +1,230 @@ +/* + * 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. + */ + +// Originally these classes are from Chromium. +// http://src.chromium.org/viewvc/chrome/trunk/src/base/memory/ref_counted.h?view=markup + +// +// A smart pointer class for reference counted objects. Use this class instead +// of calling AddRef and Release manually on a reference counted object to +// avoid common memory leaks caused by forgetting to Release an object +// reference. Sample usage: +// +// class MyFoo : public RefCounted { +// ... +// }; +// +// void some_function() { +// scoped_refptr foo = make_ref_counted(); +// foo->Method(param); +// // `foo` is released when this function returns +// } +// +// void some_other_function() { +// scoped_refptr foo = make_ref_counted(); +// ... +// foo = nullptr; // explicitly releases `foo` +// ... +// if (foo) +// foo->Method(param); +// } +// +// The above examples show how scoped_refptr acts like a pointer to T. +// Given two scoped_refptr classes, it is also possible to exchange +// references between the two objects, like so: +// +// { +// scoped_refptr a = make_ref_counted(); +// scoped_refptr b; +// +// b.swap(a); +// // now, `b` references the MyFoo object, and `a` references null. +// } +// +// To make both `a` and `b` in the above example reference the same MyFoo +// object, simply use the assignment operator: +// +// { +// scoped_refptr a = make_ref_counted(); +// scoped_refptr b; +// +// b = a; +// // now, `a` and `b` each own a reference to the same MyFoo object. +// } +// + +#ifndef API_SCOPED_REFPTR_H_ +#define API_SCOPED_REFPTR_H_ + +#include +#include + +#include "absl/base/nullability.h" + +namespace webrtc { + +template +class ABSL_NULLABILITY_COMPATIBLE scoped_refptr { + public: + using element_type = T; + + scoped_refptr() : ptr_(nullptr) {} + scoped_refptr(std::nullptr_t) : ptr_(nullptr) {} // NOLINT(runtime/explicit) + + explicit scoped_refptr(T* absl_nullable p) : ptr_(p) { + if (ptr_) + ptr_->AddRef(); + } + + scoped_refptr(const scoped_refptr& r) : ptr_(r.ptr_) { + if (ptr_) + ptr_->AddRef(); + } + + template + scoped_refptr(const scoped_refptr& r) : ptr_(r.get()) { + if (ptr_) + ptr_->AddRef(); + } + + // Move constructors. + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.release()) {} + + template + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.release()) {} + + ~scoped_refptr() { + if (ptr_) + ptr_->Release(); + } + + T* get() 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 + // null pointer, all without touching the reference count of the underlying + // pointed-to object. The object is still reference counted, and the caller of + // release() is now the proud owner of one reference, so it is responsible for + // calling Release() once on the object when no longer using it. + T* release() { + T* retVal = ptr_; + ptr_ = nullptr; + return retVal; + } + + scoped_refptr& operator=(T* absl_nullable p) { + // AddRef first so that self assignment should work + if (p) + p->AddRef(); + if (ptr_) + ptr_->Release(); + ptr_ = p; + return *this; + } + + scoped_refptr& operator=(const scoped_refptr& r) { + return *this = r.ptr_; + } + + template + scoped_refptr& operator=(const scoped_refptr& r) { + return *this = r.get(); + } + + scoped_refptr& operator=(scoped_refptr&& r) noexcept { + scoped_refptr(std::move(r)).swap(*this); + return *this; + } + + template + scoped_refptr& operator=(scoped_refptr&& r) noexcept { + scoped_refptr(std::move(r)).swap(*this); + return *this; + } + + void swap(T** absl_nonnull pp) noexcept { + T* p = ptr_; + ptr_ = *pp; + *pp = p; + } + + void swap(scoped_refptr& r) noexcept { swap(&r.ptr_); } + + protected: + T* ptr_; +}; + +template +bool operator==(const scoped_refptr& a, const scoped_refptr& b) { + return a.get() == b.get(); +} +template +bool operator!=(const scoped_refptr& a, const scoped_refptr& b) { + return !(a == b); +} + +template +bool operator==(const scoped_refptr& a, std::nullptr_t) { + return a.get() == nullptr; +} + +template +bool operator!=(const scoped_refptr& a, std::nullptr_t) { + return !(a == nullptr); +} + +template +bool operator==(std::nullptr_t, const scoped_refptr& a) { + return a.get() == nullptr; +} + +template +bool operator!=(std::nullptr_t, const scoped_refptr& a) { + return !(a == nullptr); +} + +// Comparison with raw pointer. +template +bool operator==(const scoped_refptr& a, const U* b) { + return a.get() == b; +} +template +bool operator!=(const scoped_refptr& a, const U* b) { + return !(a == b); +} + +template +bool operator==(const T* a, const scoped_refptr& b) { + return a == b.get(); +} +template +bool operator!=(const T* a, const scoped_refptr& b) { + return !(a == b); +} + +// Ordered comparison, needed for use as a std::map key. +template +bool operator<(const scoped_refptr& a, const scoped_refptr& b) { + return a.get() < b.get(); +} + +} // namespace webrtc + +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +// Backwards compatible alias. +// TODO: bugs.webrtc.org/42225969 - Deprecate and remove. +using ::webrtc::scoped_refptr; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // API_SCOPED_REFPTR_H_ diff --git a/pkg/apm/webrtc/api/sctp_transport_interface.h b/pkg/apm/webrtc/api/sctp_transport_interface.h new file mode 100644 index 00000000..a9fd1159 --- /dev/null +++ b/pkg/apm/webrtc/api/sctp_transport_interface.h @@ -0,0 +1,112 @@ +/* + * 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_SCTP_TRANSPORT_INTERFACE_H_ +#define API_SCTP_TRANSPORT_INTERFACE_H_ + +#include + +#include "api/dtls_transport_interface.h" +#include "api/ref_count.h" +#include "api/scoped_refptr.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// States of a SCTP transport, corresponding to the JS API specification. +// http://w3c.github.io/webrtc-pc/#dom-rtcsctptransportstate +enum class SctpTransportState { + kNew, // Has not started negotiating yet. Non-standard state. + kConnecting, // In the process of negotiating an association. + kConnected, // Completed negotiation of an association. + kClosed, // Closed by local or remote party. + kNumValues +}; + +// This object gives snapshot information about the changeable state of a +// SctpTransport. +// It reflects the readonly attributes of the object in the specification. +// http://w3c.github.io/webrtc-pc/#rtcsctptransport-interface +class RTC_EXPORT SctpTransportInformation { + public: + SctpTransportInformation() = default; + SctpTransportInformation(const SctpTransportInformation&) = default; + explicit SctpTransportInformation(SctpTransportState state); + SctpTransportInformation(SctpTransportState state, + scoped_refptr dtls_transport, + std::optional max_message_size, + std::optional max_channels); + ~SctpTransportInformation(); + // The DTLS transport that supports this SCTP transport. + scoped_refptr dtls_transport() const { + return dtls_transport_; + } + SctpTransportState state() const { return state_; } + std::optional MaxMessageSize() const { return max_message_size_; } + std::optional MaxChannels() const { return max_channels_; } + + private: + SctpTransportState state_ = SctpTransportState::kNew; + scoped_refptr dtls_transport_; + std::optional max_message_size_; + std::optional max_channels_; +}; + +class SctpTransportObserverInterface { + public: + // This callback carries information about the state of the transport. + // The argument is a pass-by-value snapshot of the state. + // The callback will be called on the network thread. + virtual void OnStateChange(SctpTransportInformation info) = 0; + + protected: + virtual ~SctpTransportObserverInterface() = default; +}; + +// A SCTP transport, as represented to the outside world. +// This object is created on the network thread, and can only be +// accessed on that thread, except for functions explicitly marked otherwise. +// References can be held by other threads, and destruction can therefore +// be initiated by other threads. +class SctpTransportInterface : public webrtc::RefCountInterface { + public: + // This function can be called from other threads. + virtual scoped_refptr dtls_transport() const = 0; + // Returns information on the state of the SctpTransport. + // This function can be called from other threads. + virtual SctpTransportInformation Information() const = 0; + // Observer management. + virtual void RegisterObserver(SctpTransportObserverInterface* observer) = 0; + virtual void UnregisterObserver() = 0; +}; + +// The size of the SCTP association send buffer. 256kB, the usrsctp default. +constexpr int kSctpSendBufferSize = 256 * 1024; + +// SCTP options negotiated in the SDP. +struct SctpOptions { + // https://www.rfc-editor.org/rfc/rfc8841.html#name-sctp-port + // `local_port` and `remote_port` are passed along the wire and the + // listener and connector must be using the same port. They are not related + // to the ports at the IP level. If set to -1 we default to + // kSctpDefaultPort. + // TODO(bugs.webrtc.org/402429107): make these optional. + int local_port = -1; + int remote_port = -1; + + // https://www.rfc-editor.org/rfc/rfc8841.html#name-max-message-size + // `max_message_size` sets the maxium message size on the connection. + // It must be smaller than or equal to kSctpSendBufferSize. + int max_message_size = kSctpSendBufferSize; +}; + +} // namespace webrtc + +#endif // API_SCTP_TRANSPORT_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/sequence_checker.h b/pkg/apm/webrtc/api/sequence_checker.h new file mode 100644 index 00000000..0d2d8380 --- /dev/null +++ b/pkg/apm/webrtc/api/sequence_checker.h @@ -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: +// webrtc::TaskQueueBase& Queue() { return encoder_queue_; } +// void Encode() { +// RTC_DCHECK_RUN_ON(&encoder_queue_); +// DoSomething(var_); +// } +// +// private: +// webrtc::TaskQueueBase& encoder_queue_; +// Frame var_ RTC_GUARDED_BY(encoder_queue_); +// }; +// +// void Encode() { +// // Will fail at runtime when DCHECK is enabled: +// // encoder_->Encode(); +// // Will work: +// webrtc::scoped_refptr encoder = encoder_; +// encoder_->Queue().PostTask([encoder] { encoder->Encode(); }); +// } +// +// private: +// webrtc::scoped_refptr 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 annotates for the thread safety analyzer the check was done. +// Second statement validates it is running on the sequence `x`. +// 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_ASSERT_EXCLUSIVE_LOCK(x) {}(); \ + RTC_DCHECK((x)->IsCurrent()) \ + << webrtc::webrtc_sequence_checker_internal::ExpectationToString(x); + +#endif // API_SEQUENCE_CHECKER_H_ diff --git a/pkg/apm/webrtc/api/set_local_description_observer_interface.h b/pkg/apm/webrtc/api/set_local_description_observer_interface.h new file mode 100644 index 00000000..40f85b94 --- /dev/null +++ b/pkg/apm/webrtc/api/set_local_description_observer_interface.h @@ -0,0 +1,30 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_SET_LOCAL_DESCRIPTION_OBSERVER_INTERFACE_H_ +#define API_SET_LOCAL_DESCRIPTION_OBSERVER_INTERFACE_H_ + +#include "api/ref_count.h" +#include "api/rtc_error.h" + +namespace webrtc { + +// OnSetLocalDescriptionComplete() invokes as soon as +// PeerConnectionInterface::SetLocalDescription() operation completes, allowing +// the observer to examine the effects of the operation without delay. +class SetLocalDescriptionObserverInterface : public webrtc::RefCountInterface { + public: + // On success, `error.ok()` is true. + virtual void OnSetLocalDescriptionComplete(RTCError error) = 0; +}; + +} // namespace webrtc + +#endif // API_SET_LOCAL_DESCRIPTION_OBSERVER_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/set_remote_description_observer_interface.h b/pkg/apm/webrtc/api/set_remote_description_observer_interface.h new file mode 100644 index 00000000..c1625410 --- /dev/null +++ b/pkg/apm/webrtc/api/set_remote_description_observer_interface.h @@ -0,0 +1,31 @@ +/* + * 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_SET_REMOTE_DESCRIPTION_OBSERVER_INTERFACE_H_ +#define API_SET_REMOTE_DESCRIPTION_OBSERVER_INTERFACE_H_ + +#include "api/ref_count.h" +#include "api/rtc_error.h" + +namespace webrtc { + +// An observer for PeerConnectionInterface::SetRemoteDescription(). The +// callback is invoked such that the state of the peer connection can be +// examined to accurately reflect the effects of the SetRemoteDescription +// operation. +class SetRemoteDescriptionObserverInterface : public webrtc::RefCountInterface { + public: + // On success, `error.ok()` is true. + virtual void OnSetRemoteDescriptionComplete(RTCError error) = 0; +}; + +} // namespace webrtc + +#endif // API_SET_REMOTE_DESCRIPTION_OBSERVER_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/stats/attribute.h b/pkg/apm/webrtc/api/stats/attribute.h new file mode 100644 index 00000000..931a3d09 --- /dev/null +++ b/pkg/apm/webrtc/api/stats/attribute.h @@ -0,0 +1,94 @@ +/* + * Copyright 2024 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_STATS_ATTRIBUTE_H_ +#define API_STATS_ATTRIBUTE_H_ + +#include +#include +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// A light-weight wrapper of an RTCStats attribute, i.e. an individual metric of +// type std::optional. +class RTC_EXPORT Attribute { + public: + // All supported attribute types. + typedef std::variant*, + const std::optional*, + const std::optional*, + const std::optional*, + const std::optional*, + const std::optional*, + const std::optional*, + const std::optional>*, + const std::optional>*, + const std::optional>*, + const std::optional>*, + const std::optional>*, + const std::optional>*, + const std::optional>*, + const std::optional>*, + const std::optional>*> + StatVariant; + + template + Attribute(const char* name, const std::optional* attribute) + : name_(name), attribute_(attribute) {} + + const char* name() const; + const StatVariant& as_variant() const; + + bool has_value() const; + template + bool holds_alternative() const { + return std::holds_alternative*>(attribute_); + } + template + const std::optional& as_optional() const { + RTC_CHECK(holds_alternative()); + return *std::get*>(attribute_); + } + template + const T& get() const { + RTC_CHECK(holds_alternative()); + RTC_CHECK(has_value()); + return std::get*>(attribute_)->value(); + } + + bool is_sequence() const; + bool is_string() const; + std::string ToString() const; + + bool operator==(const Attribute& other) const; + bool operator!=(const Attribute& other) const; + + private: + const char* name_; + StatVariant attribute_; +}; + +struct RTC_EXPORT AttributeInit { + AttributeInit(const char* name, const Attribute::StatVariant& variant); + + const char* name; + Attribute::StatVariant variant; +}; + +} // namespace webrtc + +#endif // API_STATS_ATTRIBUTE_H_ diff --git a/pkg/apm/webrtc/api/stats/rtc_stats.h b/pkg/apm/webrtc/api/stats/rtc_stats.h new file mode 100644 index 00000000..b8070877 --- /dev/null +++ b/pkg/apm/webrtc/api/stats/rtc_stats.h @@ -0,0 +1,197 @@ +/* + * Copyright 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. + */ + +#ifndef API_STATS_RTC_STATS_H_ +#define API_STATS_RTC_STATS_H_ + +#include +#include + +#include +#include +#include +#include + +#include "api/stats/attribute.h" +#include "api/units/timestamp.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Abstract base class for RTCStats-derived dictionaries, see +// https://w3c.github.io/webrtc-stats/. +// +// All derived classes must have the following static variable defined: +// static const char kType[]; +// It is used as a unique class identifier and a string representation of the +// class type, see https://w3c.github.io/webrtc-stats/#rtcstatstype-str*. +// Use the `WEBRTC_RTCSTATS_IMPL` macro when implementing subclasses, see macro +// for details. +// +// Derived classes list their dictionary attributes, std::optional, as +// public fields, allowing the following: +// +// RTCFooStats foo("fooId", Timestamp::Micros(GetCurrentTime())); +// foo.bar = 42; +// foo.baz = std::vector(); +// foo.baz->push_back("hello world"); +// uint32_t x = *foo.bar; +// +// Pointers to all the attributes are available with `Attributes()`, allowing +// iteration: +// +// for (const auto& attribute : foo.Attributes()) { +// printf("%s = %s\n", attribute.name(), attribute.ToString().c_str()); +// } +class RTC_EXPORT RTCStats { + public: + RTCStats(const std::string& id, Timestamp timestamp) + : id_(id), timestamp_(timestamp) {} + RTCStats(const RTCStats& other); + virtual ~RTCStats(); + + virtual std::unique_ptr copy() const = 0; + + const std::string& id() const { return id_; } + // Time relative to the UNIX epoch (Jan 1, 1970, UTC), in microseconds. + Timestamp timestamp() const { return timestamp_; } + void set_timestamp(Timestamp timestamp) { timestamp_ = timestamp; } + + // Returns the static member variable `kType` of the implementing class. + virtual const char* type() const = 0; + // Returns all attributes of this stats object, i.e. a list of its individual + // metrics as viewed via the Attribute wrapper. + std::vector Attributes() const; + template + Attribute GetAttribute(const std::optional& stat) const { + for (const auto& attribute : Attributes()) { + if (!attribute.holds_alternative()) { + continue; + } + if (std::get*>(attribute.as_variant()) == &stat) { + return attribute; + } + } + RTC_CHECK_NOTREACHED(); + } + // Checks if the two stats objects are of the same type and have the same + // attribute values. Timestamps are not compared. These operators are exposed + // for testing. + bool operator==(const RTCStats& other) const; + bool operator!=(const RTCStats& other) const; + + // Creates a JSON readable string representation of the stats + // object, listing all of its attributes (names and values). + std::string ToJson() const; + + // Downcasts the stats object to an `RTCStats` subclass `T`. DCHECKs that the + // object is of type `T`. + template + const T& cast_to() const { + RTC_DCHECK_EQ(type(), T::kType); + return static_cast(*this); + } + + protected: + virtual std::vector AttributesImpl( + size_t additional_capacity) const; + + std::string id_; + Timestamp timestamp_; +}; + +// All `RTCStats` classes should use these macros. +// `WEBRTC_RTCSTATS_DECL` is placed in a public section of the class definition. +// `WEBRTC_RTCSTATS_IMPL` is placed outside the class definition (in a .cc). +// +// These macros declare (in _DECL) and define (in _IMPL) the static `kType` and +// overrides methods as required by subclasses of `RTCStats`: `copy`, `type` and +// `AttributesImpl`. The |...| argument is a list of addresses to each attribute +// defined in the implementing class. The list must have at least one attribute. +// +// (Since class names need to be known to implement these methods this cannot be +// part of the base `RTCStats`. While these methods could be implemented using +// templates, that would only work for immediate subclasses. Subclasses of +// subclasses also have to override these methods, resulting in boilerplate +// code. Using a macro avoids this and works for any `RTCStats` class, including +// grandchildren.) +// +// Sample usage: +// +// rtcfoostats.h: +// class RTCFooStats : public RTCStats { +// public: +// WEBRTC_RTCSTATS_DECL(); +// +// RTCFooStats(const std::string& id, Timestamp timestamp); +// +// std::optional foo; +// std::optional bar; +// }; +// +// rtcfoostats.cc: +// WEBRTC_RTCSTATS_IMPL(RTCFooStats, RTCStats, "foo-stats" +// &foo, +// &bar); +// +// RTCFooStats::RTCFooStats(const std::string& id, Timestamp timestamp) +// : RTCStats(id, timestamp), +// foo("foo"), +// bar("bar") { +// } +// +#define WEBRTC_RTCSTATS_DECL(SelfT) \ + protected: \ + std::vector AttributesImpl(size_t additional_capacity) \ + const override; \ + \ + public: \ + static const char kType[]; \ + \ + template \ + friend void AbslStringify(Sink& sink, const SelfT& stats) { \ + sink.Append(stats.ToJson()); \ + } \ + \ + std::unique_ptr copy() const override; \ + const char* type() const override + +#define WEBRTC_RTCSTATS_IMPL(this_class, parent_class, type_str, ...) \ + const char this_class::kType[] = type_str; \ + \ + std::unique_ptr this_class::copy() const { \ + return std::make_unique(*this); \ + } \ + \ + const char* this_class::type() const { \ + return this_class::kType; \ + } \ + \ + std::vector this_class::AttributesImpl( \ + size_t additional_capacity) const { \ + webrtc::AttributeInit attribute_inits[] = {__VA_ARGS__}; \ + size_t attribute_inits_size = \ + sizeof(attribute_inits) / sizeof(attribute_inits[0]); \ + std::vector attributes = parent_class::AttributesImpl( \ + attribute_inits_size + additional_capacity); \ + for (size_t i = 0; i < attribute_inits_size; ++i) { \ + attributes.push_back(std::visit( \ + [&](const auto* field) { \ + return Attribute(attribute_inits[i].name, field); \ + }, \ + attribute_inits[i].variant)); \ + } \ + return attributes; \ + } + +} // namespace webrtc + +#endif // API_STATS_RTC_STATS_H_ diff --git a/pkg/apm/webrtc/api/stats/rtc_stats_collector_callback.h b/pkg/apm/webrtc/api/stats/rtc_stats_collector_callback.h new file mode 100644 index 00000000..90819a91 --- /dev/null +++ b/pkg/apm/webrtc/api/stats/rtc_stats_collector_callback.h @@ -0,0 +1,30 @@ +/* + * Copyright 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. + */ + +#ifndef API_STATS_RTC_STATS_COLLECTOR_CALLBACK_H_ +#define API_STATS_RTC_STATS_COLLECTOR_CALLBACK_H_ + +#include "api/ref_count.h" +#include "api/scoped_refptr.h" +#include "api/stats/rtc_stats_report.h" + +namespace webrtc { + +class RTCStatsCollectorCallback : public RefCountInterface { + public: + ~RTCStatsCollectorCallback() override = default; + + virtual void OnStatsDelivered( + const scoped_refptr& report) = 0; +}; + +} // namespace webrtc + +#endif // API_STATS_RTC_STATS_COLLECTOR_CALLBACK_H_ diff --git a/pkg/apm/webrtc/api/stats/rtc_stats_report.h b/pkg/apm/webrtc/api/stats/rtc_stats_report.h new file mode 100644 index 00000000..fdeec278 --- /dev/null +++ b/pkg/apm/webrtc/api/stats/rtc_stats_report.h @@ -0,0 +1,134 @@ +/* + * Copyright 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. + */ + +#ifndef API_STATS_RTC_STATS_REPORT_H_ +#define API_STATS_RTC_STATS_REPORT_H_ + +#include +#include + +#include +#include +#include +#include +#include + +#include "api/ref_counted_base.h" +#include "api/scoped_refptr.h" +#include "api/stats/rtc_stats.h" +#include "api/units/timestamp.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// A collection of stats. +// This is accessible as a map from `RTCStats::id` to `RTCStats`. +class RTC_EXPORT RTCStatsReport final + : public RefCountedNonVirtual { + public: + typedef std::map> StatsMap; + + class RTC_EXPORT ConstIterator { + public: + ConstIterator(ConstIterator&& other); + ~ConstIterator(); + + ConstIterator& operator++(); + ConstIterator& operator++(int); + const RTCStats& operator*() const; + const RTCStats* operator->() const; + bool operator==(const ConstIterator& other) const; + bool operator!=(const ConstIterator& other) const; + + private: + friend class RTCStatsReport; + ConstIterator(const scoped_refptr& report, + StatsMap::const_iterator it); + + // Reference report to make sure it is kept alive. + scoped_refptr report_; + StatsMap::const_iterator it_; + }; + + static scoped_refptr Create(Timestamp timestamp); + + explicit RTCStatsReport(Timestamp timestamp); + + RTCStatsReport(const RTCStatsReport& other) = delete; + scoped_refptr Copy() const; + + Timestamp timestamp() const { return timestamp_; } + void AddStats(std::unique_ptr stats); + // On success, returns a non-owning pointer to `stats`. If the stats ID is not + // unique, `stats` is not inserted and nullptr is returned. + template + T* TryAddStats(std::unique_ptr stats) { + T* stats_ptr = stats.get(); + if (!stats_ + .insert(std::make_pair(std::string(stats->id()), std::move(stats))) + .second) { + return nullptr; + } + return stats_ptr; + } + const RTCStats* Get(const std::string& id) const; + size_t size() const { return stats_.size(); } + + // Gets the stat object of type `T` by ID, where `T` is any class descending + // from `RTCStats`. + // Returns null if there is no stats object for the given ID or it is the + // wrong type. + template + const T* GetAs(const std::string& id) const { + const RTCStats* stats = Get(id); + if (!stats || stats->type() != T::kType) { + return nullptr; + } + return &stats->cast_to(); + } + + // Removes the stats object from the report, returning ownership of it or null + // if there is no object with `id`. + std::unique_ptr Take(const std::string& id); + // Takes ownership of all the stats in `other`, leaving it empty. + void TakeMembersFrom(scoped_refptr other); + + // Stats iterators. Stats are ordered lexicographically on `RTCStats::id`. + ConstIterator begin() const; + ConstIterator end() const; + + // Gets the subset of stats that are of type `T`, where `T` is any class + // descending from `RTCStats`. + template + std::vector GetStatsOfType() const { + std::vector stats_of_type; + for (const RTCStats& stats : *this) { + if (stats.type() == T::kType) + stats_of_type.push_back(&stats.cast_to()); + } + return stats_of_type; + } + + // Creates a JSON readable string representation of the report, + // listing all of its stats objects. + std::string ToJson() const; + + protected: + friend class RefCountedNonVirtual; + ~RTCStatsReport() = default; + + private: + Timestamp timestamp_; + StatsMap stats_; +}; + +} // namespace webrtc + +#endif // API_STATS_RTC_STATS_REPORT_H_ diff --git a/pkg/apm/webrtc/api/stats/rtcstats_objects.h b/pkg/apm/webrtc/api/stats/rtcstats_objects.h new file mode 100644 index 00000000..82acb453 --- /dev/null +++ b/pkg/apm/webrtc/api/stats/rtcstats_objects.h @@ -0,0 +1,488 @@ +/* + * Copyright 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. + */ + +#ifndef API_STATS_RTCSTATS_OBJECTS_H_ +#define API_STATS_RTCSTATS_OBJECTS_H_ + +#include + +#include +#include +#include +#include + +#include "api/stats/rtc_stats.h" +#include "api/units/timestamp.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// https://w3c.github.io/webrtc-stats/#certificatestats-dict* +class RTC_EXPORT RTCCertificateStats final : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(RTCCertificateStats); + RTCCertificateStats(std::string id, Timestamp timestamp); + ~RTCCertificateStats() override; + + std::optional fingerprint; + std::optional fingerprint_algorithm; + std::optional base64_certificate; + std::optional issuer_certificate_id; +}; + +// https://w3c.github.io/webrtc-stats/#codec-dict* +class RTC_EXPORT RTCCodecStats final : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(RTCCodecStats); + RTCCodecStats(std::string id, Timestamp timestamp); + ~RTCCodecStats() override; + + std::optional transport_id; + std::optional payload_type; + std::optional mime_type; + std::optional clock_rate; + std::optional channels; + std::optional sdp_fmtp_line; +}; + +// https://w3c.github.io/webrtc-stats/#dcstats-dict* +class RTC_EXPORT RTCDataChannelStats final : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(RTCDataChannelStats); + RTCDataChannelStats(std::string id, Timestamp timestamp); + ~RTCDataChannelStats() override; + + std::optional label; + std::optional protocol; + std::optional data_channel_identifier; + std::optional state; + std::optional messages_sent; + std::optional bytes_sent; + std::optional messages_received; + std::optional bytes_received; +}; + +// https://w3c.github.io/webrtc-stats/#candidatepair-dict* +class RTC_EXPORT RTCIceCandidatePairStats final : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(RTCIceCandidatePairStats); + RTCIceCandidatePairStats(std::string id, Timestamp timestamp); + ~RTCIceCandidatePairStats() override; + + std::optional transport_id; + std::optional local_candidate_id; + std::optional remote_candidate_id; + std::optional state; + // Obsolete: priority + std::optional priority; + std::optional nominated; + // `writable` does not exist in the spec and old comments suggest it used to + // exist but was incorrectly implemented. + // TODO(https://crbug.com/webrtc/14171): Standardize and/or modify + // implementation. + std::optional writable; + std::optional packets_sent; + std::optional packets_received; + std::optional bytes_sent; + std::optional bytes_received; + std::optional total_round_trip_time; + std::optional current_round_trip_time; + std::optional available_outgoing_bitrate; + std::optional available_incoming_bitrate; + std::optional requests_received; + std::optional requests_sent; + std::optional responses_received; + std::optional responses_sent; + std::optional consent_requests_sent; + std::optional packets_discarded_on_send; + std::optional bytes_discarded_on_send; + std::optional last_packet_received_timestamp; + std::optional last_packet_sent_timestamp; +}; + +// https://w3c.github.io/webrtc-stats/#icecandidate-dict* +class RTC_EXPORT RTCIceCandidateStats : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(RTCIceCandidateStats); + ~RTCIceCandidateStats() override; + + std::optional transport_id; + // Obsolete: is_remote + std::optional is_remote; + std::optional network_type; + std::optional ip; + std::optional address; + std::optional port; + std::optional protocol; + std::optional relay_protocol; + std::optional candidate_type; + std::optional priority; + std::optional url; + std::optional foundation; + std::optional related_address; + std::optional related_port; + std::optional username_fragment; + std::optional tcp_type; + + // The following metrics are NOT exposed to JavaScript. We should consider + // standardizing or removing them. + std::optional vpn; + std::optional network_adapter_type; + + protected: + RTCIceCandidateStats(std::string id, Timestamp timestamp, bool is_remote); +}; + +// In the spec both local and remote varieties are of type RTCIceCandidateStats. +// But here we define them as subclasses of `RTCIceCandidateStats` because the +// `kType` need to be different ("RTCStatsType type") in the local/remote case. +// https://w3c.github.io/webrtc-stats/#rtcstatstype-str* +// This forces us to have to override copy() and type(). +class RTC_EXPORT RTCLocalIceCandidateStats final : public RTCIceCandidateStats { + public: + static const char kType[]; + RTCLocalIceCandidateStats(std::string id, Timestamp timestamp); + std::unique_ptr copy() const override; + const char* type() const override; +}; + +class RTC_EXPORT RTCRemoteIceCandidateStats final + : public RTCIceCandidateStats { + public: + static const char kType[]; + RTCRemoteIceCandidateStats(std::string id, Timestamp timestamp); + std::unique_ptr copy() const override; + const char* type() const override; +}; + +// https://w3c.github.io/webrtc-stats/#pcstats-dict* +class RTC_EXPORT RTCPeerConnectionStats final : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(RTCPeerConnectionStats); + RTCPeerConnectionStats(std::string id, Timestamp timestamp); + ~RTCPeerConnectionStats() override; + + std::optional data_channels_opened; + std::optional data_channels_closed; +}; + +// https://w3c.github.io/webrtc-stats/#streamstats-dict* +class RTC_EXPORT RTCRtpStreamStats : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(RTCRtpStreamStats); + ~RTCRtpStreamStats() override; + + std::optional ssrc; + std::optional kind; + std::optional transport_id; + std::optional codec_id; + + protected: + RTCRtpStreamStats(std::string id, Timestamp timestamp); +}; + +// https://www.w3.org/TR/webrtc-stats/#receivedrtpstats-dict* +class RTC_EXPORT RTCReceivedRtpStreamStats : public RTCRtpStreamStats { + public: + WEBRTC_RTCSTATS_DECL(RTCReceivedRtpStreamStats); + ~RTCReceivedRtpStreamStats() override; + + std::optional jitter; + std::optional packets_lost; // Signed per RFC 3550 + + protected: + RTCReceivedRtpStreamStats(std::string id, Timestamp timestamp); +}; + +// https://www.w3.org/TR/webrtc-stats/#sentrtpstats-dict* +class RTC_EXPORT RTCSentRtpStreamStats : public RTCRtpStreamStats { + public: + WEBRTC_RTCSTATS_DECL(RTCSentRtpStreamStats); + ~RTCSentRtpStreamStats() override; + + std::optional packets_sent; + std::optional bytes_sent; + + protected: + RTCSentRtpStreamStats(std::string id, Timestamp timestamp); +}; + +// https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict* +class RTC_EXPORT RTCInboundRtpStreamStats final + : public RTCReceivedRtpStreamStats { + public: + WEBRTC_RTCSTATS_DECL(RTCInboundRtpStreamStats); + RTCInboundRtpStreamStats(std::string id, Timestamp timestamp); + ~RTCInboundRtpStreamStats() override; + + std::optional playout_id; + std::optional track_identifier; + std::optional mid; + std::optional remote_id; + std::optional packets_received; + std::optional packets_discarded; + std::optional fec_packets_received; + std::optional fec_bytes_received; + std::optional fec_packets_discarded; + // Inbound FEC SSRC. Only present if a mechanism like FlexFEC is negotiated. + std::optional fec_ssrc; + std::optional bytes_received; + std::optional header_bytes_received; + // Inbound RTX stats. Only defined when RTX is used and it is therefore + // possible to distinguish retransmissions. + std::optional retransmitted_packets_received; + std::optional retransmitted_bytes_received; + std::optional rtx_ssrc; + + std::optional last_packet_received_timestamp; + std::optional jitter_buffer_delay; + std::optional jitter_buffer_target_delay; + std::optional jitter_buffer_minimum_delay; + std::optional jitter_buffer_emitted_count; + std::optional total_samples_received; + std::optional concealed_samples; + std::optional silent_concealed_samples; + std::optional concealment_events; + std::optional inserted_samples_for_deceleration; + std::optional removed_samples_for_acceleration; + std::optional audio_level; + std::optional total_audio_energy; + std::optional total_samples_duration; + // Stats below are only implemented or defined for video. + std::optional frames_received; + std::optional frame_width; + std::optional frame_height; + std::optional frames_per_second; + std::optional frames_decoded; + std::optional key_frames_decoded; + std::optional frames_dropped; + std::optional total_decode_time; + std::optional total_processing_delay; + std::optional total_assembly_time; + std::optional frames_assembled_from_multiple_packets; + // TODO(https://crbug.com/webrtc/15600): Implement framesRendered, which is + // incremented at the same time that totalInterFrameDelay and + // totalSquaredInterFrameDelay is incremented. (Dividing inter-frame delay by + // framesDecoded is slightly wrong.) + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-framesrendered + // + // TODO(https://crbug.com/webrtc/15601): Inter-frame, pause and freeze metrics + // all related to when the frame is rendered, but our implementation measures + // at delivery to sink, not at actual render time. When we have an actual + // frame rendered callback, move the calculating of these metrics to there in + // order to make them more accurate. + std::optional total_inter_frame_delay; + std::optional total_squared_inter_frame_delay; + std::optional pause_count; + std::optional total_pauses_duration; + std::optional freeze_count; + std::optional total_freezes_duration; + // https://w3c.github.io/webrtc-provisional-stats/#dom-rtcinboundrtpstreamstats-contenttype + std::optional content_type; + // Only populated if audio/video sync is enabled. + // TODO(https://crbug.com/webrtc/14177): Expose even if A/V sync is off? + std::optional estimated_playout_timestamp; + // Only defined for video. + // In JavaScript, this is only exposed if HW exposure is allowed. + std::optional decoder_implementation; + // FIR and PLI counts are only defined for |kind == "video"|. + std::optional fir_count; + std::optional pli_count; + std::optional nack_count; + std::optional qp_sum; + std::optional total_corruption_probability; + std::optional total_squared_corruption_probability; + std::optional corruption_measurements; + // This is a remnant of the legacy getStats() API. When the "video-timing" + // header extension is used, + // https://webrtc.github.io/webrtc-org/experiments/rtp-hdrext/video-timing/, + // `googTimingFrameInfo` is exposed with the value of + // TimingFrameInfo::ToString(). + // TODO(https://crbug.com/webrtc/14586): Unship or standardize this metric. + std::optional goog_timing_frame_info; + // In JavaScript, this is only exposed if HW exposure is allowed. + std::optional power_efficient_decoder; + + // The following metrics are NOT exposed to JavaScript. We should consider + // standardizing or removing them. + std::optional jitter_buffer_flushes; + std::optional delayed_packet_outage_samples; + std::optional relative_packet_arrival_delay; + std::optional interruption_count; + std::optional total_interruption_duration; + std::optional min_playout_delay; +}; + +// https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict* +class RTC_EXPORT RTCOutboundRtpStreamStats final + : public RTCSentRtpStreamStats { + public: + WEBRTC_RTCSTATS_DECL(RTCOutboundRtpStreamStats); + RTCOutboundRtpStreamStats(std::string id, Timestamp timestamp); + ~RTCOutboundRtpStreamStats() override; + + std::optional media_source_id; + std::optional remote_id; + std::optional mid; + std::optional rid; + std::optional encoding_index; + std::optional retransmitted_packets_sent; + std::optional header_bytes_sent; + std::optional retransmitted_bytes_sent; + std::optional target_bitrate; + std::optional frames_encoded; + std::optional key_frames_encoded; + std::optional total_encode_time; + std::optional total_encoded_bytes_target; + std::optional frame_width; + std::optional frame_height; + std::optional frames_per_second; + std::optional frames_sent; + std::optional huge_frames_sent; + std::optional total_packet_send_delay; + std::optional quality_limitation_reason; + std::optional> quality_limitation_durations; + // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationresolutionchanges + std::optional quality_limitation_resolution_changes; + // https://w3c.github.io/webrtc-provisional-stats/#dom-rtcoutboundrtpstreamstats-contenttype + std::optional content_type; + // In JavaScript, this is only exposed if HW exposure is allowed. + // Only implemented for video. + // TODO(https://crbug.com/webrtc/14178): Implement for audio as well. + std::optional encoder_implementation; + // FIR and PLI counts are only defined for |kind == "video"|. + std::optional fir_count; + std::optional pli_count; + std::optional nack_count; + std::optional qp_sum; + std::optional active; + // In JavaScript, this is only exposed if HW exposure is allowed. + std::optional power_efficient_encoder; + std::optional scalability_mode; + + // RTX ssrc. Only present if RTX is negotiated. + std::optional rtx_ssrc; +}; + +// https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict* +class RTC_EXPORT RTCRemoteInboundRtpStreamStats final + : public RTCReceivedRtpStreamStats { + public: + WEBRTC_RTCSTATS_DECL(RTCRemoteInboundRtpStreamStats); + RTCRemoteInboundRtpStreamStats(std::string id, Timestamp timestamp); + ~RTCRemoteInboundRtpStreamStats() override; + + std::optional local_id; + std::optional round_trip_time; + std::optional fraction_lost; + std::optional total_round_trip_time; + std::optional round_trip_time_measurements; +}; + +// https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict* +class RTC_EXPORT RTCRemoteOutboundRtpStreamStats final + : public RTCSentRtpStreamStats { + public: + WEBRTC_RTCSTATS_DECL(RTCRemoteOutboundRtpStreamStats); + RTCRemoteOutboundRtpStreamStats(std::string id, Timestamp timestamp); + ~RTCRemoteOutboundRtpStreamStats() override; + + std::optional local_id; + std::optional remote_timestamp; + std::optional reports_sent; + std::optional round_trip_time; + std::optional round_trip_time_measurements; + std::optional total_round_trip_time; +}; + +// https://w3c.github.io/webrtc-stats/#dom-rtcmediasourcestats +class RTC_EXPORT RTCMediaSourceStats : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(RTCMediaSourceStats); + ~RTCMediaSourceStats() override; + + std::optional track_identifier; + std::optional kind; + + protected: + RTCMediaSourceStats(std::string id, Timestamp timestamp); +}; + +// https://w3c.github.io/webrtc-stats/#dom-rtcaudiosourcestats +class RTC_EXPORT RTCAudioSourceStats final : public RTCMediaSourceStats { + public: + WEBRTC_RTCSTATS_DECL(RTCAudioSourceStats); + RTCAudioSourceStats(std::string id, Timestamp timestamp); + ~RTCAudioSourceStats() override; + + std::optional audio_level; + std::optional total_audio_energy; + std::optional total_samples_duration; + std::optional echo_return_loss; + std::optional echo_return_loss_enhancement; +}; + +// https://w3c.github.io/webrtc-stats/#dom-rtcvideosourcestats +class RTC_EXPORT RTCVideoSourceStats final : public RTCMediaSourceStats { + public: + WEBRTC_RTCSTATS_DECL(RTCVideoSourceStats); + RTCVideoSourceStats(std::string id, Timestamp timestamp); + ~RTCVideoSourceStats() override; + + std::optional width; + std::optional height; + std::optional frames; + std::optional frames_per_second; +}; + +// https://w3c.github.io/webrtc-stats/#transportstats-dict* +class RTC_EXPORT RTCTransportStats final : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(RTCTransportStats); + RTCTransportStats(std::string id, Timestamp timestamp); + ~RTCTransportStats() override; + + std::optional bytes_sent; + std::optional packets_sent; + std::optional bytes_received; + std::optional packets_received; + std::optional rtcp_transport_stats_id; + std::optional dtls_state; + std::optional selected_candidate_pair_id; + std::optional local_certificate_id; + std::optional remote_certificate_id; + std::optional tls_version; + std::optional dtls_cipher; + std::optional dtls_role; + std::optional srtp_cipher; + std::optional selected_candidate_pair_changes; + std::optional ice_role; + std::optional ice_local_username_fragment; + std::optional ice_state; +}; + +// https://w3c.github.io/webrtc-stats/#playoutstats-dict* +class RTC_EXPORT RTCAudioPlayoutStats final : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(RTCAudioPlayoutStats); + RTCAudioPlayoutStats(const std::string& id, Timestamp timestamp); + ~RTCAudioPlayoutStats() override; + + std::optional kind; + std::optional synthesized_samples_duration; + std::optional synthesized_samples_events; + std::optional total_samples_duration; + std::optional total_playout_delay; + std::optional total_samples_count; +}; + +} // namespace webrtc + +#endif // API_STATS_RTCSTATS_OBJECTS_H_ diff --git a/pkg/apm/webrtc/api/task_queue/default_task_queue_factory.h b/pkg/apm/webrtc/api/task_queue/default_task_queue_factory.h new file mode 100644 index 00000000..1d2dbd7e --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/default_task_queue_factory.h @@ -0,0 +1,25 @@ +/* + * 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_TASK_QUEUE_DEFAULT_TASK_QUEUE_FACTORY_H_ +#define API_TASK_QUEUE_DEFAULT_TASK_QUEUE_FACTORY_H_ + +#include + +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_factory.h" + +namespace webrtc { + +std::unique_ptr CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials = nullptr); + +} // namespace webrtc + +#endif // API_TASK_QUEUE_DEFAULT_TASK_QUEUE_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/task_queue/gcd/default_task_queue_factory_gcd.cc b/pkg/apm/webrtc/api/task_queue/gcd/default_task_queue_factory_gcd.cc new file mode 100644 index 00000000..fb293b3c --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/gcd/default_task_queue_factory_gcd.cc @@ -0,0 +1,23 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include + +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_factory.h" +#include "rtc_base/task_queue_gcd.h" + +namespace webrtc { + +std::unique_ptr CreateDefaultTaskQueueFactory( + const FieldTrialsView* /* field_trials */) { + return CreateTaskQueueGcdFactory(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/task_queue/gcd/gcd.go b/pkg/apm/webrtc/api/task_queue/gcd/gcd.go new file mode 100644 index 00000000..5aabb282 --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/gcd/gcd.go @@ -0,0 +1,9 @@ +//go:build console && darwin + +package gcd + +// #cgo CFLAGS: -I${SRCDIR}/../../.. -DWEBRTC_MAC -DWEBRTC_POSIX -Wno-deprecated-declarations -Wno-nullability-completeness +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/api/task_queue/gcd/gcd_helpers.m b/pkg/apm/webrtc/api/task_queue/gcd/gcd_helpers.m new file mode 100644 index 00000000..fd9a361f --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/gcd/gcd_helpers.m @@ -0,0 +1,22 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/system/gcd_helpers.h" + +dispatch_queue_t RTCDispatchQueueCreateWithTarget(const char *label, + dispatch_queue_attr_t attr, + dispatch_queue_t target) { + if (@available(iOS 10, macOS 10.12, tvOS 10, watchOS 3, *)) { + return dispatch_queue_create_with_target(label, attr, target); + } + dispatch_queue_t queue = dispatch_queue_create(label, attr); + dispatch_set_target_queue(queue, target); + return queue; +} diff --git a/pkg/apm/webrtc/api/task_queue/gcd/task_queue_gcd.cc b/pkg/apm/webrtc/api/task_queue/gcd/task_queue_gcd.cc new file mode 100644 index 00000000..fb870e76 --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/gcd/task_queue_gcd.cc @@ -0,0 +1,162 @@ +/* + * Copyright 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. + */ + +// This file contains the implementation of TaskQueue for Mac and iOS. +// The implementation uses Grand Central Dispatch queues (GCD) to +// do the actual task queuing. + +#include "rtc_base/task_queue_gcd.h" + +#include +#include + +#include + +#include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" +#include "api/location.h" +#include "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/system/gcd_helpers.h" + +namespace webrtc { +namespace { + +int TaskQueuePriorityToGCD(TaskQueueFactory::Priority priority) { + switch (priority) { + case TaskQueueFactory::Priority::NORMAL: + return DISPATCH_QUEUE_PRIORITY_DEFAULT; + case TaskQueueFactory::Priority::HIGH: + return DISPATCH_QUEUE_PRIORITY_HIGH; + case TaskQueueFactory::Priority::LOW: + return DISPATCH_QUEUE_PRIORITY_LOW; + } +} + +class TaskQueueGcd final : public TaskQueueBase { + public: + TaskQueueGcd(absl::string_view queue_name, int gcd_priority); + + void Delete() override; + + protected: + void PostTaskImpl(absl::AnyInvocable task, + const PostTaskTraits& traits, + const Location& location) override; + void PostDelayedTaskImpl(absl::AnyInvocable task, + TimeDelta delay, + const PostDelayedTaskTraits& traits, + const Location& location) override; + + private: + struct TaskContext { + TaskContext(TaskQueueGcd* queue, absl::AnyInvocable task) + : queue(queue), task(std::move(task)) {} + + TaskQueueGcd* const queue; + absl::AnyInvocable task; + }; + + ~TaskQueueGcd() override; + static void RunTask(void* task_context); + static void SetNotActive(void* task_queue); + static void DeleteQueue(void* task_queue); + + dispatch_queue_t queue_; + bool is_active_; +}; + +TaskQueueGcd::TaskQueueGcd(absl::string_view queue_name, int gcd_priority) + : queue_(RTCDispatchQueueCreateWithTarget( + std::string(queue_name).c_str(), + DISPATCH_QUEUE_SERIAL, + dispatch_get_global_queue(gcd_priority, 0))), + is_active_(true) { + RTC_CHECK(queue_); + dispatch_set_context(queue_, this); + // Assign a finalizer that will delete the queue when the last reference + // is released. This may run after the TaskQueue::Delete. + dispatch_set_finalizer_f(queue_, &DeleteQueue); +} + +TaskQueueGcd::~TaskQueueGcd() = default; + +void TaskQueueGcd::Delete() { + RTC_DCHECK(!IsCurrent()); + // Implementation/behavioral note: + // Dispatch queues are reference counted via calls to dispatch_retain and + // dispatch_release. Pending blocks submitted to a queue also hold a + // reference to the queue until they have finished. Once all references to a + // queue have been released, the queue will be deallocated by the system. + // This is why we check the is_active_ before running tasks. + + // Use dispatch_sync to set the is_active_ to guarantee that there's not a + // race with checking it from a task. + dispatch_sync_f(queue_, this, &SetNotActive); + dispatch_release(queue_); +} + +void TaskQueueGcd::PostTaskImpl(absl::AnyInvocable task, + const PostTaskTraits& traits, + const Location& location) { + auto* context = new TaskContext(this, std::move(task)); + dispatch_async_f(queue_, context, &RunTask); +} + +void TaskQueueGcd::PostDelayedTaskImpl(absl::AnyInvocable task, + TimeDelta delay, + const PostDelayedTaskTraits& traits, + const Location& location) { + auto* context = new TaskContext(this, std::move(task)); + dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW, delay.us() * NSEC_PER_USEC), + queue_, context, &RunTask); +} + +// static +void TaskQueueGcd::RunTask(void* task_context) { + std::unique_ptr tc(static_cast(task_context)); + CurrentTaskQueueSetter set_current(tc->queue); + if (tc->queue->is_active_) { + std::move(tc->task)(); + } + // Delete the task before CurrentTaskQueueSetter clears state that this code + // is running on the task queue. + tc = nullptr; +} + +// static +void TaskQueueGcd::SetNotActive(void* task_queue) { + static_cast(task_queue)->is_active_ = false; +} + +// static +void TaskQueueGcd::DeleteQueue(void* task_queue) { + delete static_cast(task_queue); +} + +class TaskQueueGcdFactory final : public TaskQueueFactory { + public: + std::unique_ptr CreateTaskQueue( + absl::string_view name, + Priority priority) const override { + return std::unique_ptr( + new TaskQueueGcd(name, TaskQueuePriorityToGCD(priority))); + } +}; + +} // namespace + +std::unique_ptr CreateTaskQueueGcdFactory() { + return std::make_unique(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/task_queue/gcd/task_queue_gcd.h b/pkg/apm/webrtc/api/task_queue/gcd/task_queue_gcd.h new file mode 100644 index 00000000..dc6039e9 --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/gcd/task_queue_gcd.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_TASK_QUEUE_GCD_H_ +#define RTC_BASE_TASK_QUEUE_GCD_H_ + +#include + +#include "api/task_queue/task_queue_factory.h" + +namespace webrtc { + +std::unique_ptr CreateTaskQueueGcdFactory(); + +} // namespace webrtc + +#endif // RTC_BASE_TASK_QUEUE_GCD_H_ diff --git a/pkg/apm/webrtc/api/task_queue/pending_task_safety_flag.cc b/pkg/apm/webrtc/api/task_queue/pending_task_safety_flag.cc new file mode 100644 index 00000000..32bc037e --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/pending_task_safety_flag.cc @@ -0,0 +1,72 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "api/task_queue/pending_task_safety_flag.h" + +#include "absl/base/nullability.h" +#include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "api/task_queue/task_queue_base.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// static +scoped_refptr PendingTaskSafetyFlag::CreateInternal( + bool alive) { + // Explicit new, to access private constructor. + return scoped_refptr(new PendingTaskSafetyFlag(alive)); +} + +// static +scoped_refptr PendingTaskSafetyFlag::Create() { + return CreateInternal(true); +} + +scoped_refptr PendingTaskSafetyFlag::CreateDetached() { + scoped_refptr safety_flag = CreateInternal(true); + safety_flag->main_sequence_.Detach(); + return safety_flag; +} + +// Creates a flag, but with its SequenceChecker explicitly initialized for +// a given task queue and the `alive()` flag specified. +scoped_refptr +PendingTaskSafetyFlag::CreateAttachedToTaskQueue(bool alive, + TaskQueueBase* absl_nonnull + attached_queue) { + RTC_DCHECK(attached_queue) << "Null TaskQueue provided"; + return scoped_refptr( + new PendingTaskSafetyFlag(alive, attached_queue)); +} + +scoped_refptr +PendingTaskSafetyFlag::CreateDetachedInactive() { + scoped_refptr safety_flag = CreateInternal(false); + safety_flag->main_sequence_.Detach(); + return safety_flag; +} + +void PendingTaskSafetyFlag::SetNotAlive() { + RTC_DCHECK_RUN_ON(&main_sequence_); + alive_ = false; +} + +void PendingTaskSafetyFlag::SetAlive() { + RTC_DCHECK_RUN_ON(&main_sequence_); + alive_ = true; +} + +bool PendingTaskSafetyFlag::alive() const { + RTC_DCHECK_RUN_ON(&main_sequence_); + return alive_; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/task_queue/pending_task_safety_flag.h b/pkg/apm/webrtc/api/task_queue/pending_task_safety_flag.h new file mode 100644 index 00000000..71e87106 --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/pending_task_safety_flag.h @@ -0,0 +1,177 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_ +#define API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_ + +#include + +#include "absl/base/nullability.h" +#include "absl/functional/any_invocable.h" +#include "api/ref_counted_base.h" +#include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "api/task_queue/task_queue_base.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// The PendingTaskSafetyFlag and the ScopedTaskSafety are designed to address +// the issue where you have a task to be executed later that has references, +// but cannot guarantee that the referenced object is alive when the task is +// executed. + +// This mechanism can be used with tasks that are created and destroyed +// on a single thread / task queue, and with tasks posted to the same +// thread/task queue, but tasks can be posted from any thread/TQ. + +// Typical usage: +// When posting a task, post a copy (capture by-value in a lambda) of the flag +// reference and before performing the work, check the `alive()` state. Abort if +// alive() returns `false`: +// +// class ExampleClass { +// .... +// webrtc::scoped_refptr flag = safety_flag_; +// my_task_queue_->PostTask( +// [flag = std::move(flag), this] { +// // Now running on the main thread. +// if (!flag->alive()) +// return; +// MyMethod(); +// }); +// .... +// ~ExampleClass() { +// safety_flag_->SetNotAlive(); +// } +// scoped_refptr safety_flag_ +// = PendingTaskSafetyFlag::Create(); +// } +// +// SafeTask makes this check automatic: +// +// my_task_queue_->PostTask(SafeTask(safety_flag_, [this] { MyMethod(); })); +// +class RTC_EXPORT PendingTaskSafetyFlag final + : public RefCountedNonVirtual { + public: + static scoped_refptr Create(); + + // Creates a flag, but with its SequenceChecker initially detached. Hence, it + // may be created on a different thread than the flag will be used on. + static scoped_refptr CreateDetached(); + + // Creates a flag, but with its SequenceChecker explicitly initialized for + // a given task queue and the `alive()` flag specified. + static scoped_refptr CreateAttachedToTaskQueue( + bool alive, + TaskQueueBase* absl_nonnull attached_queue); + + // Same as `CreateDetached()` except the initial state of the returned flag + // will be `!alive()`. + static scoped_refptr CreateDetachedInactive(); + + ~PendingTaskSafetyFlag() = default; + + void SetNotAlive(); + // The SetAlive method is intended to support Start/Stop/Restart usecases. + // When a class has called SetNotAlive on a flag used for posted tasks, and + // decides it wants to post new tasks and have them run, there are two + // reasonable ways to do that: + // + // (i) Use the below SetAlive method. One subtlety is that any task posted + // prior to SetNotAlive, and still in the queue, is resurrected and will + // run. + // + // (ii) Create a fresh flag, and just drop the reference to the old one. This + // avoids the above problem, and ensures that tasks poster prior to + // SetNotAlive stay cancelled. Instead, there's a potential data race on + // the flag pointer itself. Some synchronization is required between the + // thread overwriting the flag pointer, and the threads that want to post + // tasks and therefore read that same pointer. + void SetAlive(); + bool alive() const; + + protected: + explicit PendingTaskSafetyFlag(bool alive) : alive_(alive) {} + PendingTaskSafetyFlag(bool alive, TaskQueueBase* absl_nonnull attached_queue) + : alive_(alive), main_sequence_(attached_queue) {} + + private: + static scoped_refptr CreateInternal(bool alive); + + bool alive_ = true; + RTC_NO_UNIQUE_ADDRESS SequenceChecker main_sequence_; +}; + +// The ScopedTaskSafety makes using PendingTaskSafetyFlag very simple. +// It does automatic PTSF creation and signalling of destruction when the +// ScopedTaskSafety instance goes out of scope. +// +// Example usage: +// +// my_task_queue->PostTask(SafeTask(scoped_task_safety.flag(), +// [this] { +// // task goes here +// } +// +// This should be used by the class that wants tasks dropped after destruction. +// The requirement is that the instance has to be constructed and destructed on +// the same thread as the potentially dropped tasks would be running on. +class RTC_EXPORT ScopedTaskSafety final { + public: + ScopedTaskSafety() = default; + explicit ScopedTaskSafety(scoped_refptr flag) + : flag_(std::move(flag)) {} + ~ScopedTaskSafety() { flag_->SetNotAlive(); } + + // Returns a new reference to the safety flag. + scoped_refptr flag() const { return flag_; } + + // Marks the current flag as not-alive and attaches to a new one. + void reset(scoped_refptr new_flag = + PendingTaskSafetyFlag::Create()) { + flag_->SetNotAlive(); + flag_ = std::move(new_flag); + } + + private: + scoped_refptr flag_ = PendingTaskSafetyFlag::Create(); +}; + +// Like ScopedTaskSafety, but allows construction on a different thread than +// where the flag will be used. +class RTC_EXPORT ScopedTaskSafetyDetached final { + public: + ScopedTaskSafetyDetached() = default; + ~ScopedTaskSafetyDetached() { flag_->SetNotAlive(); } + + // Returns a new reference to the safety flag. + scoped_refptr flag() const { return flag_; } + + private: + scoped_refptr flag_ = + PendingTaskSafetyFlag::CreateDetached(); +}; + +inline absl::AnyInvocable SafeTask( + scoped_refptr flag, + absl::AnyInvocable task) { + return [flag = std::move(flag), task = std::move(task)]() mutable { + if (flag->alive()) { + std::move(task)(); + } + }; +} + +} // namespace webrtc + +#endif // API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_ diff --git a/pkg/apm/webrtc/api/task_queue/stdlib/default_task_queue_factory_stdlib.cc b/pkg/apm/webrtc/api/task_queue/stdlib/default_task_queue_factory_stdlib.cc new file mode 100644 index 00000000..10cda7c5 --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/stdlib/default_task_queue_factory_stdlib.cc @@ -0,0 +1,23 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include + +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_factory.h" +#include "rtc_base/task_queue_stdlib.h" + +namespace webrtc { + +std::unique_ptr CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials) { + return CreateTaskQueueStdlibFactory(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/task_queue/stdlib/stdlib.go b/pkg/apm/webrtc/api/task_queue/stdlib/stdlib.go new file mode 100644 index 00000000..49af39bd --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/stdlib/stdlib.go @@ -0,0 +1,9 @@ +//go:build console && !darwin + +package stdlib + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/api/task_queue/stdlib/task_queue_stdlib.cc b/pkg/apm/webrtc/api/task_queue/stdlib/task_queue_stdlib.cc new file mode 100644 index 00000000..0895eb18 --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/stdlib/task_queue_stdlib.cc @@ -0,0 +1,313 @@ +/* + * Copyright 2018 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/task_queue_stdlib.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" +#include "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" +#include "rtc_base/checks.h" +#include "rtc_base/event.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/divide_round.h" +#include "rtc_base/platform_thread.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { +namespace { + +ThreadPriority TaskQueuePriorityToThreadPriority( + TaskQueueFactory::Priority priority) { + switch (priority) { + case TaskQueueFactory::Priority::HIGH: + return ThreadPriority::kRealtime; + case TaskQueueFactory::Priority::LOW: + return ThreadPriority::kLow; + case TaskQueueFactory::Priority::NORMAL: + return ThreadPriority::kNormal; + } +} + +class TaskQueueStdlib final : public TaskQueueBase { + public: + TaskQueueStdlib(absl::string_view queue_name, ThreadPriority priority); + ~TaskQueueStdlib() override = default; + + void Delete() override; + + protected: + void PostTaskImpl(absl::AnyInvocable task, + const PostTaskTraits& traits, + const Location& location) override; + void PostDelayedTaskImpl(absl::AnyInvocable task, + TimeDelta delay, + const PostDelayedTaskTraits& traits, + const Location& location) override; + + private: + using OrderId = uint64_t; + + struct DelayedEntryTimeout { + // TODO(bugs.webrtc.org/13756): Migrate to Timestamp. + int64_t next_fire_at_us{}; + OrderId order{}; + + bool operator<(const DelayedEntryTimeout& o) const { + return std::tie(next_fire_at_us, order) < + std::tie(o.next_fire_at_us, o.order); + } + }; + + struct NextTask { + bool final_task = false; + absl::AnyInvocable run_task; + TimeDelta sleep_time = Event::kForever; + }; + + static PlatformThread InitializeThread(TaskQueueStdlib* me, + absl::string_view queue_name, + ThreadPriority priority); + + NextTask GetNextTask(); + + void ProcessTasks(); + + void NotifyWake(); + + // Signaled whenever a new task is pending. + Event flag_notify_; + + Mutex pending_lock_; + + // Indicates if the worker thread needs to shutdown now. + bool thread_should_quit_ RTC_GUARDED_BY(pending_lock_) = false; + + // Holds the next order to use for the next task to be + // put into one of the pending queues. + OrderId thread_posting_order_ RTC_GUARDED_BY(pending_lock_) = 0; + + // The list of all pending tasks that need to be processed in the + // FIFO queue ordering on the worker thread. + std::queue>> pending_queue_ + RTC_GUARDED_BY(pending_lock_); + + // The list of all pending tasks that need to be processed at a future + // time based upon a delay. On the off change the delayed task should + // happen at exactly the same time interval as another task then the + // task is processed based on FIFO ordering. std::priority_queue was + // considered but rejected due to its inability to extract the + // move-only value out of the queue without the presence of a hack. + std::map> delayed_queue_ + RTC_GUARDED_BY(pending_lock_); + + // Contains the active worker thread assigned to processing + // tasks (including delayed tasks). + // Placing this last ensures the thread doesn't touch uninitialized attributes + // throughout it's lifetime. + PlatformThread thread_; +}; + +TaskQueueStdlib::TaskQueueStdlib(absl::string_view queue_name, + ThreadPriority priority) + : flag_notify_(/*manual_reset=*/false, /*initially_signaled=*/false), + thread_(InitializeThread(this, queue_name, priority)) {} + +// static +PlatformThread TaskQueueStdlib::InitializeThread(TaskQueueStdlib* me, + absl::string_view queue_name, + ThreadPriority priority) { + Event started; + auto thread = PlatformThread::SpawnJoinable( + [&started, me] { + CurrentTaskQueueSetter set_current(me); + started.Set(); + me->ProcessTasks(); + }, + queue_name, ThreadAttributes().SetPriority(priority)); + started.Wait(Event::kForever); + return thread; +} + +void TaskQueueStdlib::Delete() { + RTC_DCHECK(!IsCurrent()); + + { + MutexLock lock(&pending_lock_); + thread_should_quit_ = true; + } + + NotifyWake(); + + delete this; +} + +void TaskQueueStdlib::PostTaskImpl(absl::AnyInvocable task, + const PostTaskTraits& traits, + const Location& location) { + { + MutexLock lock(&pending_lock_); + pending_queue_.push( + std::make_pair(++thread_posting_order_, std::move(task))); + } + + NotifyWake(); +} + +void TaskQueueStdlib::PostDelayedTaskImpl(absl::AnyInvocable task, + TimeDelta delay, + const PostDelayedTaskTraits& traits, + const Location& location) { + DelayedEntryTimeout delayed_entry; + delayed_entry.next_fire_at_us = TimeMicros() + delay.us(); + + { + MutexLock lock(&pending_lock_); + delayed_entry.order = ++thread_posting_order_; + delayed_queue_[delayed_entry] = std::move(task); + } + + NotifyWake(); +} + +TaskQueueStdlib::NextTask TaskQueueStdlib::GetNextTask() { + NextTask result; + + const int64_t tick_us = TimeMicros(); + + MutexLock lock(&pending_lock_); + + if (thread_should_quit_) { + result.final_task = true; + return result; + } + + if (!delayed_queue_.empty()) { + auto delayed_entry = delayed_queue_.begin(); + const auto& delay_info = delayed_entry->first; + auto& delay_run = delayed_entry->second; + if (tick_us >= delay_info.next_fire_at_us) { + if (!pending_queue_.empty()) { + auto& entry = pending_queue_.front(); + auto& entry_order = entry.first; + auto& entry_run = entry.second; + if (entry_order < delay_info.order) { + result.run_task = std::move(entry_run); + pending_queue_.pop(); + return result; + } + } + + result.run_task = std::move(delay_run); + delayed_queue_.erase(delayed_entry); + return result; + } + + result.sleep_time = TimeDelta::Millis( + DivideRoundUp(delay_info.next_fire_at_us - tick_us, 1'000)); + } + + if (!pending_queue_.empty()) { + auto& entry = pending_queue_.front(); + result.run_task = std::move(entry.second); + pending_queue_.pop(); + } + + return result; +} + +void TaskQueueStdlib::ProcessTasks() { + while (true) { + auto task = GetNextTask(); + + if (task.final_task) + break; + + if (task.run_task) { + // process entry immediately then try again + std::move(task.run_task)(); + + // Attempt to run more tasks before going to sleep. + continue; + } + + flag_notify_.Wait(task.sleep_time, task.sleep_time); + } + + // Ensure remaining deleted tasks are destroyed with Current() set up to this + // task queue. + std::queue>> pending_queue; + { + MutexLock lock(&pending_lock_); + pending_queue_.swap(pending_queue); + } + pending_queue = {}; +#if RTC_DCHECK_IS_ON + MutexLock lock(&pending_lock_); + RTC_DCHECK(pending_queue_.empty()); +#endif +} + +void TaskQueueStdlib::NotifyWake() { + // The queue holds pending tasks to complete. Either tasks are to be + // executed immediately or tasks are to be run at some future delayed time. + // For immediate tasks the task queue's thread is busy running the task and + // the thread will not be waiting on the flag_notify_ event. If no immediate + // tasks are available but a delayed task is pending then the thread will be + // waiting on flag_notify_ with a delayed time-out of the nearest timed task + // to run. If no immediate or pending tasks are available, the thread will + // wait on flag_notify_ until signaled that a task has been added (or the + // thread to be told to shutdown). + + // In all cases, when a new immediate task, delayed task, or request to + // shutdown the thread is added the flag_notify_ is signaled after. If the + // thread was waiting then the thread will wake up immediately and re-assess + // what task needs to be run next (i.e. run a task now, wait for the nearest + // timed delayed task, or shutdown the thread). If the thread was not waiting + // then the thread will remained signaled to wake up the next time any + // attempt to wait on the flag_notify_ event occurs. + + // Any immediate or delayed pending task (or request to shutdown the thread) + // must always be added to the queue prior to signaling flag_notify_ to wake + // up the possibly sleeping thread. This prevents a race condition where the + // thread is notified to wake up but the task queue's thread finds nothing to + // do so it waits once again to be signaled where such a signal may never + // happen. + flag_notify_.Set(); +} + +class TaskQueueStdlibFactory final : public TaskQueueFactory { + public: + std::unique_ptr CreateTaskQueue( + absl::string_view name, + Priority priority) const override { + return std::unique_ptr( + new TaskQueueStdlib(name, TaskQueuePriorityToThreadPriority(priority))); + } +}; + +} // namespace + +std::unique_ptr CreateTaskQueueStdlibFactory() { + return std::make_unique(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/task_queue/stdlib/task_queue_stdlib.h b/pkg/apm/webrtc/api/task_queue/stdlib/task_queue_stdlib.h new file mode 100644 index 00000000..fb03dff3 --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/stdlib/task_queue_stdlib.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_TASK_QUEUE_STDLIB_H_ +#define RTC_BASE_TASK_QUEUE_STDLIB_H_ + +#include + +#include "api/task_queue/task_queue_factory.h" + +namespace webrtc { + +std::unique_ptr CreateTaskQueueStdlibFactory(); + +} // namespace webrtc + +#endif // RTC_BASE_TASK_QUEUE_STDLIB_H_ diff --git a/pkg/apm/webrtc/api/task_queue/task_queue.go b/pkg/apm/webrtc/api/task_queue/task_queue.go new file mode 100644 index 00000000..6419e7e4 --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/task_queue.go @@ -0,0 +1,10 @@ +//go:build console + +package task_queue + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/api/task_queue/task_queue_base.cc b/pkg/apm/webrtc/api/task_queue/task_queue_base.cc new file mode 100644 index 00000000..6533aa01 --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/task_queue_base.cc @@ -0,0 +1,78 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "api/task_queue/task_queue_base.h" + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +#if defined(ABSL_HAVE_THREAD_LOCAL) + +namespace webrtc { +namespace { + +ABSL_CONST_INIT thread_local TaskQueueBase* current = nullptr; + +} // namespace + +TaskQueueBase* TaskQueueBase::Current() { + return current; +} + +TaskQueueBase::CurrentTaskQueueSetter::CurrentTaskQueueSetter( + TaskQueueBase* task_queue) + : previous_(current) { + current = task_queue; +} + +TaskQueueBase::CurrentTaskQueueSetter::~CurrentTaskQueueSetter() { + current = previous_; +} +} // namespace webrtc + +#elif defined(WEBRTC_POSIX) + +#include + +namespace webrtc { +namespace { + +ABSL_CONST_INIT pthread_key_t g_queue_ptr_tls = 0; + +void InitializeTls() { + RTC_CHECK(pthread_key_create(&g_queue_ptr_tls, nullptr) == 0); +} + +pthread_key_t GetQueuePtrTls() { + static pthread_once_t init_once = PTHREAD_ONCE_INIT; + RTC_CHECK(pthread_once(&init_once, &InitializeTls) == 0); + return g_queue_ptr_tls; +} + +} // namespace + +TaskQueueBase* TaskQueueBase::Current() { + return static_cast(pthread_getspecific(GetQueuePtrTls())); +} + +TaskQueueBase::CurrentTaskQueueSetter::CurrentTaskQueueSetter( + TaskQueueBase* task_queue) + : previous_(TaskQueueBase::Current()) { + pthread_setspecific(GetQueuePtrTls(), task_queue); +} + +TaskQueueBase::CurrentTaskQueueSetter::~CurrentTaskQueueSetter() { + pthread_setspecific(GetQueuePtrTls(), previous_); +} + +} // namespace webrtc + +#else +#error Unsupported platform +#endif diff --git a/pkg/apm/webrtc/api/task_queue/task_queue_base.h b/pkg/apm/webrtc/api/task_queue/task_queue_base.h new file mode 100644 index 00000000..8b7bb1b9 --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/task_queue_base.h @@ -0,0 +1,197 @@ +/* + * 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_TASK_QUEUE_TASK_QUEUE_BASE_H_ +#define API_TASK_QUEUE_TASK_QUEUE_BASE_H_ + +#include + +#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" + +namespace webrtc { + +// Asynchronously executes tasks in a way that guarantees that they're executed +// in FIFO order and that tasks never overlap. Tasks may always execute on the +// same worker thread and they may not. To DCHECK that tasks are executing on a +// 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 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. + // When a TaskQueue is deleted, pending tasks will not be executed but they + // 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 task, + const Location& location = Location::Current()) { + PostTaskImpl(std::move(task), PostTaskTraits{}, location); + } + + // 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 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 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 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: + // 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; + CurrentTaskQueueSetter& operator=(const CurrentTaskQueueSetter&) = delete; + ~CurrentTaskQueueSetter(); + + private: + 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 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 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; +}; + +struct TaskQueueDeleter { + void operator()(TaskQueueBase* task_queue) const { task_queue->Delete(); } +}; + +} // namespace webrtc + +#endif // API_TASK_QUEUE_TASK_QUEUE_BASE_H_ diff --git a/pkg/apm/webrtc/api/task_queue/task_queue_factory.h b/pkg/apm/webrtc/api/task_queue/task_queue_factory.h new file mode 100644 index 00000000..b68ab33f --- /dev/null +++ b/pkg/apm/webrtc/api/task_queue/task_queue_factory.h @@ -0,0 +1,35 @@ +/* + * 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_TASK_QUEUE_TASK_QUEUE_FACTORY_H_ +#define API_TASK_QUEUE_TASK_QUEUE_FACTORY_H_ + +#include + +#include "absl/strings/string_view.h" +#include "api/task_queue/task_queue_base.h" + +namespace webrtc { + +// The implementation of this interface must be thread-safe. +class TaskQueueFactory { + public: + // TaskQueue priority levels. On some platforms these will map to thread + // priorities, on others such as Mac and iOS, GCD queue priorities. + enum class Priority { NORMAL = 0, HIGH, LOW }; + + virtual ~TaskQueueFactory() = default; + virtual std::unique_ptr CreateTaskQueue( + absl::string_view name, + Priority priority) const = 0; +}; + +} // namespace webrtc + +#endif // API_TASK_QUEUE_TASK_QUEUE_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/transport/bandwidth_estimation_settings.h b/pkg/apm/webrtc/api/transport/bandwidth_estimation_settings.h new file mode 100644 index 00000000..7ae8cc9e --- /dev/null +++ b/pkg/apm/webrtc/api/transport/bandwidth_estimation_settings.h @@ -0,0 +1,27 @@ +/* + * 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_TRANSPORT_BANDWIDTH_ESTIMATION_SETTINGS_H_ +#define API_TRANSPORT_BANDWIDTH_ESTIMATION_SETTINGS_H_ + +#include "rtc_base/system/rtc_export.h" +namespace webrtc { +// Configuration settings affecting bandwidth estimation. +// These settings can be set and changed by an application. +struct RTC_EXPORT BandwidthEstimationSettings { + // A bandwith estimation probe may be sent using a RtpTransceiver with + // direction SendOnly or SendRecv that supports RTX. The probe can be sent + // without first sending media packets in which case Rtp padding packets are + // used. + bool allow_probe_without_media = false; +}; + +} // namespace webrtc +#endif // API_TRANSPORT_BANDWIDTH_ESTIMATION_SETTINGS_H_ diff --git a/pkg/apm/webrtc/api/transport/bandwidth_usage.h b/pkg/apm/webrtc/api/transport/bandwidth_usage.h new file mode 100644 index 00000000..77500be9 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/bandwidth_usage.h @@ -0,0 +1,25 @@ +/* + * 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_TRANSPORT_BANDWIDTH_USAGE_H_ +#define API_TRANSPORT_BANDWIDTH_USAGE_H_ + +namespace webrtc { + +enum class BandwidthUsage { + kBwNormal = 0, + kBwUnderusing = 1, + kBwOverusing = 2, + kLast +}; + +} // namespace webrtc + +#endif // API_TRANSPORT_BANDWIDTH_USAGE_H_ diff --git a/pkg/apm/webrtc/api/transport/bitrate_settings.cc b/pkg/apm/webrtc/api/transport/bitrate_settings.cc new file mode 100644 index 00000000..c72bd827 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/bitrate_settings.cc @@ -0,0 +1,19 @@ +/* + * 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/transport/bitrate_settings.h" + +namespace webrtc { + +BitrateSettings::BitrateSettings() = default; +BitrateSettings::~BitrateSettings() = default; +BitrateSettings::BitrateSettings(const BitrateSettings&) = default; + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/transport/bitrate_settings.h b/pkg/apm/webrtc/api/transport/bitrate_settings.h new file mode 100644 index 00000000..9bcd694d --- /dev/null +++ b/pkg/apm/webrtc/api/transport/bitrate_settings.h @@ -0,0 +1,49 @@ +/* + * 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_TRANSPORT_BITRATE_SETTINGS_H_ +#define API_TRANSPORT_BITRATE_SETTINGS_H_ + +#include + +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Configuration of send bitrate. The `start_bitrate_bps` value is +// used for multiple purposes, both as a prior in the bandwidth +// estimator, and for initial configuration of the encoder. We may +// want to create separate apis for those, and use a smaller struct +// with only the min and max constraints. +struct RTC_EXPORT BitrateSettings { + BitrateSettings(); + ~BitrateSettings(); + BitrateSettings(const BitrateSettings&); + // 0 <= min <= start <= max should hold for set parameters. + std::optional min_bitrate_bps; + std::optional start_bitrate_bps; + std::optional max_bitrate_bps; +}; + +// TODO(srte): BitrateConstraints and BitrateSettings should be merged. +// Both represent the same kind data, but are using different default +// initializer and representation of unset values. +struct BitrateConstraints { + int min_bitrate_bps = 0; + int start_bitrate_bps = kDefaultStartBitrateBps; + int max_bitrate_bps = -1; + + private: + static constexpr int kDefaultStartBitrateBps = 300000; +}; + +} // namespace webrtc + +#endif // API_TRANSPORT_BITRATE_SETTINGS_H_ diff --git a/pkg/apm/webrtc/api/transport/data_channel_transport_interface.h b/pkg/apm/webrtc/api/transport/data_channel_transport_interface.h new file mode 100644 index 00000000..d6b939ab --- /dev/null +++ b/pkg/apm/webrtc/api/transport/data_channel_transport_interface.h @@ -0,0 +1,136 @@ +/* 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. + */ + +// This is an experimental interface and is subject to change without notice. + +#ifndef API_TRANSPORT_DATA_CHANNEL_TRANSPORT_INTERFACE_H_ +#define API_TRANSPORT_DATA_CHANNEL_TRANSPORT_INTERFACE_H_ + +#include +#include + +#include "api/priority.h" +#include "api/rtc_error.h" +#include "rtc_base/copy_on_write_buffer.h" + +namespace webrtc { + +// Supported types of application data messages. +enum class DataMessageType { + // Application data buffer with the binary bit unset. + kText, + + // Application data buffer with the binary bit set. + kBinary, + + // Transport-agnostic control messages, such as open or open-ack messages. + kControl, +}; + +// Parameters for sending data. The parameters may change from message to +// message, even within a single channel. For example, control messages may be +// sent reliably and in-order, even if the data channel is configured for +// unreliable delivery. +struct SendDataParams { + DataMessageType type = DataMessageType::kText; + + // Whether to deliver the message in order with respect to other ordered + // messages with the same channel_id. + bool ordered = false; + + // If set, the maximum number of times this message may be + // retransmitted by the transport before it is dropped. + // Setting this value to zero disables retransmission. + // Valid values are in the range [0-UINT16_MAX]. + // `max_rtx_count` and `max_rtx_ms` may not be set simultaneously. + std::optional max_rtx_count; + + // If set, the maximum number of milliseconds for which the transport + // may retransmit this message before it is dropped. + // Setting this value to zero disables retransmission. + // Valid values are in the range [0-UINT16_MAX]. + // `max_rtx_count` and `max_rtx_ms` may not be set simultaneously. + std::optional max_rtx_ms; +}; + +// Sink for callbacks related to a data channel. +class DataChannelSink { + public: + virtual ~DataChannelSink() = default; + + // Callback issued when data is received by the transport. + virtual void OnDataReceived(int channel_id, + DataMessageType type, + const CopyOnWriteBuffer& buffer) = 0; + + // Callback issued when a remote data channel begins the closing procedure. + // Messages sent after the closing procedure begins will not be transmitted. + virtual void OnChannelClosing(int channel_id) = 0; + + // Callback issued when a (remote or local) data channel completes the closing + // procedure. Closing channels become closed after all pending data has been + // transmitted. + virtual void OnChannelClosed(int channel_id) = 0; + + // Callback issued when the data channel becomes ready to send. + // This callback will be issued immediately when the data channel sink is + // registered if the transport is ready at that time. This callback may be + // invoked again following send errors (eg. due to the transport being + // temporarily blocked or unavailable). + virtual void OnReadyToSend() = 0; + + // Callback issued when the data channel becomes unusable (closed). + // TODO(https://crbug.com/webrtc/10360): Make pure virtual when all + // consumers updated. + virtual void OnTransportClosed(RTCError /* error */) {} + + // The data channel's buffered_amount has fallen to or below the threshold + // set when calling `SetBufferedAmountLowThreshold` + virtual void OnBufferedAmountLow(int channel_id) = 0; +}; + +// Transport for data channels. +class DataChannelTransportInterface { + public: + virtual ~DataChannelTransportInterface() = default; + + // Opens a data `channel_id` for sending. May return an error if the + // specified `channel_id` is unusable. Must be called before `SendData`. + virtual RTCError OpenChannel(int channel_id, PriorityValue priority) = 0; + + // Sends a data buffer to the remote endpoint using the given send parameters. + // `buffer` may not be larger than 256 KiB. Returns an error if the send + // fails. + virtual RTCError SendData(int channel_id, + const SendDataParams& params, + const CopyOnWriteBuffer& buffer) = 0; + + // Closes `channel_id` gracefully. Returns an error if `channel_id` is not + // open. Data sent after the closing procedure begins will not be + // transmitted. The channel becomes closed after pending data is transmitted. + virtual RTCError CloseChannel(int channel_id) = 0; + + // Sets a sink for data messages and channel state callbacks. Before media + // transport is destroyed, the sink must be unregistered by setting it to + // nullptr. + virtual void SetDataSink(DataChannelSink* sink) = 0; + + // Returns whether this data channel transport is ready to send. + // Note: the default implementation always returns false (as it assumes no one + // has implemented the interface). This default implementation is temporary. + virtual bool IsReadyToSend() const = 0; + + virtual size_t buffered_amount(int channel_id) const = 0; + virtual size_t buffered_amount_low_threshold(int channel_id) const = 0; + virtual void SetBufferedAmountLowThreshold(int channel_id, size_t bytes) = 0; +}; + +} // namespace webrtc + +#endif // API_TRANSPORT_DATA_CHANNEL_TRANSPORT_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/transport/ecn_marking.h b/pkg/apm/webrtc/api/transport/ecn_marking.h new file mode 100644 index 00000000..bbcab6eb --- /dev/null +++ b/pkg/apm/webrtc/api/transport/ecn_marking.h @@ -0,0 +1,42 @@ +/* + * Copyright 2024 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_TRANSPORT_ECN_MARKING_H_ +#define API_TRANSPORT_ECN_MARKING_H_ + +namespace webrtc { + +// TODO: bugs.webrtc.org/42225697 - L4S support is slowly being developed. +// Help is appreciated. + +// L4S Explicit Congestion Notification (ECN) . +// https://www.rfc-editor.org/rfc/rfc9331.html ECT stands for ECN-Capable +// Transport and CE stands for Congestion Experienced. + +// RFC-3168, Section 5 +// +-----+-----+ +// | ECN FIELD | +// +-----+-----+ +// ECT CE [Obsolete] RFC 2481 names for the ECN bits. +// 0 0 Not-ECT +// 0 1 ECT(1) +// 1 0 ECT(0) +// 1 1 CE + +enum class EcnMarking { + kNotEct = 0, // Not ECN-Capable Transport + kEct1 = 1, // ECN-Capable Transport + kEct0 = 2, // Not used by L4s (or webrtc.) + kCe = 3, // Congestion experienced +}; + +} // namespace webrtc + +#endif // API_TRANSPORT_ECN_MARKING_H_ diff --git a/pkg/apm/webrtc/api/transport/enums.h b/pkg/apm/webrtc/api/transport/enums.h new file mode 100644 index 00000000..3bc8fd15 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/enums.h @@ -0,0 +1,49 @@ +/* + * 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_TRANSPORT_ENUMS_H_ +#define API_TRANSPORT_ENUMS_H_ + +namespace webrtc { + +// See https://w3c.github.io/webrtc-pc/#rtcicetransportstate +// Note that kFailed is currently not a terminal state, and a transport might +// incorrectly be marked as failed while gathering candidates, see +// bugs.webrtc.org/8833 +enum class IceTransportState { + kNew, + kChecking, + kConnected, + kCompleted, + kFailed, + kDisconnected, + kClosed, +}; + +enum PortPrunePolicy { + NO_PRUNE, // Do not prune. + PRUNE_BASED_ON_PRIORITY, // Prune lower-priority ports on the same network. + KEEP_FIRST_READY // Keep the first ready port and prune the rest + // on the same network. +}; + +enum class VpnPreference { + kDefault, // No VPN preference. + kOnlyUseVpn, // only use VPN connections. + kNeverUseVpn, // never use VPN connections + kPreferVpn, // use a VPN connection if possible, + // i.e VPN connections sorts first. + kAvoidVpn, // only use VPN if there is no other connections, + // i.e VPN connections sorts last. +}; + +} // namespace webrtc + +#endif // API_TRANSPORT_ENUMS_H_ diff --git a/pkg/apm/webrtc/api/transport/field_trial_based_config.cc b/pkg/apm/webrtc/api/transport/field_trial_based_config.cc new file mode 100644 index 00000000..ea3ce215 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/field_trial_based_config.cc @@ -0,0 +1,21 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "api/transport/field_trial_based_config.h" + +#include + +#include "absl/strings/string_view.h" +#include "system_wrappers/include/field_trial.h" + +namespace webrtc { +std::string FieldTrialBasedConfig::GetValue(absl::string_view key) const { + return webrtc::field_trial::FindFullName(std::string(key)); +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/transport/field_trial_based_config.h b/pkg/apm/webrtc/api/transport/field_trial_based_config.h new file mode 100644 index 00000000..441f89e5 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/field_trial_based_config.h @@ -0,0 +1,27 @@ +/* + * 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_TRANSPORT_FIELD_TRIAL_BASED_CONFIG_H_ +#define API_TRANSPORT_FIELD_TRIAL_BASED_CONFIG_H_ + +#include + +#include "absl/strings/string_view.h" +#include "api/field_trials_registry.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { +// Implementation using the field trial API fo the key value lookup. +class RTC_EXPORT FieldTrialBasedConfig : public FieldTrialsRegistry { + private: + std::string GetValue(absl::string_view key) const override; +}; +} // namespace webrtc + +#endif // API_TRANSPORT_FIELD_TRIAL_BASED_CONFIG_H_ diff --git a/pkg/apm/webrtc/api/transport/goog_cc_factory.h b/pkg/apm/webrtc/api/transport/goog_cc_factory.h new file mode 100644 index 00000000..56947306 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/goog_cc_factory.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_TRANSPORT_GOOG_CC_FACTORY_H_ +#define API_TRANSPORT_GOOG_CC_FACTORY_H_ + +#include + +#include "api/network_state_predictor.h" +#include "api/transport/network_control.h" +#include "api/units/time_delta.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +struct GoogCcFactoryConfig { + std::unique_ptr network_state_estimator_factory; + NetworkStatePredictorFactoryInterface* network_state_predictor_factory = + nullptr; + bool feedback_only = false; +}; + +class RTC_EXPORT GoogCcNetworkControllerFactory + : public NetworkControllerFactoryInterface { + public: + GoogCcNetworkControllerFactory() = default; + explicit GoogCcNetworkControllerFactory(GoogCcFactoryConfig config); + + std::unique_ptr Create( + NetworkControllerConfig config) override; + TimeDelta GetProcessInterval() const override; + + private: + GoogCcFactoryConfig factory_config_; +}; + +} // namespace webrtc + +#endif // API_TRANSPORT_GOOG_CC_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/transport/network_control.h b/pkg/apm/webrtc/api/transport/network_control.h new file mode 100644 index 00000000..90cdebe9 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/network_control.h @@ -0,0 +1,140 @@ +/* + * 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_TRANSPORT_NETWORK_CONTROL_H_ +#define API_TRANSPORT_NETWORK_CONTROL_H_ + +#include +#include + +#include "absl/base/attributes.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "api/transport/network_types.h" +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" + +namespace webrtc { + +class TargetTransferRateObserver { + public: + virtual ~TargetTransferRateObserver() = default; + // Called to indicate target transfer rate as well as giving information about + // the current estimate of network parameters. + virtual void OnTargetTransferRate(TargetTransferRate) = 0; + // Called to provide updates to the expected target rate in case it changes + // before the first call to OnTargetTransferRate. + virtual void OnStartRateUpdate(DataRate) {} +}; + +// Configuration sent to factory create function. The parameters here are +// optional to use for a network controller implementation. +struct NetworkControllerConfig { + explicit NetworkControllerConfig(const Environment& env) : env(env) {} + + Environment env; + + // The initial constraints to start with, these can be changed at any later + // time by calls to OnTargetRateConstraints. Note that the starting rate + // has to be set initially to provide a starting state for the network + // controller, even though the field is marked as optional. + TargetRateConstraints constraints; + // Initial stream specific configuration, these are changed at any later time + // by calls to OnStreamsConfig. + StreamsConfig stream_based_config; +}; + +// NetworkControllerInterface is implemented by network controllers. A network +// controller is a class that uses information about network state and traffic +// to estimate network parameters such as round trip time and bandwidth. Network +// controllers does not guarantee thread safety, the interface must be used in a +// non-concurrent fashion. +class NetworkControllerInterface { + public: + virtual ~NetworkControllerInterface() = default; + + // Called when network availabilty changes. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnNetworkAvailability( + NetworkAvailability) = 0; + // Called when the receiving or sending endpoint changes address. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnNetworkRouteChange( + NetworkRouteChange) = 0; + // Called periodically with a periodicy as specified by + // NetworkControllerFactoryInterface::GetProcessInterval. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnProcessInterval( + ProcessInterval) = 0; + // Called when remotely calculated bitrate is received. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnRemoteBitrateReport( + RemoteBitrateReport) = 0; + // Called round trip time has been calculated by protocol specific mechanisms. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnRoundTripTimeUpdate( + RoundTripTimeUpdate) = 0; + // Called when a packet is sent on the network. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnSentPacket( + SentPacket) = 0; + // Called when a packet is received from the remote client. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnReceivedPacket( + ReceivedPacket) = 0; + // Called when the stream specific configuration has been updated. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnStreamsConfig( + StreamsConfig) = 0; + // Called when target transfer rate constraints has been changed. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnTargetRateConstraints( + TargetRateConstraints) = 0; + // Called when a protocol specific calculation of packet loss has been made. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnTransportLossReport( + TransportLossReport) = 0; + // Called with per packet feedback regarding receive time. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnTransportPacketsFeedback( + TransportPacketsFeedback) = 0; + // Called with network state estimate updates. + ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnNetworkStateEstimate( + NetworkStateEstimate) = 0; +}; + +// NetworkControllerFactoryInterface is an interface for creating a network +// controller. +class NetworkControllerFactoryInterface { + public: + virtual ~NetworkControllerFactoryInterface() = default; + + // Used to create a new network controller, requires an observer to be + // provided to handle callbacks. + virtual std::unique_ptr Create( + NetworkControllerConfig config) = 0; + // Returns the interval by which the network controller expects + // OnProcessInterval calls. + virtual TimeDelta GetProcessInterval() const = 0; +}; + +// Under development, subject to change without notice. +class NetworkStateEstimator { + public: + // Gets the current best estimate according to the estimator. + virtual std::optional GetCurrentEstimate() = 0; + // Called with per packet feedback regarding receive time. + // Used when the NetworkStateEstimator runs in the sending endpoint. + virtual void OnTransportPacketsFeedback(const TransportPacketsFeedback&) = 0; + // Called with per packet feedback regarding receive time. + // Used when the NetworkStateEstimator runs in the receiving endpoint. + virtual void OnReceivedPacket(const PacketResult&) {} + // Called when the receiving or sending endpoint changes address. + virtual void OnRouteChange(const NetworkRouteChange&) = 0; + virtual ~NetworkStateEstimator() = default; +}; +class NetworkStateEstimatorFactory { + public: + virtual std::unique_ptr Create( + const FieldTrialsView* key_value_config) = 0; + virtual ~NetworkStateEstimatorFactory() = default; +}; +} // namespace webrtc + +#endif // API_TRANSPORT_NETWORK_CONTROL_H_ diff --git a/pkg/apm/webrtc/api/transport/network_types.cc b/pkg/apm/webrtc/api/transport/network_types.cc new file mode 100644 index 00000000..ccb4a6e3 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/network_types.cc @@ -0,0 +1,107 @@ +/* + * 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/transport/network_types.h" + +#include +#include + +namespace webrtc { +StreamsConfig::StreamsConfig() = default; +StreamsConfig::StreamsConfig(const StreamsConfig&) = default; +StreamsConfig::~StreamsConfig() = default; + +TargetRateConstraints::TargetRateConstraints() = default; +TargetRateConstraints::TargetRateConstraints(const TargetRateConstraints&) = + default; +TargetRateConstraints::~TargetRateConstraints() = default; + +NetworkRouteChange::NetworkRouteChange() = default; +NetworkRouteChange::NetworkRouteChange(const NetworkRouteChange&) = default; +NetworkRouteChange::~NetworkRouteChange() = default; + +PacketResult::PacketResult() = default; +PacketResult::PacketResult(const PacketResult& other) = default; +PacketResult::~PacketResult() = default; + +bool PacketResult::ReceiveTimeOrder::operator()(const PacketResult& lhs, + const PacketResult& rhs) { + if (lhs.receive_time != rhs.receive_time) + return lhs.receive_time < rhs.receive_time; + if (lhs.sent_packet.send_time != rhs.sent_packet.send_time) + return lhs.sent_packet.send_time < rhs.sent_packet.send_time; + return lhs.sent_packet.sequence_number < rhs.sent_packet.sequence_number; +} + +TransportPacketsFeedback::TransportPacketsFeedback() = default; +TransportPacketsFeedback::TransportPacketsFeedback( + const TransportPacketsFeedback& other) = default; +TransportPacketsFeedback::~TransportPacketsFeedback() = default; + +std::vector TransportPacketsFeedback::ReceivedWithSendInfo() + const { + std::vector res; + for (const PacketResult& fb : packet_feedbacks) { + if (fb.IsReceived()) { + res.push_back(fb); + } + } + return res; +} + +std::vector TransportPacketsFeedback::LostWithSendInfo() const { + std::vector res; + for (const PacketResult& fb : packet_feedbacks) { + if (!fb.IsReceived()) { + res.push_back(fb); + } + } + return res; +} + +std::vector TransportPacketsFeedback::PacketsWithFeedback() + const { + return packet_feedbacks; +} + +std::vector TransportPacketsFeedback::SortedByReceiveTime() + const { + std::vector res; + for (const PacketResult& fb : packet_feedbacks) { + if (fb.IsReceived()) { + res.push_back(fb); + } + } + std::sort(res.begin(), res.end(), PacketResult::ReceiveTimeOrder()); + return res; +} + +NetworkControlUpdate::NetworkControlUpdate() = default; +NetworkControlUpdate::NetworkControlUpdate(const NetworkControlUpdate&) = + default; +NetworkControlUpdate::~NetworkControlUpdate() = default; + +PacedPacketInfo::PacedPacketInfo() = default; + +PacedPacketInfo::PacedPacketInfo(int probe_cluster_id, + int probe_cluster_min_probes, + int probe_cluster_min_bytes) + : probe_cluster_id(probe_cluster_id), + probe_cluster_min_probes(probe_cluster_min_probes), + probe_cluster_min_bytes(probe_cluster_min_bytes) {} + +bool PacedPacketInfo::operator==(const PacedPacketInfo& rhs) const { + return send_bitrate == rhs.send_bitrate && + probe_cluster_id == rhs.probe_cluster_id && + probe_cluster_min_probes == rhs.probe_cluster_min_probes && + probe_cluster_min_bytes == rhs.probe_cluster_min_bytes; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/transport/network_types.h b/pkg/apm/webrtc/api/transport/network_types.h new file mode 100644 index 00000000..d229d77b --- /dev/null +++ b/pkg/apm/webrtc/api/transport/network_types.h @@ -0,0 +1,294 @@ +/* + * 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_TRANSPORT_NETWORK_TYPES_H_ +#define API_TRANSPORT_NETWORK_TYPES_H_ +#include + +#include +#include +#include + +#include "api/transport/ecn_marking.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Configuration + +// Represents constraints and rates related to the currently enabled streams. +// This is used as input to the congestion controller via the StreamsConfig +// struct. +struct RTC_EXPORT BitrateAllocationLimits { + // The total minimum send bitrate required by all sending streams. + DataRate min_allocatable_rate = DataRate::Zero(); + // The total maximum allocatable bitrate for all currently available streams. + DataRate max_allocatable_rate = DataRate::Zero(); + // The max bitrate to use for padding. The sum of the per-stream max padding + // rate. + DataRate max_padding_rate = DataRate::Zero(); +}; + +// Use StreamsConfig for information about streams that is required for specific +// adjustments to the algorithms in network controllers. Especially useful +// for experiments. +struct RTC_EXPORT StreamsConfig { + StreamsConfig(); + StreamsConfig(const StreamsConfig&); + ~StreamsConfig(); + Timestamp at_time = Timestamp::PlusInfinity(); + std::optional requests_alr_probing; + // If `enable_repeated_initial_probing` is set to true, Probes are sent + // periodically every 1s during the first 5s after the network becomes + // available. The probes ignores max_total_allocated_bitrate. + std::optional enable_repeated_initial_probing; + std::optional pacing_factor; + + // TODO(srte): Use BitrateAllocationLimits here. + std::optional min_total_allocated_bitrate; + std::optional max_padding_rate; + std::optional max_total_allocated_bitrate; +}; + +struct RTC_EXPORT TargetRateConstraints { + TargetRateConstraints(); + TargetRateConstraints(const TargetRateConstraints&); + ~TargetRateConstraints(); + Timestamp at_time = Timestamp::PlusInfinity(); + std::optional min_data_rate; + std::optional max_data_rate; + // The initial bandwidth estimate to base target rate on. This should be used + // as the basis for initial OnTargetTransferRate and OnPacerConfig callbacks. + std::optional starting_rate; +}; + +// Send side information + +struct RTC_EXPORT NetworkAvailability { + Timestamp at_time = Timestamp::PlusInfinity(); + bool network_available = false; +}; + +struct RTC_EXPORT NetworkRouteChange { + NetworkRouteChange(); + NetworkRouteChange(const NetworkRouteChange&); + ~NetworkRouteChange(); + Timestamp at_time = Timestamp::PlusInfinity(); + // The TargetRateConstraints are set here so they can be changed synchronously + // when network route changes. + TargetRateConstraints constraints; +}; + +struct RTC_EXPORT PacedPacketInfo { + PacedPacketInfo(); + PacedPacketInfo(int probe_cluster_id, + int probe_cluster_min_probes, + int probe_cluster_min_bytes); + + bool operator==(const PacedPacketInfo& rhs) const; + + // TODO(srte): Move probing info to a separate, optional struct. + static constexpr int kNotAProbe = -1; + DataRate send_bitrate = DataRate::BitsPerSec(0); + int probe_cluster_id = kNotAProbe; + int probe_cluster_min_probes = -1; + int probe_cluster_min_bytes = -1; + int probe_cluster_bytes_sent = 0; +}; + +struct RTC_EXPORT SentPacket { + Timestamp send_time = Timestamp::PlusInfinity(); + // Size of packet with overhead up to IP layer. + DataSize size = DataSize::Zero(); + // Size of preceeding packets that are not part of feedback. + DataSize prior_unacked_data = DataSize::Zero(); + // Probe cluster id and parameters including bitrate, number of packets and + // number of bytes. + PacedPacketInfo pacing_info; + // True if the packet is an audio packet, false for video, padding, RTX etc. + bool audio = false; + // Transport independent sequence number, any tracked packet should have a + // sequence number that is unique over the whole call and increasing by 1 for + // each packet. + int64_t sequence_number; + // Tracked data in flight when the packet was sent, excluding unacked data. + DataSize data_in_flight = DataSize::Zero(); +}; + +struct RTC_EXPORT ReceivedPacket { + Timestamp send_time = Timestamp::MinusInfinity(); + Timestamp receive_time = Timestamp::PlusInfinity(); + DataSize size = DataSize::Zero(); +}; + +// Transport level feedback + +struct RTC_EXPORT RemoteBitrateReport { + Timestamp receive_time = Timestamp::PlusInfinity(); + DataRate bandwidth = DataRate::Infinity(); +}; + +struct RTC_EXPORT RoundTripTimeUpdate { + Timestamp receive_time = Timestamp::PlusInfinity(); + TimeDelta round_trip_time = TimeDelta::PlusInfinity(); + bool smoothed = false; +}; + +struct RTC_EXPORT TransportLossReport { + Timestamp receive_time = Timestamp::PlusInfinity(); + Timestamp start_time = Timestamp::PlusInfinity(); + Timestamp end_time = Timestamp::PlusInfinity(); + uint64_t packets_lost_delta = 0; + uint64_t packets_received_delta = 0; +}; + +// Packet level feedback + +struct RTC_EXPORT PacketResult { + class ReceiveTimeOrder { + public: + bool operator()(const PacketResult& lhs, const PacketResult& rhs); + }; + + PacketResult(); + PacketResult(const PacketResult&); + ~PacketResult(); + + inline bool IsReceived() const { return !receive_time.IsPlusInfinity(); } + + SentPacket sent_packet; + Timestamp receive_time = Timestamp::PlusInfinity(); + EcnMarking ecn = EcnMarking::kNotEct; +}; + +struct RTC_EXPORT TransportPacketsFeedback { + TransportPacketsFeedback(); + TransportPacketsFeedback(const TransportPacketsFeedback& other); + ~TransportPacketsFeedback(); + + Timestamp feedback_time = Timestamp::PlusInfinity(); + DataSize data_in_flight = DataSize::Zero(); + bool transport_supports_ecn = false; + std::vector packet_feedbacks; + + // Arrival times for messages without send time information. + std::vector sendless_arrival_times; + + std::vector ReceivedWithSendInfo() const; + std::vector LostWithSendInfo() const; + std::vector PacketsWithFeedback() const; + std::vector SortedByReceiveTime() const; +}; + +// Network estimation + +struct RTC_EXPORT NetworkEstimate { + Timestamp at_time = Timestamp::PlusInfinity(); + // Deprecated, use TargetTransferRate::target_rate instead. + DataRate bandwidth = DataRate::Infinity(); + TimeDelta round_trip_time = TimeDelta::PlusInfinity(); + TimeDelta bwe_period = TimeDelta::PlusInfinity(); + + float loss_rate_ratio = 0; +}; + +// Network control + +struct RTC_EXPORT PacerConfig { + Timestamp at_time = Timestamp::PlusInfinity(); + // Pacer should send at most data_window data over time_window duration. + DataSize data_window = DataSize::Infinity(); + TimeDelta time_window = TimeDelta::PlusInfinity(); + // Pacer should send at least pad_window data over time_window duration. + DataSize pad_window = DataSize::Zero(); + DataRate data_rate() const { return data_window / time_window; } + DataRate pad_rate() const { return pad_window / time_window; } +}; + +struct RTC_EXPORT ProbeClusterConfig { + Timestamp at_time = Timestamp::PlusInfinity(); + DataRate target_data_rate = DataRate::Zero(); + // Duration of a probe. + TimeDelta target_duration = TimeDelta::Zero(); + // Delta time between sent bursts of packets during probe. + TimeDelta min_probe_delta = TimeDelta::Millis(2); + int32_t target_probe_count = 0; + int32_t id = 0; +}; + +struct RTC_EXPORT TargetTransferRate { + Timestamp at_time = Timestamp::PlusInfinity(); + // The estimate on which the target rate is based on. + NetworkEstimate network_estimate; + DataRate target_rate = DataRate::Zero(); + DataRate stable_target_rate = DataRate::Zero(); + double cwnd_reduce_ratio = 0; +}; + +// Contains updates of network controller comand state. Using optionals to +// indicate whether a member has been updated. The array of probe clusters +// should be used to send out probes if not empty. +struct RTC_EXPORT NetworkControlUpdate { + NetworkControlUpdate(); + NetworkControlUpdate(const NetworkControlUpdate&); + ~NetworkControlUpdate(); + + bool has_updates() const { + return congestion_window.has_value() || pacer_config.has_value() || + !probe_cluster_configs.empty() || target_rate.has_value(); + } + + std::optional congestion_window; + std::optional pacer_config; + std::vector probe_cluster_configs; + std::optional target_rate; +}; + +// Process control +struct RTC_EXPORT ProcessInterval { + Timestamp at_time = Timestamp::PlusInfinity(); + std::optional pacer_queue; +}; + +// Under development, subject to change without notice. +struct RTC_EXPORT NetworkStateEstimate { + double confidence = NAN; + // The time the estimate was received/calculated. + Timestamp update_time = Timestamp::MinusInfinity(); + Timestamp last_receive_time = Timestamp::MinusInfinity(); + Timestamp last_send_time = Timestamp::MinusInfinity(); + + // Total estimated link capacity. + DataRate link_capacity = DataRate::MinusInfinity(); + // Used as a safe measure of available capacity. + DataRate link_capacity_lower = DataRate::MinusInfinity(); + // Used as limit for increasing bitrate. + DataRate link_capacity_upper = DataRate::MinusInfinity(); + + TimeDelta pre_link_buffer_delay = TimeDelta::MinusInfinity(); + TimeDelta post_link_buffer_delay = TimeDelta::MinusInfinity(); + TimeDelta propagation_delay = TimeDelta::MinusInfinity(); + + // Only for debugging + TimeDelta time_delta = TimeDelta::MinusInfinity(); + Timestamp last_feed_time = Timestamp::MinusInfinity(); + double cross_delay_rate = NAN; + double spike_delay_rate = NAN; + DataRate link_capacity_std_dev = DataRate::MinusInfinity(); + DataRate link_capacity_min = DataRate::MinusInfinity(); + double cross_traffic_ratio = NAN; +}; +} // namespace webrtc + +#endif // API_TRANSPORT_NETWORK_TYPES_H_ diff --git a/pkg/apm/webrtc/api/transport/rtp/corruption_detection_message.h b/pkg/apm/webrtc/api/transport/rtp/corruption_detection_message.h new file mode 100644 index 00000000..4364c4bf --- /dev/null +++ b/pkg/apm/webrtc/api/transport/rtp/corruption_detection_message.h @@ -0,0 +1,153 @@ +/* + * Copyright 2024 The WebRTC project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_TRANSPORT_RTP_CORRUPTION_DETECTION_MESSAGE_H_ +#define API_TRANSPORT_RTP_CORRUPTION_DETECTION_MESSAGE_H_ + +#include +#include + +#include "absl/container/inlined_vector.h" +#include "api/array_view.h" + +namespace webrtc { + +class CorruptionDetectionMessage { + public: + class Builder; + + CorruptionDetectionMessage() = default; + + CorruptionDetectionMessage(const CorruptionDetectionMessage&) = default; + CorruptionDetectionMessage& operator=(const CorruptionDetectionMessage&) = + default; + + ~CorruptionDetectionMessage() = default; + + int sequence_index() const { return sequence_index_; } + bool interpret_sequence_index_as_most_significant_bits() const { + return interpret_sequence_index_as_most_significant_bits_; + } + double std_dev() const { return std_dev_; } + int luma_error_threshold() const { return luma_error_threshold_; } + int chroma_error_threshold() const { return chroma_error_threshold_; } + ArrayView sample_values() const { + return MakeArrayView(sample_values_.data(), sample_values_.size()); + } + + private: + friend class CorruptionDetectionExtension; + + static const size_t kMaxSampleSize = 13; + + // Sequence index in the Halton sequence. + // Valid values: [0, 2^7-1] + int sequence_index_ = 0; + + // Whether to interpret the `sequence_index_` as the most significant bits of + // the true sequence index. + bool interpret_sequence_index_as_most_significant_bits_ = false; + + // Standard deviation of the Gaussian filter kernel. + // Valid values: [0, 40.0] + double std_dev_ = 0.0; + + // Corruption threshold for the luma layer. + // Valid values: [0, 2^4 - 1] + int luma_error_threshold_ = 0; + + // Corruption threshold for the chroma layer. + // Valid values: [0, 2^4 - 1] + int chroma_error_threshold_ = 0; + + // An ordered list of samples that are the result of applying the Gaussian + // filter on the image. The coordinates of the samples and their layer are + // determined by the Halton sequence. + // An empty list should be interpreted as a way to keep the `sequence_index` + // in sync. + absl::InlinedVector sample_values_; +}; + +class CorruptionDetectionMessage::Builder { + public: + Builder() = default; + + Builder(const Builder&) = default; + Builder& operator=(const Builder&) = default; + + ~Builder() = default; + + std::optional Build() { + if (message_.sequence_index_ < 0 || + message_.sequence_index_ > 0b0111'1111) { + return std::nullopt; + } + if (message_.std_dev_ < 0.0 || message_.std_dev_ > 40.0) { + return std::nullopt; + } + if (message_.luma_error_threshold_ < 0 || + message_.luma_error_threshold_ > 15) { + return std::nullopt; + } + if (message_.chroma_error_threshold_ < 0 || + message_.chroma_error_threshold_ > 15) { + return std::nullopt; + } + if (message_.sample_values_.size() > kMaxSampleSize) { + return std::nullopt; + } + for (double sample_value : message_.sample_values_) { + if (sample_value < 0.0 || sample_value > 255.0) { + return std::nullopt; + } + } + return message_; + } + + Builder& WithSequenceIndex(int sequence_index) { + message_.sequence_index_ = sequence_index; + return *this; + } + + Builder& WithInterpretSequenceIndexAsMostSignificantBits( + bool interpret_sequence_index_as_most_significant_bits) { + message_.interpret_sequence_index_as_most_significant_bits_ = + interpret_sequence_index_as_most_significant_bits; + return *this; + } + + Builder& WithStdDev(double std_dev) { + message_.std_dev_ = std_dev; + return *this; + } + + Builder& WithLumaErrorThreshold(int luma_error_threshold) { + message_.luma_error_threshold_ = luma_error_threshold; + return *this; + } + + Builder& WithChromaErrorThreshold(int chroma_error_threshold) { + message_.chroma_error_threshold_ = chroma_error_threshold; + return *this; + } + + Builder& WithSampleValues(const ArrayView& sample_values) { + message_.sample_values_.assign(sample_values.cbegin(), + sample_values.cend()); + return *this; + } + + private: + CorruptionDetectionMessage message_; +}; + +} // namespace webrtc + +#endif // API_TRANSPORT_RTP_CORRUPTION_DETECTION_MESSAGE_H_ diff --git a/pkg/apm/webrtc/api/transport/rtp/dependency_descriptor.h b/pkg/apm/webrtc/api/transport/rtp/dependency_descriptor.h new file mode 100644 index 00000000..2ff5ed89 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/rtp/dependency_descriptor.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_TRANSPORT_RTP_DEPENDENCY_DESCRIPTOR_H_ +#define API_TRANSPORT_RTP_DEPENDENCY_DESCRIPTOR_H_ + +#include + +#include +#include +#include +#include + +#include "absl/container/inlined_vector.h" +#include "absl/strings/string_view.h" +#include "api/video/render_resolution.h" + +namespace webrtc { +// Structures to build and parse dependency descriptor as described in +// https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension + +// Relationship of a frame to a Decode target. +enum class DecodeTargetIndication { + kNotPresent = 0, // DecodeTargetInfo symbol '-' + kDiscardable = 1, // DecodeTargetInfo symbol 'D' + kSwitch = 2, // DecodeTargetInfo symbol 'S' + kRequired = 3 // DecodeTargetInfo symbol 'R' +}; + +struct FrameDependencyTemplate { + // Setters are named briefly to chain them when building the template. + FrameDependencyTemplate& S(int spatial_layer); + FrameDependencyTemplate& T(int temporal_layer); + FrameDependencyTemplate& Dtis(absl::string_view dtis); + FrameDependencyTemplate& FrameDiffs(std::initializer_list diffs); + FrameDependencyTemplate& ChainDiffs(std::initializer_list diffs); + + friend bool operator==(const FrameDependencyTemplate& lhs, + const FrameDependencyTemplate& rhs) { + return lhs.spatial_id == rhs.spatial_id && + lhs.temporal_id == rhs.temporal_id && + lhs.decode_target_indications == rhs.decode_target_indications && + lhs.frame_diffs == rhs.frame_diffs && + lhs.chain_diffs == rhs.chain_diffs; + } + + int spatial_id = 0; + int temporal_id = 0; + absl::InlinedVector decode_target_indications; + absl::InlinedVector frame_diffs; + absl::InlinedVector chain_diffs; +}; + +struct FrameDependencyStructure { + friend bool operator==(const FrameDependencyStructure& lhs, + const FrameDependencyStructure& rhs) { + return lhs.num_decode_targets == rhs.num_decode_targets && + lhs.num_chains == rhs.num_chains && + lhs.decode_target_protected_by_chain == + rhs.decode_target_protected_by_chain && + lhs.resolutions == rhs.resolutions && lhs.templates == rhs.templates; + } + + int structure_id = 0; + int num_decode_targets = 0; + int num_chains = 0; + // If chains are used (num_chains > 0), maps decode target index into index of + // the chain protecting that target. + absl::InlinedVector decode_target_protected_by_chain; + absl::InlinedVector resolutions; + std::vector templates; +}; + +class DependencyDescriptorMandatory { + public: + void set_frame_number(int frame_number) { frame_number_ = frame_number; } + int frame_number() const { return frame_number_; } + + void set_template_id(int template_id) { template_id_ = template_id; } + int template_id() const { return template_id_; } + + void set_first_packet_in_frame(bool first) { first_packet_in_frame_ = first; } + bool first_packet_in_frame() const { return first_packet_in_frame_; } + + void set_last_packet_in_frame(bool last) { last_packet_in_frame_ = last; } + bool last_packet_in_frame() const { return last_packet_in_frame_; } + + private: + int frame_number_; + int template_id_; + bool first_packet_in_frame_; + bool last_packet_in_frame_; +}; + +struct DependencyDescriptor { + static constexpr int kMaxSpatialIds = 4; + static constexpr int kMaxTemporalIds = 8; + static constexpr int kMaxDecodeTargets = 32; + static constexpr int kMaxTemplates = 64; + + bool first_packet_in_frame = true; + bool last_packet_in_frame = true; + int frame_number = 0; + FrameDependencyTemplate frame_dependencies; + std::optional resolution; + std::optional active_decode_targets_bitmask; + std::unique_ptr attached_structure; +}; + +// Below are implementation details. +namespace webrtc_impl { +absl::InlinedVector StringToDecodeTargetIndications( + absl::string_view indication_symbols); +} // namespace webrtc_impl + +inline FrameDependencyTemplate& FrameDependencyTemplate::S(int spatial_layer) { + this->spatial_id = spatial_layer; + return *this; +} +inline FrameDependencyTemplate& FrameDependencyTemplate::T(int temporal_layer) { + this->temporal_id = temporal_layer; + return *this; +} +inline FrameDependencyTemplate& FrameDependencyTemplate::Dtis( + absl::string_view dtis) { + this->decode_target_indications = + webrtc_impl::StringToDecodeTargetIndications(dtis); + return *this; +} +inline FrameDependencyTemplate& FrameDependencyTemplate::FrameDiffs( + std::initializer_list diffs) { + this->frame_diffs.assign(diffs.begin(), diffs.end()); + return *this; +} +inline FrameDependencyTemplate& FrameDependencyTemplate::ChainDiffs( + std::initializer_list diffs) { + this->chain_diffs.assign(diffs.begin(), diffs.end()); + return *this; +} + +} // namespace webrtc + +#endif // API_TRANSPORT_RTP_DEPENDENCY_DESCRIPTOR_H_ diff --git a/pkg/apm/webrtc/api/transport/rtp/rtp_source.h b/pkg/apm/webrtc/api/transport/rtp/rtp_source.h new file mode 100644 index 00000000..4732044b --- /dev/null +++ b/pkg/apm/webrtc/api/transport/rtp/rtp_source.h @@ -0,0 +1,106 @@ +/* + * 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_TRANSPORT_RTP_RTP_SOURCE_H_ +#define API_TRANSPORT_RTP_RTP_SOURCE_H_ + +#include + +#include + +#include "api/rtp_headers.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" + +namespace webrtc { + +enum class RtpSourceType { + SSRC, + CSRC, +}; + +class RtpSource { + public: + struct Extensions { + std::optional audio_level; + + // Fields from the Absolute Capture Time header extension: + // http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time + std::optional absolute_capture_time; + + // Clock offset between the local clock and the capturer's clock. + // Do not confuse with `AbsoluteCaptureTime::estimated_capture_clock_offset` + // which instead represents the clock offset between a remote sender and the + // capturer. The following holds: + // Capture's NTP Clock = Local NTP Clock + Local-Capture Clock Offset + std::optional local_capture_clock_offset; + }; + + RtpSource() = delete; + + RtpSource(Timestamp timestamp, + uint32_t source_id, + RtpSourceType source_type, + uint32_t rtp_timestamp, + const RtpSource::Extensions& extensions) + : timestamp_(timestamp), + source_id_(source_id), + source_type_(source_type), + extensions_(extensions), + rtp_timestamp_(rtp_timestamp) {} + + RtpSource(const RtpSource&) = default; + RtpSource& operator=(const RtpSource&) = default; + ~RtpSource() = default; + + Timestamp timestamp() const { return timestamp_; } + + // The identifier of the source can be the CSRC or the SSRC. + uint32_t source_id() const { return source_id_; } + + // The source can be either a contributing source or a synchronization source. + RtpSourceType source_type() const { return source_type_; } + + std::optional audio_level() const { return extensions_.audio_level; } + + void set_audio_level(const std::optional& level) { + extensions_.audio_level = level; + } + + uint32_t rtp_timestamp() const { return rtp_timestamp_; } + + std::optional absolute_capture_time() const { + return extensions_.absolute_capture_time; + } + + std::optional local_capture_clock_offset() const { + return extensions_.local_capture_clock_offset; + } + + bool operator==(const RtpSource& o) const { + return timestamp_ == o.timestamp() && source_id_ == o.source_id() && + source_type_ == o.source_type() && + extensions_.audio_level == o.extensions_.audio_level && + extensions_.absolute_capture_time == + o.extensions_.absolute_capture_time && + rtp_timestamp_ == o.rtp_timestamp(); + } + + private: + Timestamp timestamp_; + uint32_t source_id_; + RtpSourceType source_type_; + RtpSource::Extensions extensions_; + uint32_t rtp_timestamp_; +}; + +} // namespace webrtc + +#endif // API_TRANSPORT_RTP_RTP_SOURCE_H_ diff --git a/pkg/apm/webrtc/api/transport/sctp_transport_factory_interface.h b/pkg/apm/webrtc/api/transport/sctp_transport_factory_interface.h new file mode 100644 index 00000000..d6249941 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/sctp_transport_factory_interface.h @@ -0,0 +1,41 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_TRANSPORT_SCTP_TRANSPORT_FACTORY_INTERFACE_H_ +#define API_TRANSPORT_SCTP_TRANSPORT_FACTORY_INTERFACE_H_ + +#include + +#include "api/environment/environment.h" + +// These classes are not part of the API, and are treated as opaque pointers. + +namespace webrtc { + +class DtlsTransportInternal; +class SctpTransportInternal; + +// Factory class which can be used to allow fake SctpTransports to be injected +// for testing. An application is not intended to implement this interface nor +// 'webrtc::SctpTransportInternal' because SctpTransportInternal is not +// guaranteed to remain stable in future WebRTC versions. +class SctpTransportFactoryInterface { + public: + virtual ~SctpTransportFactoryInterface() = default; + + // Create an SCTP transport using `channel` for the underlying transport. + virtual std::unique_ptr CreateSctpTransport( + const Environment& env, + DtlsTransportInternal* channel) = 0; +}; + +} // namespace webrtc + +#endif // API_TRANSPORT_SCTP_TRANSPORT_FACTORY_INTERFACE_H_ diff --git a/pkg/apm/webrtc/api/transport/stun.h b/pkg/apm/webrtc/api/transport/stun.h new file mode 100644 index 00000000..80cce735 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/stun.h @@ -0,0 +1,907 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_TRANSPORT_STUN_H_ +#define API_TRANSPORT_STUN_H_ + +// This file contains classes for dealing with the STUN protocol, as specified +// in RFC 5389, and its descendants. + +#include +#include + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "rtc_base/byte_buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/ip_address.h" +#include "rtc_base/net_helpers.h" +#include "rtc_base/socket_address.h" + +namespace webrtc { + +// These are the types of STUN messages defined in RFC 5389. +enum StunMessageType : uint16_t { + STUN_INVALID_MESSAGE_TYPE = 0x0000, + STUN_BINDING_REQUEST = 0x0001, + STUN_BINDING_INDICATION = 0x0011, + STUN_BINDING_RESPONSE = 0x0101, + STUN_BINDING_ERROR_RESPONSE = 0x0111, + + // Method 0x80, GOOG-PING is a variant of STUN BINDING + // that is sent instead of a STUN BINDING if the binding + // was identical to the one before. + GOOG_PING_REQUEST = 0x200, + GOOG_PING_RESPONSE = 0x300, + GOOG_PING_ERROR_RESPONSE = 0x310, +}; + +// These are all known STUN attributes, defined in RFC 5389 and elsewhere. +// Next to each is the name of the class (T is StunTAttribute) that implements +// that type. +// RETRANSMIT_COUNT is the number of outstanding pings without a response at +// the time the packet is generated. +enum StunAttributeType { + STUN_ATTR_MAPPED_ADDRESS = 0x0001, // Address + STUN_ATTR_USERNAME = 0x0006, // ByteString + STUN_ATTR_MESSAGE_INTEGRITY = 0x0008, // ByteString, 20 bytes + STUN_ATTR_ERROR_CODE = 0x0009, // ErrorCode + STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000a, // UInt16List + STUN_ATTR_REALM = 0x0014, // ByteString + STUN_ATTR_NONCE = 0x0015, // ByteString + STUN_ATTR_XOR_MAPPED_ADDRESS = 0x0020, // XorAddress + STUN_ATTR_SOFTWARE = 0x8022, // ByteString + STUN_ATTR_ALTERNATE_SERVER = 0x8023, // Address + STUN_ATTR_FINGERPRINT = 0x8028, // UInt32 + STUN_ATTR_RETRANSMIT_COUNT = 0xFF00 // UInt32 +}; + +// These are the types of the values associated with the attributes above. +// This allows us to perform some basic validation when reading or adding +// attributes. Note that these values are for our own use, and not defined in +// RFC 5389. +enum StunAttributeValueType { + STUN_VALUE_UNKNOWN = 0, + STUN_VALUE_ADDRESS = 1, + STUN_VALUE_XOR_ADDRESS = 2, + STUN_VALUE_UINT32 = 3, + STUN_VALUE_UINT64 = 4, + STUN_VALUE_BYTE_STRING = 5, + STUN_VALUE_ERROR_CODE = 6, + STUN_VALUE_UINT16_LIST = 7 +}; + +// These are the types of STUN addresses defined in RFC 5389. +enum StunAddressFamily { + // NB: UNDEF is not part of the STUN spec. + STUN_ADDRESS_UNDEF = 0, + STUN_ADDRESS_IPV4 = 1, + STUN_ADDRESS_IPV6 = 2 +}; + +// These are the types of STUN error codes defined in RFC 5389. +enum StunErrorCode { + // Not an actual error from RFC 5389 and not emitted via icecandidateerror. + STUN_ERROR_NOT_AN_ERROR = 0, + STUN_ERROR_TRY_ALTERNATE = 300, + STUN_ERROR_BAD_REQUEST = 400, + STUN_ERROR_UNAUTHORIZED = 401, + STUN_ERROR_UNKNOWN_ATTRIBUTE = 420, + STUN_ERROR_STALE_NONCE = 438, + STUN_ERROR_SERVER_ERROR = 500, + STUN_ERROR_GLOBAL_FAILURE = 600, + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnectioniceerrorevent-errorcode + STUN_ERROR_SERVER_NOT_REACHABLE = 701, +}; + +// Strings for the error codes above. +extern const char STUN_ERROR_REASON_TRY_ALTERNATE_SERVER[]; +extern const char STUN_ERROR_REASON_BAD_REQUEST[]; +extern const char STUN_ERROR_REASON_UNAUTHORIZED[]; +extern const char STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE[]; +extern const char STUN_ERROR_REASON_STALE_NONCE[]; +extern const char STUN_ERROR_REASON_SERVER_ERROR[]; + +// The mask used to determine whether a STUN message is a request/response etc. +const uint32_t kStunTypeMask = 0x0110; + +// STUN Attribute header length. +const size_t kStunAttributeHeaderSize = 4; + +// Following values correspond to RFC5389. +const size_t kStunHeaderSize = 20; +const size_t kStunTransactionIdOffset = 8; +const size_t kStunTransactionIdLength = 12; +const uint32_t kStunMagicCookie = 0x2112A442; +constexpr size_t kStunMagicCookieLength = sizeof(kStunMagicCookie); + +// Following value corresponds to an earlier version of STUN from +// RFC3489. +const size_t kStunLegacyTransactionIdLength = 16; + +// STUN Message Integrity HMAC length. +const size_t kStunMessageIntegritySize = 20; +// Size of STUN_ATTR_MESSAGE_INTEGRITY_32 +const size_t kStunMessageIntegrity32Size = 4; + +class StunAddressAttribute; +class StunAttribute; +class StunByteStringAttribute; +class StunErrorCodeAttribute; +class StunUInt16ListAttribute; +class StunUInt32Attribute; +class StunUInt64Attribute; +class StunXorAddressAttribute; + +// Records a complete STUN/TURN message. Each message consists of a type and +// any number of attributes. Each attribute is parsed into an instance of an +// appropriate class (see above). The Get* methods will return instances of +// that attribute class. +class StunMessage { + public: + // Constructs a StunMessage with an invalid type and empty, legacy length + // (16 bytes, RFC3489) transaction id. + StunMessage(); + + // Construct a `StunMessage` with a specific type and generate a new + // 12 byte transaction id (RFC5389). + explicit StunMessage(uint16_t type); + + StunMessage(uint16_t type, absl::string_view transaction_id); + + virtual ~StunMessage(); + + // The verification status of the message. This is checked on parsing, + // or set by AddMessageIntegrity. + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class IntegrityStatus { + kNotSet = 0, + kNoIntegrity = 1, // Message-integrity attribute missing + kIntegrityOk = 2, // Message-integrity checked OK + kIntegrityBad = 3, // Message-integrity verification failed + kMaxValue = kIntegrityBad, + }; + + int type() const { return type_; } + size_t length() const { return length_; } + const std::string& transaction_id() const { return transaction_id_; } + uint32_t reduced_transaction_id() const { return reduced_transaction_id_; } + + // Returns true if the message confirms to RFC3489 rather than + // RFC5389. The main difference between the two versions of the STUN + // protocol is the presence of the magic cookie and different length + // of transaction ID. For outgoing packets the version of the protocol + // is determined by the lengths of the transaction ID. + bool IsLegacy() const; + + [[deprecated]] void SetType(int type) { type_ = static_cast(type); } + [[deprecated]] bool SetTransactionID(absl::string_view transaction_id) { + if (!IsValidTransactionId(transaction_id)) + return false; + SetTransactionIdForTesting(transaction_id); + return true; + } + + // Get a list of all of the attribute types in the "comprehension required" + // range that were not recognized. + std::vector GetNonComprehendedAttributes() const; + + // Gets the desired attribute value, or NULL if no such attribute type exists. + const StunAddressAttribute* GetAddress(int type) const; + const StunUInt32Attribute* GetUInt32(int type) const; + const StunUInt64Attribute* GetUInt64(int type) const; + const StunByteStringAttribute* GetByteString(int type) const; + const StunUInt16ListAttribute* GetUInt16List(int type) const; + + // Gets these specific attribute values. + const StunErrorCodeAttribute* GetErrorCode() const; + // Returns the code inside the error code attribute, if present, and + // STUN_ERROR_GLOBAL_FAILURE otherwise. + int GetErrorCodeValue() const; + const StunUInt16ListAttribute* GetUnknownAttributes() const; + + // Takes ownership of the specified attribute and adds it to the message. + void AddAttribute(std::unique_ptr attr); + + // Remove the last occurrence of an attribute. + std::unique_ptr RemoveAttribute(int type); + + // Remote all attributes and releases them. + void ClearAttributes(); + + // Validates that a STUN message has a correct MESSAGE-INTEGRITY value. + // This uses the buffered raw-format message stored by Read(). + IntegrityStatus ValidateMessageIntegrity(const std::string& password); + + // Revalidates the STUN message with (possibly) a new password. + // Indicates that calling logic needs review - probably previous call + // was checking with the wrong password. + IntegrityStatus RevalidateMessageIntegrity(const std::string& password); + + // Returns the current integrity status of the message. + IntegrityStatus integrity() const { return integrity_; } + + // Shortcut for checking if integrity is verified. + bool IntegrityOk() const { + return integrity_ == IntegrityStatus::kIntegrityOk; + } + + // Returns the password attribute used to set or check the integrity. + // Can only be called after adding or checking the integrity. + std::string password() const { + RTC_DCHECK(integrity_ != IntegrityStatus::kNotSet); + return password_; + } + + // Adds a MESSAGE-INTEGRITY attribute that is valid for the current message. + bool AddMessageIntegrity(absl::string_view password); + + // Adds a STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32 attribute that is valid for the + // current message. + bool AddMessageIntegrity32(absl::string_view password); + + // Verify that a buffer has stun magic cookie and one of the specified + // methods. Note that it does not check for the existance of FINGERPRINT. + static bool IsStunMethod(ArrayView methods, + const char* data, + size_t size); + + // Verifies that a given buffer is STUN by checking for a correct FINGERPRINT. + static bool ValidateFingerprint(const char* data, size_t size); + + // Generates a new 12 byte (RFC5389) transaction id. + static std::string GenerateTransactionId(); + + // Adds a FINGERPRINT attribute that is valid for the current message. + bool AddFingerprint(); + + // Parses the STUN packet in the given buffer and records it here. The + // return value indicates whether this was successful. + bool Read(ByteBufferReader* buf); + + // Writes this object into a STUN packet. The return value indicates whether + // this was successful. + bool Write(ByteBufferWriter* buf) const; + + // Creates an empty message. Overridable by derived classes. + virtual StunMessage* CreateNew() const; + + // Modify the stun magic cookie used for this STUN message. + // This is used for testing. + [[deprecated]] void SetStunMagicCookie(uint32_t val); + + // Change the internal transaction id. Used only for testing. + void SetTransactionIdForTesting(absl::string_view transaction_id); + + // Contruct a copy of `this`. + std::unique_ptr Clone() const; + + // Check if the attributes of this StunMessage equals those of `other` + // for all attributes that `attribute_type_mask` return true + bool EqualAttributes(const StunMessage* other, + std::function attribute_type_mask) const; + + // Expose raw-buffer ValidateMessageIntegrity function for testing. + static bool ValidateMessageIntegrityForTesting(const char* data, + size_t size, + const std::string& password); + // Expose raw-buffer ValidateMessageIntegrity function for testing. + static bool ValidateMessageIntegrity32ForTesting(const char* data, + size_t size, + const std::string& password); + + protected: + // Verifies that the given attribute is allowed for this message. + virtual StunAttributeValueType GetAttributeValueType(int type) const; + + std::vector> attrs_; + + private: + StunAttribute* CreateAttribute(int type, size_t length) /* const*/; + const StunAttribute* GetAttribute(int type) const; + static bool IsValidTransactionId(absl::string_view transaction_id); + bool AddMessageIntegrityOfType(int mi_attr_type, + size_t mi_attr_size, + absl::string_view key); + static bool ValidateMessageIntegrityOfType(int mi_attr_type, + size_t mi_attr_size, + const char* data, + size_t size, + const std::string& password); + + uint16_t type_ = STUN_INVALID_MESSAGE_TYPE; + uint16_t length_ = 0; + std::string transaction_id_; + uint32_t reduced_transaction_id_ = 0; + uint32_t stun_magic_cookie_ = kStunMagicCookie; + // The original buffer for messages created by Read(). + std::string buffer_; + IntegrityStatus integrity_ = IntegrityStatus::kNotSet; + std::string password_; +}; + +// Base class for all STUN/TURN attributes. +class StunAttribute { + public: + virtual ~StunAttribute() {} + + int type() const { return type_; } + size_t length() const { return length_; } + + // Return the type of this attribute. + virtual StunAttributeValueType value_type() const = 0; + + // Only XorAddressAttribute needs this so far. + virtual void SetOwner(StunMessage* /* owner */) {} + + // Reads the body (not the type or length) for this type of attribute from + // the given buffer. Return value is true if successful. + virtual bool Read(ByteBufferReader* buf) = 0; + + // Writes the body (not the type or length) to the given buffer. Return + // value is true if successful. + virtual bool Write(ByteBufferWriter* buf) const = 0; + + // Creates an attribute object with the given type and smallest length. + static StunAttribute* Create(StunAttributeValueType value_type, + uint16_t type, + uint16_t length, + StunMessage* owner); + // TODO(?): Allow these create functions to take parameters, to reduce + // the amount of work callers need to do to initialize attributes. + static std::unique_ptr CreateAddress(uint16_t type); + static std::unique_ptr CreateXorAddress( + uint16_t type); + static std::unique_ptr CreateUInt32(uint16_t type); + static std::unique_ptr CreateUInt64(uint16_t type); + static std::unique_ptr CreateByteString( + uint16_t type); + static std::unique_ptr CreateUInt16ListAttribute( + uint16_t type); + static std::unique_ptr CreateErrorCode(); + static std::unique_ptr CreateUnknownAttributes(); + + protected: + StunAttribute(uint16_t type, uint16_t length); + void SetLength(uint16_t length) { length_ = length; } + void WritePadding(ByteBufferWriter* buf) const; + void ConsumePadding(ByteBufferReader* buf) const; + + private: + uint16_t type_; + uint16_t length_; +}; + +// Implements STUN attributes that record an Internet address. +class StunAddressAttribute : public StunAttribute { + public: + static const uint16_t SIZE_UNDEF = 0; + static const uint16_t SIZE_IP4 = 8; + static const uint16_t SIZE_IP6 = 20; + StunAddressAttribute(uint16_t type, const SocketAddress& addr); + StunAddressAttribute(uint16_t type, uint16_t length); + + StunAttributeValueType value_type() const override; + + StunAddressFamily family() const { + switch (address_.ipaddr().family()) { + case AF_INET: + return STUN_ADDRESS_IPV4; + case AF_INET6: + return STUN_ADDRESS_IPV6; + } + return STUN_ADDRESS_UNDEF; + } + + const SocketAddress& GetAddress() const { return address_; } + const IPAddress& ipaddr() const { return address_.ipaddr(); } + uint16_t port() const { return address_.port(); } + + void SetAddress(const SocketAddress& addr) { + address_ = addr; + EnsureAddressLength(); + } + void SetIP(const IPAddress& ip) { + address_.SetIP(ip); + EnsureAddressLength(); + } + void SetPort(uint16_t port) { address_.SetPort(port); } + + bool Read(ByteBufferReader* buf) override; + bool Write(ByteBufferWriter* buf) const override; + + private: + void EnsureAddressLength() { + switch (family()) { + case STUN_ADDRESS_IPV4: { + SetLength(SIZE_IP4); + break; + } + case STUN_ADDRESS_IPV6: { + SetLength(SIZE_IP6); + break; + } + default: { + SetLength(SIZE_UNDEF); + break; + } + } + } + SocketAddress address_; +}; + +// Implements STUN attributes that record an Internet address. When encoded +// in a STUN message, the address contained in this attribute is XORed with the +// transaction ID of the message. +class StunXorAddressAttribute : public StunAddressAttribute { + public: + StunXorAddressAttribute(uint16_t type, const SocketAddress& addr); + StunXorAddressAttribute(uint16_t type, uint16_t length, StunMessage* owner); + + StunAttributeValueType value_type() const override; + void SetOwner(StunMessage* owner) override; + bool Read(ByteBufferReader* buf) override; + bool Write(ByteBufferWriter* buf) const override; + + private: + IPAddress GetXoredIP() const; + StunMessage* owner_; +}; + +// Implements STUN attributes that record a 32-bit integer. +class StunUInt32Attribute : public StunAttribute { + public: + static const uint16_t SIZE = 4; + StunUInt32Attribute(uint16_t type, uint32_t value); + explicit StunUInt32Attribute(uint16_t type); + + StunAttributeValueType value_type() const override; + + uint32_t value() const { return bits_; } + void SetValue(uint32_t bits) { bits_ = bits; } + + bool GetBit(size_t index) const; + void SetBit(size_t index, bool value); + + bool Read(ByteBufferReader* buf) override; + bool Write(ByteBufferWriter* buf) const override; + + private: + uint32_t bits_; +}; + +class StunUInt64Attribute : public StunAttribute { + public: + static const uint16_t SIZE = 8; + StunUInt64Attribute(uint16_t type, uint64_t value); + explicit StunUInt64Attribute(uint16_t type); + + StunAttributeValueType value_type() const override; + + uint64_t value() const { return bits_; } + void SetValue(uint64_t bits) { bits_ = bits; } + + bool Read(ByteBufferReader* buf) override; + bool Write(ByteBufferWriter* buf) const override; + + private: + uint64_t bits_; +}; + +// Implements STUN attributes that record an arbitrary byte string. +class StunByteStringAttribute : public StunAttribute { + public: + explicit StunByteStringAttribute(uint16_t type); + StunByteStringAttribute(uint16_t type, absl::string_view str); + StunByteStringAttribute(uint16_t type, const void* bytes, size_t length); + StunByteStringAttribute(uint16_t type, uint16_t length); + ~StunByteStringAttribute() override; + + StunAttributeValueType value_type() const override; + + [[deprecated("Use array_view")]] const char* bytes() const { + return reinterpret_cast(bytes_); + } + // Returns the attribute value as a string. + // Use this for attributes that are text or text-compatible. + absl::string_view string_view() const { + return absl::string_view(reinterpret_cast(bytes_), length()); + } + // Returns the attribute value as an uint8_t view. + // Use this function for values that are not text. + ArrayView array_view() const { + return MakeArrayView(bytes_, length()); + } + + [[deprecated]] std::string GetString() const { + return std::string(reinterpret_cast(bytes_), length()); + } + + void CopyBytes(const void* bytes, size_t length); + void CopyBytes(absl::string_view bytes); + + uint8_t GetByte(size_t index) const; + void SetByte(size_t index, uint8_t value); + + bool Read(ByteBufferReader* buf) override; + bool Write(ByteBufferWriter* buf) const override; + + private: + void SetBytes(uint8_t* bytes, size_t length); + + uint8_t* bytes_; +}; + +// Implements STUN attributes that record an error code. +class StunErrorCodeAttribute : public StunAttribute { + public: + static const uint16_t MIN_SIZE; + StunErrorCodeAttribute(uint16_t type, int code, const std::string& reason); + StunErrorCodeAttribute(uint16_t type, uint16_t length); + ~StunErrorCodeAttribute() override; + + StunAttributeValueType value_type() const override; + + // The combined error and class, e.g. 0x400. + int code() const; + void SetCode(int code); + + // The individual error components. + int eclass() const { return class_; } + int number() const { return number_; } + const std::string& reason() const { return reason_; } + void SetClass(uint8_t eclass) { class_ = eclass; } + void SetNumber(uint8_t number) { number_ = number; } + void SetReason(const std::string& reason); + + bool Read(ByteBufferReader* buf) override; + bool Write(ByteBufferWriter* buf) const override; + + private: + uint8_t class_; + uint8_t number_; + std::string reason_; +}; + +// Implements STUN attributes that record a list of attribute names. +class StunUInt16ListAttribute : public StunAttribute { + public: + StunUInt16ListAttribute(uint16_t type, uint16_t length); + ~StunUInt16ListAttribute() override; + + StunAttributeValueType value_type() const override; + + size_t Size() const; + uint16_t GetType(int index) const; + void SetType(int index, uint16_t value); + void AddType(uint16_t value); + void AddTypeAtIndex(uint16_t index, uint16_t value); + + bool Read(ByteBufferReader* buf) override; + bool Write(ByteBufferWriter* buf) const override; + + private: + std::vector* attr_types_; +}; + +// Return a string e.g "STUN BINDING request". +std::string StunMethodToString(int msg_type); + +// Returns the (successful) response type for the given request type. +// Returns -1 if `request_type` is not a valid request type. +int GetStunSuccessResponseType(int request_type); + +// Returns the error response type for the given request type. +// Returns -1 if `request_type` is not a valid request type. +int GetStunErrorResponseType(int request_type); + +// Returns whether a given message is a request type. +bool IsStunRequestType(int msg_type); + +// Returns whether a given message is an indication type. +bool IsStunIndicationType(int msg_type); + +// Returns whether a given response is a success type. +bool IsStunSuccessResponseType(int msg_type); + +// Returns whether a given response is an error type. +bool IsStunErrorResponseType(int msg_type); + +// Computes the STUN long-term credential hash. +bool ComputeStunCredentialHash(const std::string& username, + const std::string& realm, + const std::string& password, + std::string* hash); + +// Make a copy af `attribute` and return a new StunAttribute. +// This is useful if you don't care about what kind of attribute you +// are handling. +// +// The implementation copies by calling Write() followed by Read(). +// +// If `tmp_buffer` is supplied this buffer will be used, otherwise +// a buffer will created in the method. +std::unique_ptr CopyStunAttribute( + const StunAttribute& attribute, + ByteBufferWriter* tmp_buffer_ptr = 0); + +// Defined in TURN RFC 5766. +enum TurnMessageType : uint16_t { + STUN_ALLOCATE_REQUEST = 0x0003, + STUN_ALLOCATE_RESPONSE = 0x0103, + STUN_ALLOCATE_ERROR_RESPONSE = 0x0113, + TURN_REFRESH_REQUEST = 0x0004, + TURN_REFRESH_RESPONSE = 0x0104, + TURN_REFRESH_ERROR_RESPONSE = 0x0114, + TURN_SEND_INDICATION = 0x0016, + TURN_DATA_INDICATION = 0x0017, + TURN_CREATE_PERMISSION_REQUEST = 0x0008, + TURN_CREATE_PERMISSION_RESPONSE = 0x0108, + TURN_CREATE_PERMISSION_ERROR_RESPONSE = 0x0118, + TURN_CHANNEL_BIND_REQUEST = 0x0009, + TURN_CHANNEL_BIND_RESPONSE = 0x0109, + TURN_CHANNEL_BIND_ERROR_RESPONSE = 0x0119, +}; + +enum TurnAttributeType { + STUN_ATTR_CHANNEL_NUMBER = 0x000C, // UInt32 + STUN_ATTR_LIFETIME = 0x000d, // UInt32 + STUN_ATTR_XOR_PEER_ADDRESS = 0x0012, // XorAddress + STUN_ATTR_DATA = 0x0013, // ByteString + STUN_ATTR_XOR_RELAYED_ADDRESS = 0x0016, // XorAddress + STUN_ATTR_EVEN_PORT = 0x0018, // ByteString, 1 byte. + STUN_ATTR_REQUESTED_TRANSPORT = 0x0019, // UInt32 + STUN_ATTR_DONT_FRAGMENT = 0x001A, // No content, Length = 0 + STUN_ATTR_RESERVATION_TOKEN = 0x0022, // ByteString, 8 bytes. +}; + +// RFC 5766-defined errors. +enum TurnErrorType { + STUN_ERROR_FORBIDDEN = 403, + STUN_ERROR_ALLOCATION_MISMATCH = 437, + STUN_ERROR_WRONG_CREDENTIALS = 441, + STUN_ERROR_UNSUPPORTED_PROTOCOL = 442 +}; + +[[deprecated("Use STUN_ERROR_SERVER_NOT_REACHABLE")]] extern const int + SERVER_NOT_REACHABLE_ERROR; + +extern const char STUN_ERROR_REASON_FORBIDDEN[]; +extern const char STUN_ERROR_REASON_ALLOCATION_MISMATCH[]; +extern const char STUN_ERROR_REASON_WRONG_CREDENTIALS[]; +extern const char STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL[]; +class TurnMessage : public StunMessage { + public: + using StunMessage::StunMessage; + + protected: + StunAttributeValueType GetAttributeValueType(int type) const override; + StunMessage* CreateNew() const override; +}; + +enum IceAttributeType { + // RFC 5245 ICE STUN attributes. + STUN_ATTR_PRIORITY = 0x0024, // UInt32 + STUN_ATTR_USE_CANDIDATE = 0x0025, // No content, Length = 0 + STUN_ATTR_ICE_CONTROLLED = 0x8029, // UInt64 + STUN_ATTR_ICE_CONTROLLING = 0x802A, // UInt64 + // The following attributes are in the comprehension-optional range + // (0xC000-0xFFFF) and are not registered with IANA. These STUN attributes are + // intended for ICE and should NOT be used in generic use cases of STUN + // messages. + // + // Note that the value 0xC001 has already been assigned by IANA to + // ENF-FLOW-DESCRIPTION + // (https://www.iana.org/assignments/stun-parameters/stun-parameters.xml). + STUN_ATTR_NOMINATION = 0xC001, // UInt32 + // UInt32. The higher 16 bits are the network ID. The lower 16 bits are the + // network cost. + STUN_ATTR_GOOG_NETWORK_INFO = 0xC057, + // Experimental: Transaction ID of the last connectivity check received. + STUN_ATTR_GOOG_LAST_ICE_CHECK_RECEIVED = 0xC058, + // Uint16List. Miscellaneous attributes for future extension. + STUN_ATTR_GOOG_MISC_INFO = 0xC059, + // Obsolete. + STUN_ATTR_GOOG_OBSOLETE_1 = 0xC05A, + STUN_ATTR_GOOG_CONNECTION_ID = 0xC05B, // Not yet implemented. + STUN_ATTR_GOOG_DELTA = 0xC05C, // Not yet implemented. + STUN_ATTR_GOOG_DELTA_ACK = 0xC05D, // Not yet implemented. + STUN_ATTR_GOOG_DELTA_SYNC_REQ = 0xC05E, // Not yet implemented. + // MESSAGE-INTEGRITY truncated to 32-bit. + STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32 = 0xC060, + // Experimental: piggybacking the DTLS handshake in STUN. + STUN_ATTR_META_DTLS_IN_STUN = 0xC070, + STUN_ATTR_META_DTLS_IN_STUN_ACK = 0xC071, +}; + +// When adding new attributes to STUN_ATTR_GOOG_MISC_INFO +// (which is a list of uint16_t), append the indices of these attributes below +// and do NOT change the existing indices. The indices of attributes must be +// consistent with those used in ConnectionRequest::Prepare when forming a STUN +// message for the ICE connectivity check, and they are used when parsing a +// received STUN message. +enum class IceGoogMiscInfoBindingRequestAttributeIndex { + SUPPORT_GOOG_PING_VERSION = 0, +}; + +enum class IceGoogMiscInfoBindingResponseAttributeIndex { + SUPPORT_GOOG_PING_VERSION = 0, +}; + +// RFC 5245-defined errors. +enum IceErrorCode { + STUN_ERROR_ROLE_CONFLICT = 487, +}; +extern const char STUN_ERROR_REASON_ROLE_CONFLICT[]; + +// A RFC 5245 ICE STUN message. +class IceMessage : public StunMessage { + public: + using StunMessage::StunMessage; + + protected: + StunAttributeValueType GetAttributeValueType(int type) const override; + StunMessage* CreateNew() const override; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace cricket { +using ::webrtc::ComputeStunCredentialHash; +using ::webrtc::CopyStunAttribute; +using ::webrtc::GetStunErrorResponseType; +using ::webrtc::GetStunSuccessResponseType; +using ::webrtc::GOOG_PING_ERROR_RESPONSE; +using ::webrtc::GOOG_PING_REQUEST; +using ::webrtc::GOOG_PING_RESPONSE; +using ::webrtc::IceAttributeType; +using ::webrtc::IceErrorCode; +using ::webrtc::IceGoogMiscInfoBindingRequestAttributeIndex; +using ::webrtc::IceGoogMiscInfoBindingResponseAttributeIndex; +using ::webrtc::IceMessage; +using ::webrtc::IsStunErrorResponseType; +using ::webrtc::IsStunIndicationType; +using ::webrtc::IsStunRequestType; +using ::webrtc::IsStunSuccessResponseType; +using ::webrtc::kStunAttributeHeaderSize; +using ::webrtc::kStunHeaderSize; +using ::webrtc::kStunLegacyTransactionIdLength; +using ::webrtc::kStunMagicCookie; +using ::webrtc::kStunMagicCookieLength; +using ::webrtc::kStunMessageIntegrity32Size; +using ::webrtc::kStunMessageIntegritySize; +using ::webrtc::kStunTransactionIdLength; +using ::webrtc::kStunTransactionIdOffset; +using ::webrtc::kStunTypeMask; +using ::webrtc::SERVER_NOT_REACHABLE_ERROR; +using ::webrtc::STUN_ADDRESS_IPV4; +using ::webrtc::STUN_ADDRESS_IPV6; +using ::webrtc::STUN_ADDRESS_UNDEF; +using ::webrtc::STUN_ALLOCATE_ERROR_RESPONSE; +using ::webrtc::STUN_ALLOCATE_REQUEST; +using ::webrtc::STUN_ALLOCATE_RESPONSE; +using ::webrtc::STUN_ATTR_ALTERNATE_SERVER; +using ::webrtc::STUN_ATTR_CHANNEL_NUMBER; +using ::webrtc::STUN_ATTR_DATA; +using ::webrtc::STUN_ATTR_DONT_FRAGMENT; +using ::webrtc::STUN_ATTR_ERROR_CODE; +using ::webrtc::STUN_ATTR_EVEN_PORT; +using ::webrtc::STUN_ATTR_FINGERPRINT; +using ::webrtc::STUN_ATTR_GOOG_CONNECTION_ID; +using ::webrtc::STUN_ATTR_GOOG_DELTA; +using ::webrtc::STUN_ATTR_GOOG_DELTA_ACK; +using ::webrtc::STUN_ATTR_GOOG_DELTA_SYNC_REQ; +using ::webrtc::STUN_ATTR_GOOG_LAST_ICE_CHECK_RECEIVED; +using ::webrtc::STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32; +using ::webrtc::STUN_ATTR_GOOG_MISC_INFO; +using ::webrtc::STUN_ATTR_GOOG_NETWORK_INFO; +using ::webrtc::STUN_ATTR_GOOG_OBSOLETE_1; +using ::webrtc::STUN_ATTR_ICE_CONTROLLED; +using ::webrtc::STUN_ATTR_ICE_CONTROLLING; +using ::webrtc::STUN_ATTR_LIFETIME; +using ::webrtc::STUN_ATTR_MAPPED_ADDRESS; +using ::webrtc::STUN_ATTR_MESSAGE_INTEGRITY; +using ::webrtc::STUN_ATTR_META_DTLS_IN_STUN; +using ::webrtc::STUN_ATTR_META_DTLS_IN_STUN_ACK; +using ::webrtc::STUN_ATTR_NOMINATION; +using ::webrtc::STUN_ATTR_NONCE; +using ::webrtc::STUN_ATTR_PRIORITY; +using ::webrtc::STUN_ATTR_REALM; +using ::webrtc::STUN_ATTR_REQUESTED_TRANSPORT; +using ::webrtc::STUN_ATTR_RESERVATION_TOKEN; +using ::webrtc::STUN_ATTR_RETRANSMIT_COUNT; +using ::webrtc::STUN_ATTR_SOFTWARE; +using ::webrtc::STUN_ATTR_UNKNOWN_ATTRIBUTES; +using ::webrtc::STUN_ATTR_USE_CANDIDATE; +using ::webrtc::STUN_ATTR_USERNAME; +using ::webrtc::STUN_ATTR_XOR_MAPPED_ADDRESS; +using ::webrtc::STUN_ATTR_XOR_PEER_ADDRESS; +using ::webrtc::STUN_ATTR_XOR_RELAYED_ADDRESS; +using ::webrtc::STUN_BINDING_ERROR_RESPONSE; +using ::webrtc::STUN_BINDING_INDICATION; +using ::webrtc::STUN_BINDING_REQUEST; +using ::webrtc::STUN_BINDING_RESPONSE; +using ::webrtc::STUN_ERROR_ALLOCATION_MISMATCH; +using ::webrtc::STUN_ERROR_BAD_REQUEST; +using ::webrtc::STUN_ERROR_FORBIDDEN; +using ::webrtc::STUN_ERROR_GLOBAL_FAILURE; +using ::webrtc::STUN_ERROR_NOT_AN_ERROR; +using ::webrtc::STUN_ERROR_REASON_ALLOCATION_MISMATCH; +using ::webrtc::STUN_ERROR_REASON_BAD_REQUEST; +using ::webrtc::STUN_ERROR_REASON_FORBIDDEN; +using ::webrtc::STUN_ERROR_REASON_ROLE_CONFLICT; +using ::webrtc::STUN_ERROR_REASON_SERVER_ERROR; +using ::webrtc::STUN_ERROR_REASON_STALE_NONCE; +using ::webrtc::STUN_ERROR_REASON_TRY_ALTERNATE_SERVER; +using ::webrtc::STUN_ERROR_REASON_UNAUTHORIZED; +using ::webrtc::STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE; +using ::webrtc::STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL; +using ::webrtc::STUN_ERROR_REASON_WRONG_CREDENTIALS; +using ::webrtc::STUN_ERROR_ROLE_CONFLICT; +using ::webrtc::STUN_ERROR_SERVER_ERROR; +using ::webrtc::STUN_ERROR_SERVER_NOT_REACHABLE; +using ::webrtc::STUN_ERROR_STALE_NONCE; +using ::webrtc::STUN_ERROR_TRY_ALTERNATE; +using ::webrtc::STUN_ERROR_UNAUTHORIZED; +using ::webrtc::STUN_ERROR_UNKNOWN_ATTRIBUTE; +using ::webrtc::STUN_ERROR_UNSUPPORTED_PROTOCOL; +using ::webrtc::STUN_ERROR_WRONG_CREDENTIALS; +using ::webrtc::STUN_INVALID_MESSAGE_TYPE; +using ::webrtc::STUN_VALUE_ADDRESS; +using ::webrtc::STUN_VALUE_BYTE_STRING; +using ::webrtc::STUN_VALUE_ERROR_CODE; +using ::webrtc::STUN_VALUE_UINT16_LIST; +using ::webrtc::STUN_VALUE_UINT32; +using ::webrtc::STUN_VALUE_UINT64; +using ::webrtc::STUN_VALUE_UNKNOWN; +using ::webrtc::STUN_VALUE_XOR_ADDRESS; +using ::webrtc::StunAddressAttribute; +using ::webrtc::StunAddressFamily; +using ::webrtc::StunAttribute; +using ::webrtc::StunAttributeType; +using ::webrtc::StunAttributeValueType; +using ::webrtc::StunByteStringAttribute; +using ::webrtc::StunErrorCode; +using ::webrtc::StunErrorCodeAttribute; +using ::webrtc::StunMessage; +using ::webrtc::StunMessageType; +using ::webrtc::StunMethodToString; +using ::webrtc::StunUInt16ListAttribute; +using ::webrtc::StunUInt32Attribute; +using ::webrtc::StunUInt64Attribute; +using ::webrtc::StunXorAddressAttribute; +using ::webrtc::TURN_CHANNEL_BIND_ERROR_RESPONSE; +using ::webrtc::TURN_CHANNEL_BIND_REQUEST; +using ::webrtc::TURN_CHANNEL_BIND_RESPONSE; +using ::webrtc::TURN_CREATE_PERMISSION_ERROR_RESPONSE; +using ::webrtc::TURN_CREATE_PERMISSION_REQUEST; +using ::webrtc::TURN_CREATE_PERMISSION_RESPONSE; +using ::webrtc::TURN_DATA_INDICATION; +using ::webrtc::TURN_REFRESH_ERROR_RESPONSE; +using ::webrtc::TURN_REFRESH_REQUEST; +using ::webrtc::TURN_REFRESH_RESPONSE; +using ::webrtc::TURN_SEND_INDICATION; +using ::webrtc::TurnAttributeType; +using ::webrtc::TurnErrorType; +using ::webrtc::TurnMessage; +using ::webrtc::TurnMessageType; +} // namespace cricket +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // API_TRANSPORT_STUN_H_ diff --git a/pkg/apm/webrtc/api/transport/transport.go b/pkg/apm/webrtc/api/transport/transport.go new file mode 100644 index 00000000..f122b4d3 --- /dev/null +++ b/pkg/apm/webrtc/api/transport/transport.go @@ -0,0 +1,10 @@ +//go:build console + +package transport + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/api/turn_customizer.h b/pkg/apm/webrtc/api/turn_customizer.h new file mode 100644 index 00000000..af41649f --- /dev/null +++ b/pkg/apm/webrtc/api/turn_customizer.h @@ -0,0 +1,42 @@ +/* + * 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_TURN_CUSTOMIZER_H_ +#define API_TURN_CUSTOMIZER_H_ + +#include + +#include "api/transport/stun.h" +#include "p2p/base/port_interface.h" + +namespace webrtc { + +class TurnCustomizer { + public: + // This is called before a TURN message is sent. + // This could be used to add implementation specific attributes to a request. + virtual void MaybeModifyOutgoingStunMessage(PortInterface* port, + StunMessage* message) = 0; + + // TURN can send data using channel data messages or Send indication. + // This method should return false if `data` should be sent using + // a Send indication instead of a ChannelData message, even if a + // channel is bound. + virtual bool AllowChannelData(PortInterface* port, + const void* data, + size_t size, + bool payload) = 0; + + virtual ~TurnCustomizer() {} +}; + +} // namespace webrtc + +#endif // API_TURN_CUSTOMIZER_H_ diff --git a/pkg/apm/webrtc/api/uma_metrics.h b/pkg/apm/webrtc/api/uma_metrics.h new file mode 100644 index 00000000..1525f085 --- /dev/null +++ b/pkg/apm/webrtc/api/uma_metrics.h @@ -0,0 +1,233 @@ +/* + * Copyright 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file contains enums related to Chrome UMA histograms. See +// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#requirements +// for requirements when adding or changing metrics. + +#ifndef API_UMA_METRICS_H_ +#define API_UMA_METRICS_H_ + +namespace webrtc { + +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum PeerConnectionAddressFamilyCounter { + kPeerConnection_IPv4 = 0, + kPeerConnection_IPv6 = 1, + kBestConnections_IPv4 = 2, + kBestConnections_IPv6 = 3, + kPeerConnectionAddressFamilyCounter_Max +}; + +// This enum defines types for UMA samples, which will have a range. +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum PeerConnectionMetricsName { + kNetworkInterfaces_IPv4 = 0, // Number of IPv4 interfaces. + kNetworkInterfaces_IPv6 = 1, // Number of IPv6 interfaces. + kTimeToConnect = 2, // In milliseconds. + kLocalCandidates_IPv4 = 3, // Number of IPv4 local candidates. + kLocalCandidates_IPv6 = 4, // Number of IPv6 local candidates. + kPeerConnectionMetricsName_Max +}; + +// The IceCandidatePairType has the format of +// _. It is recorded based on the +// type of candidate pair used when the PeerConnection first goes to a completed +// state. When BUNDLE is enabled, only the first transport gets recorded. +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum IceCandidatePairType { + // HostHost is deprecated. It was replaced with the set of types at the bottom + // to report private or public host IP address. + kIceCandidatePairHostHost = 0, + kIceCandidatePairHostSrflx = 1, + kIceCandidatePairHostRelay = 2, + kIceCandidatePairHostPrflx = 3, + kIceCandidatePairSrflxHost = 4, + kIceCandidatePairSrflxSrflx = 5, + kIceCandidatePairSrflxRelay = 6, + kIceCandidatePairSrflxPrflx = 7, + kIceCandidatePairRelayHost = 8, + kIceCandidatePairRelaySrflx = 9, + kIceCandidatePairRelayRelay = 10, + kIceCandidatePairRelayPrflx = 11, + kIceCandidatePairPrflxHost = 12, + kIceCandidatePairPrflxSrflx = 13, + kIceCandidatePairPrflxRelay = 14, + + // The following 9 types tell whether local and remote hosts have hostname, + // private or public IP addresses. + kIceCandidatePairHostPrivateHostPrivate = 15, + kIceCandidatePairHostPrivateHostPublic = 16, + kIceCandidatePairHostPublicHostPrivate = 17, + kIceCandidatePairHostPublicHostPublic = 18, + kIceCandidatePairHostNameHostName = 19, + kIceCandidatePairHostNameHostPrivate = 20, + kIceCandidatePairHostNameHostPublic = 21, + kIceCandidatePairHostPrivateHostName = 22, + kIceCandidatePairHostPublicHostName = 23, + kIceCandidatePairMax +}; + +// The difference between PeerConnectionEnumCounter and +// PeerConnectionMetricsName is that the "EnumCounter" is only counting the +// occurrences of events, while "Name" has a value associated with it which is +// used to form a histogram. + +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum KeyExchangeProtocolMedia { + kEnumCounterKeyProtocolMediaTypeDtlsAudio = 0, + kEnumCounterKeyProtocolMediaTypeDtlsVideo = 1, + kEnumCounterKeyProtocolMediaTypeDtlsData = 2, + kEnumCounterKeyProtocolMediaTypeSdesAudio = 3, + kEnumCounterKeyProtocolMediaTypeSdesVideo = 4, + kEnumCounterKeyProtocolMediaTypeSdesData = 5, + kEnumCounterKeyProtocolMediaTypeMax +}; + +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum SdpSemanticRequested { + kSdpSemanticRequestDefault = 0, + kSdpSemanticRequestPlanB = 1, + kSdpSemanticRequestUnifiedPlan = 2, + kSdpSemanticRequestMax +}; + +// Metric for counting the outcome of adding an ICE candidate +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum AddIceCandidateResult { + kAddIceCandidateSuccess = 0, + kAddIceCandidateFailClosed = 1, + kAddIceCandidateFailNoRemoteDescription = 2, + kAddIceCandidateFailNullCandidate = 3, + kAddIceCandidateFailNotValid = 4, + kAddIceCandidateFailNotReady = 5, + kAddIceCandidateFailInAddition = 6, + kAddIceCandidateFailNotUsable = 7, + kAddIceCandidateMax +}; + +// Metrics for reporting usage of BUNDLE. +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum BundleUsage { + // There are no m-lines in the SDP, only a session description. + kBundleUsageEmpty = 0, + // Only a data channel is negotiated but BUNDLE is not negotiated. + kBundleUsageNoBundleDatachannelOnly = 1, + // BUNDLE is not negotiated and there is at most one m-line per media type, + kBundleUsageNoBundleSimple = 2, + // BUNDLE is not negotiated and there are multiple m-lines per media type, + kBundleUsageNoBundleComplex = 3, + // Only a data channel is negotiated and BUNDLE is negotiated. + kBundleUsageBundleDatachannelOnly = 4, + // BUNDLE is negotiated but there is at most one m-line per media type, + kBundleUsageBundleSimple = 5, + // BUNDLE is negotiated and there are multiple m-lines per media type, + kBundleUsageBundleComplex = 6, + // Legacy plan-b metrics. + kBundleUsageNoBundlePlanB = 7, + kBundleUsageBundlePlanB = 8, + kBundleUsageMax +}; + +// Metrics for reporting configured BUNDLE policy, mapping directly to +// https://w3c.github.io/webrtc-pc/#rtcbundlepolicy-enum +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum BundlePolicyUsage { + kBundlePolicyUsageBalanced = 0, + kBundlePolicyUsageMaxBundle = 1, + kBundlePolicyUsageMaxCompat = 2, + kBundlePolicyUsageMax +}; + +// Metrics for provisional answers as described in +// https://datatracker.ietf.org/doc/html/rfc8829#section-4.1.10.1 +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum ProvisionalAnswerUsage { + kProvisionalAnswerNotUsed = 0, + kProvisionalAnswerLocal = 1, + kProvisionalAnswerRemote = 2, + kProvisionalAnswerMax +}; + +// Metrics for RTCRtpMuxPolicy. The only defined value is +// https://w3c.github.io/webrtc-pc/#rtcrtcpmuxpolicy-enum +// "require" but there is a legacy option "negotiate" which +// was removed from the spec. +enum RtcpMuxPolicyUsage { + kRtcpMuxPolicyUsageRequire = 0, + kRtcpMuxPolicyUsageNegotiate = 1, + kRtcpMuxPolicyUsageMax +}; + +// Metrics for SDP munging. +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. Keep in sync with SdpMungingType from +// tools/metrics/histograms/metadata/web_rtc/enums.xml +enum SdpMungingType { + kNoModification = 0, + kUnknownModification = 1, + kWithoutCreateAnswer = 2, + kWithoutCreateOffer = 3, + kNumberOfContents = 4, + // Transport-related munging. + kIceOptions = 20, + kIcePwd = 21, + kIceUfrag = 22, + kIceMode = 23, + kDtlsSetup = 24, + kMid = 25, + kPayloadTypes = 26, + kSsrcs = 27, + kIceOptionsRenomination = 28, + // RTP header extension munging. + kRtpHeaderExtensionRemoved = 40, + kRtpHeaderExtensionAdded = 41, + kRtpHeaderExtensionModified = 42, + // Audio-related munging. + kAudioCodecsRemoved = 60, + kAudioCodecsAdded = 61, + kAudioCodecsReordered = 62, + kAudioCodecsAddedMultiOpus = 63, + kAudioCodecsAddedL16 = 64, + kAudioCodecsRtcpFbAudioNack = 65, + kAudioCodecsFmtpOpusFec = 66, + kAudioCodecsFmtpOpusCbr = 67, + kAudioCodecsFmtpOpusStereo = 68, + kAudioCodecsFmtpOpusDtx = 69, + kAudioCodecsFmtp = 70, + kAudioCodecsRtcpFb = 71, + kAudioCodecsRtcpFbRrtr = 72, + // Video-related munging. + kVideoCodecsRemoved = 80, + kVideoCodecsAdded = 81, + kVideoCodecsReordered = 82, + kVideoCodecsLegacySimulcast = 83, + kVideoCodecsFmtpH264SpsPpsIdrInKeyframe = 84, + kVideoCodecsFmtp = 85, + kVideoCodecsRtcpFb = 86, + kMaxValue, +}; + +// When adding new metrics please consider using the style described in +// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#usage +// instead of the legacy enums used above. + +} // namespace webrtc + +#endif // API_UMA_METRICS_H_ diff --git a/pkg/apm/webrtc/api/units/data_rate.cc b/pkg/apm/webrtc/api/units/data_rate.cc new file mode 100644 index 00000000..7f99a172 --- /dev/null +++ b/pkg/apm/webrtc/api/units/data_rate.cc @@ -0,0 +1,36 @@ +/* + * 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 + +#include "api/array_view.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +std::string ToString(DataRate value) { + char buf[64]; + 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 diff --git a/pkg/apm/webrtc/api/units/data_rate.h b/pkg/apm/webrtc/api/units/data_rate.h new file mode 100644 index 00000000..a9400c04 --- /dev/null +++ b/pkg/apm/webrtc/api/units/data_rate.h @@ -0,0 +1,150 @@ +/* + * 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_ + +#include +#include +#include +#include + +#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/system/rtc_export.h" +#include "rtc_base/units/unit_base.h" // IWYU pragma: export + +namespace webrtc { +// DataRate is a class that represents a given data rate. This can be used to +// represent bandwidth, encoding bitrate, etc. The internal storage is bits per +// second (bps). +class DataRate final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr DataRate BitsPerSec(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + template + static constexpr DataRate BytesPerSec(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(8, value); + } + template + static constexpr DataRate KilobitsPerSec(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1000, value); + } + static constexpr DataRate Infinity() { return PlusInfinity(); } + + constexpr DataRate() = default; + + template + friend void AbslStringify(Sink& sink, DataRate value); + + template + constexpr T bps() const { + return ToValue(); + } + template + constexpr T bytes_per_sec() const { + return ToFraction<8, T>(); + } + template + 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; + 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::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::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(); + // 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::max() / 8 / + frequency.millihertz()); + int64_t millibits_per_second = + size.bytes() * 8 * frequency.millihertz(); + return DataRate::BitsPerSec((millibits_per_second + 500) / 1000); +} +inline constexpr DataRate operator*(const Frequency frequency, + const DataSize size) { + return size * frequency; +} + +RTC_EXPORT std::string ToString(DataRate value); + +template +void AbslStringify(Sink& sink, DataRate value) { + sink.Append(ToString(value)); +} + +} // namespace webrtc + +#endif // API_UNITS_DATA_RATE_H_ diff --git a/pkg/apm/webrtc/api/units/data_size.cc b/pkg/apm/webrtc/api/units/data_size.cc new file mode 100644 index 00000000..abe4906c --- /dev/null +++ b/pkg/apm/webrtc/api/units/data_size.cc @@ -0,0 +1,32 @@ +/* + * 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 + +#include "api/array_view.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +std::string ToString(DataSize value) { + char buf[64]; + 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 diff --git a/pkg/apm/webrtc/api/units/data_size.h b/pkg/apm/webrtc/api/units/data_size.h new file mode 100644 index 00000000..78d41b12 --- /dev/null +++ b/pkg/apm/webrtc/api/units/data_size.h @@ -0,0 +1,61 @@ +/* + * 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_ + +#include +#include +#include + +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/units/unit_base.h" // IWYU pragma: export + +namespace webrtc { +// DataSize is a class represeting a count of bytes. +class DataSize final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr DataSize Bytes(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + static constexpr DataSize Infinity() { return PlusInfinity(); } + + constexpr DataSize() = default; + + template + friend void AbslStringify(Sink& sink, DataSize value); + + template + constexpr T bytes() const { + return ToValue(); + } + + constexpr int64_t bytes_or(int64_t fallback_value) const { + return ToValueOr(fallback_value); + } + + private: + friend class rtc_units_impl::UnitBase; + using RelativeUnit::RelativeUnit; + static constexpr bool one_sided = true; +}; + +RTC_EXPORT std::string ToString(DataSize value); + +template +void AbslStringify(Sink& sink, DataSize value) { + sink.Append(ToString(value)); +} + +} // namespace webrtc + +#endif // API_UNITS_DATA_SIZE_H_ diff --git a/pkg/apm/webrtc/api/units/frequency.cc b/pkg/apm/webrtc/api/units/frequency.cc new file mode 100644 index 00000000..ca4b52e3 --- /dev/null +++ b/pkg/apm/webrtc/api/units/frequency.cc @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "api/units/frequency.h" + +#include +#include + +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { +std::string ToString(Frequency value) { + char buf[64]; + SimpleStringBuilder sb(buf); + if (value.IsPlusInfinity()) { + sb << "+inf Hz"; + } else if (value.IsMinusInfinity()) { + sb << "-inf Hz"; + } else if (value.millihertz() % 1000 != 0) { + sb.AppendFormat("%.3f Hz", value.hertz()); + } else { + sb << value.hertz() << " Hz"; + } + return sb.str(); +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/units/frequency.h b/pkg/apm/webrtc/api/units/frequency.h new file mode 100644 index 00000000..51c53f9b --- /dev/null +++ b/pkg/apm/webrtc/api/units/frequency.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef API_UNITS_FREQUENCY_H_ +#define API_UNITS_FREQUENCY_H_ + +#include +#include +#include +#include +#include + +#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 { + +class Frequency final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr Frequency MilliHertz(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + template + static constexpr Frequency Hertz(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000, value); + } + template + static constexpr Frequency KiloHertz(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000'000, value); + } + + constexpr Frequency() = default; + + template + friend void AbslStringify(Sink& sink, Frequency value); + + template + constexpr T hertz() const { + return ToFraction<1000, T>(); + } + template + constexpr T millihertz() const { + return ToValue(); + } + + private: + friend class rtc_units_impl::UnitBase; + using RelativeUnit::RelativeUnit; + static constexpr bool one_sided = true; +}; + +inline constexpr Frequency operator/(int64_t nominator, + const TimeDelta& interval) { + constexpr int64_t kKiloPerMicro = 1000 * 1000000; + RTC_DCHECK_LE(nominator, std::numeric_limits::max() / kKiloPerMicro); + RTC_CHECK(interval.IsFinite()); + RTC_CHECK(!interval.IsZero()); + return Frequency::MilliHertz(nominator * kKiloPerMicro / interval.us()); +} + +inline constexpr TimeDelta operator/(int64_t nominator, + const Frequency& frequency) { + constexpr int64_t kMegaPerMilli = 1000000 * 1000; + RTC_DCHECK_LE(nominator, std::numeric_limits::max() / kMegaPerMilli); + RTC_CHECK(frequency.IsFinite()); + RTC_CHECK(!frequency.IsZero()); + return TimeDelta::Micros(nominator * kMegaPerMilli / frequency.millihertz()); +} + +inline constexpr double operator*(Frequency frequency, TimeDelta time_delta) { + return frequency.hertz() * time_delta.seconds(); +} +inline constexpr double operator*(TimeDelta time_delta, Frequency frequency) { + return frequency * time_delta; +} + +RTC_EXPORT std::string ToString(Frequency value); + +template +void AbslStringify(Sink& sink, Frequency value) { + sink.Append(ToString(value)); +} + +} // namespace webrtc +#endif // API_UNITS_FREQUENCY_H_ diff --git a/pkg/apm/webrtc/api/units/time_delta.cc b/pkg/apm/webrtc/api/units/time_delta.cc new file mode 100644 index 00000000..efd5a2a5 --- /dev/null +++ b/pkg/apm/webrtc/api/units/time_delta.cc @@ -0,0 +1,38 @@ +/* + * 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/time_delta.h" + +#include + +#include "api/array_view.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +std::string ToString(TimeDelta value) { + char buf[64]; + SimpleStringBuilder sb(buf); + if (value.IsPlusInfinity()) { + sb << "+inf ms"; + } else if (value.IsMinusInfinity()) { + sb << "-inf ms"; + } else { + if (value.us() == 0 || (value.us() % 1000) != 0) + sb << value.us() << " us"; + else if (value.ms() % 1000 != 0) + sb << value.ms() << " ms"; + else + sb << value.seconds() << " s"; + } + return sb.str(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/units/time_delta.h b/pkg/apm/webrtc/api/units/time_delta.h new file mode 100644 index 00000000..29e86c76 --- /dev/null +++ b/pkg/apm/webrtc/api/units/time_delta.h @@ -0,0 +1,105 @@ +/* + * 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_TIME_DELTA_H_ +#define API_UNITS_TIME_DELTA_H_ + +#include +#include +#include +#include + +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/units/unit_base.h" // IWYU pragma: export + +namespace webrtc { + +// TimeDelta represents the difference between two timestamps. Commonly this can +// be a duration. However since two Timestamps are not guaranteed to have the +// same epoch (they might come from different computers, making exact +// synchronisation infeasible), the duration covered by a TimeDelta can be +// undefined. To simplify usage, it can be constructed and converted to +// different units, specifically seconds (s), milliseconds (ms) and +// microseconds (us). +class TimeDelta final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr TimeDelta Minutes(T value) { + static_assert(std::is_arithmetic::value, ""); + return Seconds(value * 60); + } + template + static constexpr TimeDelta Seconds(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000'000, value); + } + template + static constexpr TimeDelta Millis(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000, value); + } + template + static constexpr TimeDelta Micros(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + + constexpr TimeDelta() = default; + + template + friend void AbslStringify(Sink& sink, TimeDelta value); + + template + constexpr T seconds() const { + return ToFraction<1000000, T>(); + } + template + constexpr T ms() const { + return ToFraction<1000, T>(); + } + template + constexpr T us() const { + return ToValue(); + } + template + constexpr T ns() const { + return ToMultiple<1000, T>(); + } + + constexpr int64_t seconds_or(int64_t fallback_value) const { + return ToFractionOr<1000000>(fallback_value); + } + constexpr int64_t ms_or(int64_t fallback_value) const { + return ToFractionOr<1000>(fallback_value); + } + constexpr int64_t us_or(int64_t fallback_value) const { + return ToValueOr(fallback_value); + } + + constexpr TimeDelta Abs() const { + return us() < 0 ? TimeDelta::Micros(-us()) : *this; + } + + private: + friend class rtc_units_impl::UnitBase; + using RelativeUnit::RelativeUnit; + static constexpr bool one_sided = false; +}; + +RTC_EXPORT std::string ToString(TimeDelta value); + +template +void AbslStringify(Sink& sink, TimeDelta value) { + sink.Append(ToString(value)); +} + +} // namespace webrtc + +#endif // API_UNITS_TIME_DELTA_H_ diff --git a/pkg/apm/webrtc/api/units/timestamp.cc b/pkg/apm/webrtc/api/units/timestamp.cc new file mode 100644 index 00000000..38b0d115 --- /dev/null +++ b/pkg/apm/webrtc/api/units/timestamp.cc @@ -0,0 +1,36 @@ +/* + * 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/timestamp.h" + +#include + +#include "api/array_view.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { +std::string ToString(Timestamp value) { + char buf[64]; + SimpleStringBuilder sb(buf); + if (value.IsPlusInfinity()) { + sb << "+inf ms"; + } else if (value.IsMinusInfinity()) { + sb << "-inf ms"; + } else { + if (value.us() == 0 || (value.us() % 1000) != 0) + sb << value.us() << " us"; + else if (value.ms() % 1000 != 0) + sb << value.ms() << " ms"; + else + sb << value.seconds() << " s"; + } + return sb.str(); +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/api/units/timestamp.h b/pkg/apm/webrtc/api/units/timestamp.h new file mode 100644 index 00000000..7831bba2 --- /dev/null +++ b/pkg/apm/webrtc/api/units/timestamp.h @@ -0,0 +1,134 @@ +/* + * 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_TIMESTAMP_H_ +#define API_UNITS_TIMESTAMP_H_ + +#include +#include +#include + +#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. +// The epoch is assumed to be before any represented timestamps, this means that +// negative values are not valid. The most notable feature is that the +// difference of two Timestamps results in a TimeDelta. +class Timestamp final : public rtc_units_impl::UnitBase { + public: + template + static constexpr Timestamp Seconds(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000'000, value); + } + template + static constexpr Timestamp Millis(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000, value); + } + template + static constexpr Timestamp Micros(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + + Timestamp() = delete; + + template + friend void AbslStringify(Sink& sink, Timestamp value); + + template + constexpr T seconds() const { + return ToFraction<1000000, T>(); + } + template + constexpr T ms() const { + return ToFraction<1000, T>(); + } + template + constexpr T us() const { + return ToValue(); + } + + constexpr int64_t seconds_or(int64_t fallback_value) const { + return ToFractionOr<1000000>(fallback_value); + } + constexpr int64_t ms_or(int64_t fallback_value) const { + return ToFractionOr<1000>(fallback_value); + } + constexpr int64_t us_or(int64_t fallback_value) const { + return ToValueOr(fallback_value); + } + + constexpr Timestamp operator+(const TimeDelta delta) const { + if (IsPlusInfinity() || delta.IsPlusInfinity()) { + RTC_DCHECK(!IsMinusInfinity()); + RTC_DCHECK(!delta.IsMinusInfinity()); + return PlusInfinity(); + } else if (IsMinusInfinity() || delta.IsMinusInfinity()) { + RTC_DCHECK(!IsPlusInfinity()); + RTC_DCHECK(!delta.IsPlusInfinity()); + return MinusInfinity(); + } + return Timestamp::Micros(us() + delta.us()); + } + constexpr Timestamp operator-(const TimeDelta delta) const { + if (IsPlusInfinity() || delta.IsMinusInfinity()) { + RTC_DCHECK(!IsMinusInfinity()); + RTC_DCHECK(!delta.IsPlusInfinity()); + return PlusInfinity(); + } else if (IsMinusInfinity() || delta.IsPlusInfinity()) { + RTC_DCHECK(!IsPlusInfinity()); + RTC_DCHECK(!delta.IsMinusInfinity()); + return MinusInfinity(); + } + return Timestamp::Micros(us() - delta.us()); + } + constexpr TimeDelta operator-(const Timestamp other) const { + if (IsPlusInfinity() || other.IsMinusInfinity()) { + RTC_DCHECK(!IsMinusInfinity()); + RTC_DCHECK(!other.IsPlusInfinity()); + return TimeDelta::PlusInfinity(); + } else if (IsMinusInfinity() || other.IsPlusInfinity()) { + RTC_DCHECK(!IsPlusInfinity()); + RTC_DCHECK(!other.IsMinusInfinity()); + return TimeDelta::MinusInfinity(); + } + return TimeDelta::Micros(us() - other.us()); + } + constexpr Timestamp& operator-=(const TimeDelta delta) { + *this = *this - delta; + return *this; + } + constexpr Timestamp& operator+=(const TimeDelta delta) { + *this = *this + delta; + return *this; + } + + private: + friend class rtc_units_impl::UnitBase; + using UnitBase::UnitBase; + static constexpr bool one_sided = true; +}; + +RTC_EXPORT std::string ToString(Timestamp value); + +template +void AbslStringify(Sink& sink, Timestamp value) { + sink.Append(ToString(value)); +} + +} // namespace webrtc + +#endif // API_UNITS_TIMESTAMP_H_ diff --git a/pkg/apm/webrtc/api/units/units.go b/pkg/apm/webrtc/api/units/units.go new file mode 100644 index 00000000..6e234727 --- /dev/null +++ b/pkg/apm/webrtc/api/units/units.go @@ -0,0 +1,10 @@ +//go:build console + +package units + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/api/video/color_space.h b/pkg/apm/webrtc/api/video/color_space.h new file mode 100644 index 00000000..21359d5a --- /dev/null +++ b/pkg/apm/webrtc/api/video/color_space.h @@ -0,0 +1,6 @@ +#ifndef API_VIDEO_COLOR_SPACE_H_ +#define API_VIDEO_COLOR_SPACE_H_ +namespace webrtc { +class ColorSpace {}; +} +#endif diff --git a/pkg/apm/webrtc/api/video/video_content_type.h b/pkg/apm/webrtc/api/video/video_content_type.h new file mode 100644 index 00000000..6f69b880 --- /dev/null +++ b/pkg/apm/webrtc/api/video/video_content_type.h @@ -0,0 +1,6 @@ +#ifndef API_VIDEO_VIDEO_CONTENT_TYPE_H_ +#define API_VIDEO_VIDEO_CONTENT_TYPE_H_ +namespace webrtc { +enum class VideoContentType : uint8_t { UNSPECIFIED = 0 }; +} +#endif diff --git a/pkg/apm/webrtc/api/video/video_rotation.h b/pkg/apm/webrtc/api/video/video_rotation.h new file mode 100644 index 00000000..5cba0697 --- /dev/null +++ b/pkg/apm/webrtc/api/video/video_rotation.h @@ -0,0 +1,6 @@ +#ifndef API_VIDEO_VIDEO_ROTATION_H_ +#define API_VIDEO_VIDEO_ROTATION_H_ +namespace webrtc { +enum VideoRotation { kVideoRotation_0 = 0 }; +} +#endif diff --git a/pkg/apm/webrtc/api/voip/voip_base.h b/pkg/apm/webrtc/api/voip/voip_base.h new file mode 100644 index 00000000..f7f4432a --- /dev/null +++ b/pkg/apm/webrtc/api/voip/voip_base.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VOIP_VOIP_BASE_H_ +#define API_VOIP_VOIP_BASE_H_ + +#include +#include + +#include "absl/base/attributes.h" + +namespace webrtc { + +class Transport; + +// VoipBase interface +// +// VoipBase provides a management interface on a media session using a +// concept called 'channel'. A channel represents an interface handle +// for application to request various media session operations. This +// notion of channel is used throughout other interfaces as well. +// +// Underneath the interface, a channel id is mapped into an audio session +// object that is capable of sending and receiving a single RTP stream with +// another media endpoint. It's possible to create and use multiple active +// channels simultaneously which would mean that particular application +// session has RTP streams with multiple remote endpoints. +// +// A typical example for the usage context is outlined in VoipEngine +// header file. + +enum class ChannelId : int {}; + +enum class ABSL_MUST_USE_RESULT VoipResult { + // kOk indicates the function was successfully invoked with no error. + kOk, + // kInvalidArgument indicates the caller specified an invalid argument, such + // as an invalid ChannelId. + kInvalidArgument, + // kFailedPrecondition indicates that the operation was failed due to not + // satisfying prerequisite such as not setting codec type before sending. + kFailedPrecondition, + // kInternal is used to indicate various internal failures that are not the + // caller's fault. Further detail is commented on each function that uses this + // return value. + kInternal, +}; + +class VoipBase { + public: + // Creates a channel. + // Each channel handle maps into one audio media session where each has + // its own separate module for send/receive rtp packet with one peer. + // Caller must set `transport`, webrtc::Transport callback pointer to + // receive rtp/rtcp packets from corresponding media session in VoIP engine. + // VoipEngine framework expects applications to handle network I/O directly + // and injection for incoming RTP from remote endpoint is handled via + // VoipNetwork interface. `local_ssrc` is optional and when local_ssrc is not + // set, some random value will be used by voip engine. + // Returns a ChannelId created for caller to handle subsequent Channel + // operations. + virtual ChannelId CreateChannel(Transport* transport, + std::optional local_ssrc) = 0; + + // Releases `channel_id` that no longer has any use. + // Returns following VoipResult; + // kOk - `channel_id` is released. + // kInvalidArgument - `channel_id` is invalid. + // kInternal - Fails to stop audio output device. + virtual VoipResult ReleaseChannel(ChannelId channel_id) = 0; + + // Starts sending on `channel_id`. This starts microphone if not started yet. + // Returns following VoipResult; + // kOk - Channel successfully started to send. + // kInvalidArgument - `channel_id` is invalid. + // kFailedPrecondition - Missing prerequisite on VoipCodec::SetSendCodec. + // kInternal - initialization has failed on selected microphone. + virtual VoipResult StartSend(ChannelId channel_id) = 0; + + // Stops sending on `channel_id`. If this is the last active channel, it will + // stop microphone input from underlying audio platform layer. + // Returns following VoipResult; + // kOk - Channel successfully stopped to send. + // kInvalidArgument - `channel_id` is invalid. + // kInternal - Failed to stop the active microphone device. + virtual VoipResult StopSend(ChannelId channel_id) = 0; + + // Starts playing on speaker device for `channel_id`. + // This will start underlying platform speaker device if not started. + // Returns following VoipResult; + // kOk - Channel successfully started to play out. + // kInvalidArgument - `channel_id` is invalid. + // kFailedPrecondition - Missing prerequisite on VoipCodec::SetReceiveCodecs. + // kInternal - Failed to initializate the selected speaker device. + virtual VoipResult StartPlayout(ChannelId channel_id) = 0; + + // Stops playing on speaker device for `channel_id`. + // Returns following VoipResult; + // kOk - Channel successfully stopped t play out. + // kInvalidArgument - `channel_id` is invalid. + virtual VoipResult StopPlayout(ChannelId channel_id) = 0; + + protected: + virtual ~VoipBase() = default; +}; + +} // namespace webrtc + +#endif // API_VOIP_VOIP_BASE_H_ diff --git a/pkg/apm/webrtc/api/voip/voip_codec.h b/pkg/apm/webrtc/api/voip/voip_codec.h new file mode 100644 index 00000000..46cddfad --- /dev/null +++ b/pkg/apm/webrtc/api/voip/voip_codec.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VOIP_VOIP_CODEC_H_ +#define API_VOIP_VOIP_CODEC_H_ + +#include + +#include "api/audio_codecs/audio_format.h" +#include "api/voip/voip_base.h" + +namespace webrtc { + +// VoipCodec interface currently provides any codec related interface +// such as setting encoder and decoder types that are negotiated with +// remote endpoint. Typically after SDP offer and answer exchange, +// the local endpoint understands what are the codec payload types that +// are used with negotiated codecs. This interface is subject to expand +// as needed in future. +// +// This interface requires a channel id created via VoipBase interface. +class VoipCodec { + public: + // Set encoder type here along with its payload type to use. + // Returns following VoipResult; + // kOk - sending codec is set as provided. + // kInvalidArgument - `channel_id` is invalid. + virtual VoipResult SetSendCodec(ChannelId channel_id, + int payload_type, + const SdpAudioFormat& encoder_spec) = 0; + + // Set decoder payload type here. In typical offer and answer model, + // this should be called after payload type has been agreed in media + // session. Note that payload type can differ with same codec in each + // direction. + // Returns following VoipResult; + // kOk - receiving codecs are set as provided. + // kInvalidArgument - `channel_id` is invalid. + virtual VoipResult SetReceiveCodecs( + ChannelId channel_id, + const std::map& decoder_specs) = 0; + + protected: + virtual ~VoipCodec() = default; +}; + +} // namespace webrtc + +#endif // API_VOIP_VOIP_CODEC_H_ diff --git a/pkg/apm/webrtc/api/voip/voip_dtmf.h b/pkg/apm/webrtc/api/voip/voip_dtmf.h new file mode 100644 index 00000000..21d67426 --- /dev/null +++ b/pkg/apm/webrtc/api/voip/voip_dtmf.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VOIP_VOIP_DTMF_H_ +#define API_VOIP_VOIP_DTMF_H_ + +#include + +#include "api/voip/voip_base.h" + +namespace webrtc { + +// DTMF events and their event codes as defined in +// https://tools.ietf.org/html/rfc4733#section-7 +enum class DtmfEvent : uint8_t { + kDigitZero = 0, + kDigitOne, + kDigitTwo, + kDigitThree, + kDigitFour, + kDigitFive, + kDigitSix, + kDigitSeven, + kDigitEight, + kDigitNine, + kAsterisk, + kHash, + kLetterA, + kLetterB, + kLetterC, + kLetterD +}; + +// VoipDtmf interface provides DTMF related interfaces such +// as sending DTMF events to the remote endpoint. +class VoipDtmf { + public: + // Register the payload type and sample rate for DTMF (RFC 4733) payload. + // Must be called exactly once prior to calling SendDtmfEvent after payload + // type has been negotiated with remote. + // Returns following VoipResult; + // kOk - telephone event type is registered as provided. + // kInvalidArgument - `channel_id` is invalid. + virtual VoipResult RegisterTelephoneEventType(ChannelId channel_id, + int rtp_payload_type, + int sample_rate_hz) = 0; + + // Send DTMF named event as specified by + // https://tools.ietf.org/html/rfc4733#section-3.2 + // `duration_ms` specifies the duration of DTMF packets that will be emitted + // in place of real RTP packets instead. + // Must be called after RegisterTelephoneEventType and VoipBase::StartSend + // have been called. + // Returns following VoipResult; + // kOk - requested DTMF event is successfully scheduled. + // kInvalidArgument - `channel_id` is invalid. + // kFailedPrecondition - Missing prerequisite on RegisterTelephoneEventType + // or sending state. + virtual VoipResult SendDtmfEvent(ChannelId channel_id, + DtmfEvent dtmf_event, + int duration_ms) = 0; + + protected: + virtual ~VoipDtmf() = default; +}; + +} // namespace webrtc + +#endif // API_VOIP_VOIP_DTMF_H_ diff --git a/pkg/apm/webrtc/api/voip/voip_engine.h b/pkg/apm/webrtc/api/voip/voip_engine.h new file mode 100644 index 00000000..cdb74f9f --- /dev/null +++ b/pkg/apm/webrtc/api/voip/voip_engine.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VOIP_VOIP_ENGINE_H_ +#define API_VOIP_VOIP_ENGINE_H_ + +namespace webrtc { + +class VoipBase; +class VoipCodec; +class VoipNetwork; +class VoipDtmf; +class VoipStatistics; +class VoipVolumeControl; + +// VoipEngine is the main interface serving as the entry point for all VoIP +// APIs. A single instance of VoipEngine should suffice the most of the need for +// typical VoIP applications as it handles multiple media sessions including a +// specialized session type like ad-hoc conference. Below example code +// describes the typical sequence of API usage. Each API header contains more +// description on what the methods are used for. +// +// // Caller is responsible of setting desired audio components. +// VoipEngineConfig config; +// config.encoder_factory = CreateBuiltinAudioEncoderFactory(); +// config.decoder_factory = CreateBuiltinAudioDecoderFactory(); +// config.task_queue_factory = CreateDefaultTaskQueueFactory(); +// config.audio_device = +// AudioDeviceModule::Create(AudioDeviceModule::kPlatformDefaultAudio, +// config.task_queue_factory.get()); +// config.audio_processing_builder = +// std::make_unique(); +// +// auto voip_engine = CreateVoipEngine(std::move(config)); +// +// auto& voip_base = voip_engine->Base(); +// auto& voip_codec = voip_engine->Codec(); +// auto& voip_network = voip_engine->Network(); +// +// ChannelId channel = voip_base.CreateChannel(&app_transport_); +// +// // After SDP offer/answer, set payload type and codecs that have been +// // decided through SDP negotiation. +// // VoipResult handling omitted here. +// voip_codec.SetSendCodec(channel, ...); +// voip_codec.SetReceiveCodecs(channel, ...); +// +// // Start sending and playing RTP on voip channel. +// // VoipResult handling omitted here. +// voip_base.StartSend(channel); +// voip_base.StartPlayout(channel); +// +// // Inject received RTP/RTCP through VoipNetwork interface. +// // VoipResult handling omitted here. +// voip_network.ReceivedRTPPacket(channel, ...); +// voip_network.ReceivedRTCPPacket(channel, ...); +// +// // Stop and release voip channel. +// // VoipResult handling omitted here. +// voip_base.StopSend(channel); +// voip_base.StopPlayout(channel); +// voip_base.ReleaseChannel(channel); +// +class VoipEngine { + public: + virtual ~VoipEngine() = default; + + // VoipBase is the audio session management interface that + // creates/releases/starts/stops an one-to-one audio media session. + virtual VoipBase& Base() = 0; + + // VoipNetwork provides injection APIs that would enable application + // to send and receive RTP/RTCP packets. There is no default network module + // that provides RTP transmission and reception. + virtual VoipNetwork& Network() = 0; + + // VoipCodec provides codec configuration APIs for encoder and decoders. + virtual VoipCodec& Codec() = 0; + + // VoipDtmf provides DTMF event APIs to register and send DTMF events. + virtual VoipDtmf& Dtmf() = 0; + + // VoipStatistics provides performance metrics around audio decoding module + // and jitter buffer (NetEq). + virtual VoipStatistics& Statistics() = 0; + + // VoipVolumeControl provides various input/output volume control. + virtual VoipVolumeControl& VolumeControl() = 0; +}; + +} // namespace webrtc + +#endif // API_VOIP_VOIP_ENGINE_H_ diff --git a/pkg/apm/webrtc/api/voip/voip_engine_factory.h b/pkg/apm/webrtc/api/voip/voip_engine_factory.h new file mode 100644 index 00000000..f00075e2 --- /dev/null +++ b/pkg/apm/webrtc/api/voip/voip_engine_factory.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VOIP_VOIP_ENGINE_FACTORY_H_ +#define API_VOIP_VOIP_ENGINE_FACTORY_H_ + +#include +#include + +#include "api/audio/audio_device.h" +#include "api/audio/audio_processing.h" +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/audio_codecs/audio_encoder_factory.h" +#include "api/environment/environment.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/voip/voip_engine.h" + +namespace webrtc { + +// VoipEngineConfig is a struct that defines parameters to instantiate a +// VoipEngine instance through CreateVoipEngine factory method. Each member is +// marked with comments as either mandatory or optional and default +// implementations that applications can use. +struct VoipEngineConfig { + // Mandatory (e.g. api/audio_codec/builtin_audio_encoder_factory). + // AudioEncoderFactory provides a set of audio codecs for VoipEngine to encode + // the audio input sample. Application can choose to limit the set to reduce + // application footprint. + scoped_refptr encoder_factory; + + // Mandatory (e.g. api/audio_codec/builtin_audio_decoder_factory). + // AudioDecoderFactory provides a set of audio codecs for VoipEngine to decode + // the received RTP packets from remote media endpoint. Application can choose + // to limit the set to reduce application footprint. + scoped_refptr decoder_factory; + + // Optional (e.g. api/task_queue/default_task_queue_factory). + // TaskQueueFactory provided for VoipEngine to work asynchronously on its + // encoding flow. + // It is an error to provide both `env` and `task_queue_factory`. + std::unique_ptr task_queue_factory; + + // Mandatory (e.g. modules/audio_device/include). + // AudioDeviceModule that periocally provides audio input samples from + // recording device (e.g. microphone) and requests audio output samples to + // play through its output device (e.g. speaker). + scoped_refptr audio_device_module; + + // Optional. When not set, VoipEngine will use a default Environment created + // with `CreateEnvironment`, see api/environment/environment_factory.h + // Provides + // - TaskQueueFactory to work asynchronously on VoipEngine encoding flow + // - FieldTrialsView for experimentations + std::optional env; + + // Optional (e.g. api/audio/builtin_audio_processing_builder). + // AudioProcessing provides audio procesing functionalities (e.g. acoustic + // echo cancellation, noise suppression, gain control, etc) on audio input + // samples for VoipEngine. When optionally not set, VoipEngine will not have + // such functionalities to perform on audio input samples received from + // AudioDeviceModule. + std::unique_ptr audio_processing_builder; +}; + +// Creates a VoipEngine instance with provided VoipEngineConfig. +std::unique_ptr CreateVoipEngine(VoipEngineConfig config); + +} // namespace webrtc + +#endif // API_VOIP_VOIP_ENGINE_FACTORY_H_ diff --git a/pkg/apm/webrtc/api/voip/voip_network.h b/pkg/apm/webrtc/api/voip/voip_network.h new file mode 100644 index 00000000..b239c7ea --- /dev/null +++ b/pkg/apm/webrtc/api/voip/voip_network.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VOIP_VOIP_NETWORK_H_ +#define API_VOIP_VOIP_NETWORK_H_ + +#include + +#include "api/array_view.h" +#include "api/voip/voip_base.h" + +namespace webrtc { + +// VoipNetwork interface provides any network related interfaces such as +// processing received RTP/RTCP packet from remote endpoint. This interface +// requires a ChannelId created via VoipBase interface. +class VoipNetwork { + public: + // The data received from the network including RTP header is passed here. + // Returns following VoipResult; + // kOk - received RTP packet is processed. + // kInvalidArgument - `channel_id` is invalid. + virtual VoipResult ReceivedRTPPacket(ChannelId channel_id, + ArrayView rtp_packet) = 0; + + // The data received from the network including RTCP header is passed here. + // Returns following VoipResult; + // kOk - received RTCP packet is processed. + // kInvalidArgument - `channel_id` is invalid. + virtual VoipResult ReceivedRTCPPacket( + ChannelId channel_id, + ArrayView rtcp_packet) = 0; + + protected: + virtual ~VoipNetwork() = default; +}; + +} // namespace webrtc + +#endif // API_VOIP_VOIP_NETWORK_H_ diff --git a/pkg/apm/webrtc/api/voip/voip_statistics.h b/pkg/apm/webrtc/api/voip/voip_statistics.h new file mode 100644 index 00000000..8b994d1c --- /dev/null +++ b/pkg/apm/webrtc/api/voip/voip_statistics.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VOIP_VOIP_STATISTICS_H_ +#define API_VOIP_VOIP_STATISTICS_H_ + +#include +#include + +#include "api/neteq/neteq.h" +#include "api/voip/voip_base.h" + +namespace webrtc { + +struct IngressStatistics { + // Stats included from api/neteq/neteq.h. + NetEqLifetimeStatistics neteq_stats; + + // Represents the total duration in seconds of all samples that have been + // received. + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalsamplesduration + double total_duration = 0.0; +}; + +// Remote statistics obtained via remote RTCP SR/RR report received. +struct RemoteRtcpStatistics { + // Jitter as defined in RFC 3550 [6.4.1] expressed in seconds. + double jitter = 0.0; + + // Cumulative packets lost as defined in RFC 3550 [6.4.1] + int64_t packets_lost = 0; + + // Fraction lost as defined in RFC 3550 [6.4.1] expressed as a floating + // pointer number. + double fraction_lost = 0.0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcremoteinboundrtpstreamstats-roundtriptime + std::optional round_trip_time; + + // Last time (not RTP timestamp) when RTCP report received in milliseconds. + int64_t last_report_received_timestamp_ms; +}; + +struct ChannelStatistics { + // https://w3c.github.io/webrtc-stats/#dom-rtcsentrtpstreamstats-packetssent + uint64_t packets_sent = 0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcsentrtpstreamstats-bytessent + uint64_t bytes_sent = 0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcreceivedrtpstreamstats-packetsreceived + uint64_t packets_received = 0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-bytesreceived + uint64_t bytes_received = 0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcreceivedrtpstreamstats-jitter + double jitter = 0.0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcreceivedrtpstreamstats-packetslost + int64_t packets_lost = 0; + + // SSRC from remote media endpoint as indicated either by RTP header in RFC + // 3550 [5.1] or RTCP SSRC of sender in RFC 3550 [6.4.1]. + std::optional remote_ssrc; + + std::optional remote_rtcp; +}; + +// VoipStatistics interface provides the interfaces for querying metrics around +// the jitter buffer (NetEq) performance. +class VoipStatistics { + public: + // Gets the audio ingress statistics by `ingress_stats` reference. + // Returns following VoipResult; + // kOk - successfully set provided IngressStatistics reference. + // kInvalidArgument - `channel_id` is invalid. + virtual VoipResult GetIngressStatistics(ChannelId channel_id, + IngressStatistics& ingress_stats) = 0; + + // Gets the channel statistics by `channel_stats` reference. + // Returns following VoipResult; + // kOk - successfully set provided ChannelStatistics reference. + // kInvalidArgument - `channel_id` is invalid. + virtual VoipResult GetChannelStatistics(ChannelId channel_id, + ChannelStatistics& channel_stats) = 0; + + protected: + virtual ~VoipStatistics() = default; +}; + +} // namespace webrtc + +#endif // API_VOIP_VOIP_STATISTICS_H_ diff --git a/pkg/apm/webrtc/api/voip/voip_volume_control.h b/pkg/apm/webrtc/api/voip/voip_volume_control.h new file mode 100644 index 00000000..aab14180 --- /dev/null +++ b/pkg/apm/webrtc/api/voip/voip_volume_control.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VOIP_VOIP_VOLUME_CONTROL_H_ +#define API_VOIP_VOIP_VOLUME_CONTROL_H_ + +#include "api/voip/voip_base.h" + +namespace webrtc { + +struct VolumeInfo { + // https://w3c.github.io/webrtc-stats/#dom-rtcaudiosourcestats-audiolevel + double audio_level = 0; + // https://w3c.github.io/webrtc-stats/#dom-rtcaudiosourcestats-totalaudioenergy + double total_energy = 0.0; + // https://w3c.github.io/webrtc-stats/#dom-rtcaudiosourcestats-totalsamplesduration + double total_duration = 0.0; +}; + +// VoipVolumeControl interface. +// +// This sub-API supports functions related to the input (microphone) and output +// (speaker) device. +// +// Caller must ensure that ChannelId is valid otherwise it will result in no-op +// with error logging. +class VoipVolumeControl { + public: + // Mute/unmutes the microphone input sample before encoding process. Note that + // mute doesn't affect audio input level and energy values as input sample is + // silenced after the measurement. + // Returns following VoipResult; + // kOk - input source muted or unmuted as provided by `enable`. + // kInvalidArgument - `channel_id` is invalid. + virtual VoipResult SetInputMuted(ChannelId channel_id, bool enable) = 0; + + // Gets the microphone volume info via `volume_info` reference. + // Returns following VoipResult; + // kOk - successfully set provided input volume info. + // kInvalidArgument - `channel_id` is invalid. + virtual VoipResult GetInputVolumeInfo(ChannelId channel_id, + VolumeInfo& volume_info) = 0; + + // Gets the speaker volume info via `volume_info` reference. + // Returns following VoipResult; + // kOk - successfully set provided output volume info. + // kInvalidArgument - `channel_id` is invalid. + virtual VoipResult GetOutputVolumeInfo(ChannelId channel_id, + VolumeInfo& volume_info) = 0; + + protected: + virtual ~VoipVolumeControl() = default; +}; + +} // namespace webrtc + +#endif // API_VOIP_VOIP_VOLUME_CONTROL_H_ diff --git a/pkg/apm/webrtc/api/webrtc_key_value_config.h b/pkg/apm/webrtc/api/webrtc_key_value_config.h new file mode 100644 index 00000000..0648a7f5 --- /dev/null +++ b/pkg/apm/webrtc/api/webrtc_key_value_config.h @@ -0,0 +1,17 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef API_WEBRTC_KEY_VALUE_CONFIG_H_ +#define API_WEBRTC_KEY_VALUE_CONFIG_H_ + +// TODO(bugs.webrtc.org/10335): Remove once all migrated to +// api/field_trials_view.h +#include "api/field_trials_view.h" // IWYU pragma: keep + +#endif // API_WEBRTC_KEY_VALUE_CONFIG_H_ diff --git a/pkg/apm/webrtc/common_audio/audio_converter.cc b/pkg/apm/webrtc/common_audio/audio_converter.cc new file mode 100644 index 00000000..485ec80c --- /dev/null +++ b/pkg/apm/webrtc/common_audio/audio_converter.cc @@ -0,0 +1,219 @@ +/* + * 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/audio_converter.h" + +#include +#include +#include +#include + +#include "common_audio/channel_buffer.h" +#include "common_audio/resampler/push_sinc_resampler.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { + +class CopyConverter : public AudioConverter { + public: + CopyConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, + size_t dst_frames) + : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {} + ~CopyConverter() override {} + + void Convert(const float* const* src, + size_t src_size, + float* const* dst, + size_t dst_capacity) override { + CheckSizes(src_size, dst_capacity); + if (src != dst) { + for (size_t i = 0; i < src_channels(); ++i) + std::memcpy(dst[i], src[i], dst_frames() * sizeof(*dst[i])); + } + } +}; + +class UpmixConverter : public AudioConverter { + public: + UpmixConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, + size_t dst_frames) + : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {} + ~UpmixConverter() override {} + + void Convert(const float* const* src, + size_t src_size, + float* const* dst, + size_t dst_capacity) override { + CheckSizes(src_size, dst_capacity); + for (size_t i = 0; i < dst_frames(); ++i) { + const float value = src[0][i]; + for (size_t j = 0; j < dst_channels(); ++j) + dst[j][i] = value; + } + } +}; + +class DownmixConverter : public AudioConverter { + public: + DownmixConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, + size_t dst_frames) + : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {} + ~DownmixConverter() override {} + + void Convert(const float* const* src, + size_t src_size, + float* const* dst, + size_t dst_capacity) override { + CheckSizes(src_size, dst_capacity); + float* dst_mono = dst[0]; + for (size_t i = 0; i < src_frames(); ++i) { + float sum = 0; + for (size_t j = 0; j < src_channels(); ++j) + sum += src[j][i]; + dst_mono[i] = sum / src_channels(); + } + } +}; + +class ResampleConverter : public AudioConverter { + public: + ResampleConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, + size_t dst_frames) + : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) { + resamplers_.reserve(src_channels); + for (size_t i = 0; i < src_channels; ++i) + resamplers_.push_back(std::unique_ptr( + new PushSincResampler(src_frames, dst_frames))); + } + ~ResampleConverter() override {} + + void Convert(const float* const* src, + size_t src_size, + float* const* dst, + size_t dst_capacity) override { + CheckSizes(src_size, dst_capacity); + for (size_t i = 0; i < resamplers_.size(); ++i) + resamplers_[i]->Resample(src[i], src_frames(), dst[i], dst_frames()); + } + + private: + std::vector> resamplers_; +}; + +// Apply a vector of converters in serial, in the order given. At least two +// converters must be provided. +class CompositionConverter : public AudioConverter { + public: + explicit CompositionConverter( + std::vector> converters) + : converters_(std::move(converters)) { + RTC_CHECK_GE(converters_.size(), 2); + // We need an intermediate buffer after every converter. + for (auto it = converters_.begin(); it != converters_.end() - 1; ++it) + buffers_.push_back( + std::unique_ptr>(new ChannelBuffer( + (*it)->dst_frames(), (*it)->dst_channels()))); + } + ~CompositionConverter() override {} + + void Convert(const float* const* src, + size_t src_size, + float* const* dst, + size_t dst_capacity) override { + converters_.front()->Convert(src, src_size, buffers_.front()->channels(), + buffers_.front()->size()); + for (size_t i = 2; i < converters_.size(); ++i) { + auto& src_buffer = buffers_[i - 2]; + auto& dst_buffer = buffers_[i - 1]; + converters_[i]->Convert(src_buffer->channels(), src_buffer->size(), + dst_buffer->channels(), dst_buffer->size()); + } + converters_.back()->Convert(buffers_.back()->channels(), + buffers_.back()->size(), dst, dst_capacity); + } + + private: + std::vector> converters_; + std::vector>> buffers_; +}; + +std::unique_ptr AudioConverter::Create(size_t src_channels, + size_t src_frames, + size_t dst_channels, + size_t dst_frames) { + std::unique_ptr sp; + if (src_channels > dst_channels) { + if (src_frames != dst_frames) { + std::vector> converters; + converters.push_back(std::unique_ptr(new DownmixConverter( + src_channels, src_frames, dst_channels, src_frames))); + converters.push_back( + std::unique_ptr(new ResampleConverter( + dst_channels, src_frames, dst_channels, dst_frames))); + sp.reset(new CompositionConverter(std::move(converters))); + } else { + sp.reset(new DownmixConverter(src_channels, src_frames, dst_channels, + dst_frames)); + } + } else if (src_channels < dst_channels) { + if (src_frames != dst_frames) { + std::vector> converters; + converters.push_back( + std::unique_ptr(new ResampleConverter( + src_channels, src_frames, src_channels, dst_frames))); + converters.push_back(std::unique_ptr(new UpmixConverter( + src_channels, dst_frames, dst_channels, dst_frames))); + sp.reset(new CompositionConverter(std::move(converters))); + } else { + sp.reset(new UpmixConverter(src_channels, src_frames, dst_channels, + dst_frames)); + } + } else if (src_frames != dst_frames) { + sp.reset(new ResampleConverter(src_channels, src_frames, dst_channels, + dst_frames)); + } else { + sp.reset( + new CopyConverter(src_channels, src_frames, dst_channels, dst_frames)); + } + + return sp; +} + +// For CompositionConverter. +AudioConverter::AudioConverter() + : src_channels_(0), src_frames_(0), dst_channels_(0), dst_frames_(0) {} + +AudioConverter::AudioConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, + size_t dst_frames) + : src_channels_(src_channels), + src_frames_(src_frames), + dst_channels_(dst_channels), + dst_frames_(dst_frames) { + RTC_CHECK(dst_channels == src_channels || dst_channels == 1 || + src_channels == 1); +} + +void AudioConverter::CheckSizes(size_t src_size, size_t dst_capacity) const { + RTC_CHECK_EQ(src_size, src_channels() * src_frames()); + RTC_CHECK_GE(dst_capacity, dst_channels() * dst_frames()); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/audio_converter.h b/pkg/apm/webrtc/common_audio/audio_converter.h new file mode 100644 index 00000000..4afbb6d0 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/audio_converter.h @@ -0,0 +1,72 @@ +/* + * 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_AUDIO_CONVERTER_H_ +#define COMMON_AUDIO_AUDIO_CONVERTER_H_ + +#include + +#include + +namespace webrtc { + +// Format conversion (remixing and resampling) for audio. Only simple remixing +// 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 +// the number of frames is equivalent to specifying the sample rates. +class AudioConverter { + public: + // Returns a new AudioConverter, which will use the supplied format for its + // lifetime. Caller is responsible for the memory. + static std::unique_ptr Create(size_t src_channels, + size_t src_frames, + size_t dst_channels, + size_t dst_frames); + virtual ~AudioConverter() {} + + 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, + size_t src_size, + float* const* dst, + size_t dst_capacity) = 0; + + size_t src_channels() const { return src_channels_; } + size_t src_frames() const { return src_frames_; } + size_t dst_channels() const { return dst_channels_; } + size_t dst_frames() const { return dst_frames_; } + + protected: + AudioConverter(); + AudioConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, + size_t dst_frames); + + // Helper to RTC_CHECK that inputs are correctly sized. + void CheckSizes(size_t src_size, size_t dst_capacity) const; + + private: + const size_t src_channels_; + const size_t src_frames_; + const size_t dst_channels_; + const size_t dst_frames_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_AUDIO_CONVERTER_H_ diff --git a/pkg/apm/webrtc/common_audio/audio_util.cc b/pkg/apm/webrtc/common_audio/audio_util.cc new file mode 100644 index 00000000..b1e4d9ac --- /dev/null +++ b/pkg/apm/webrtc/common_audio/audio_util.cc @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "common_audio/include/audio_util.h" + +namespace webrtc { + +void FloatToS16(const float* src, size_t size, int16_t* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = FloatToS16(src[i]); +} + +void S16ToFloat(const int16_t* src, size_t size, float* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = S16ToFloat(src[i]); +} + +void S16ToFloatS16(const int16_t* src, size_t size, float* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = src[i]; +} + +void FloatS16ToS16(const float* src, size_t size, int16_t* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = FloatS16ToS16(src[i]); +} + +void FloatToFloatS16(const float* src, size_t size, float* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = FloatToFloatS16(src[i]); +} + +void FloatS16ToFloat(const float* src, size_t size, float* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = FloatS16ToFloat(src[i]); +} + +template <> +void DownmixInterleavedToMono(const int16_t* interleaved, + size_t num_frames, + int num_channels, + int16_t* deinterleaved) { + DownmixInterleavedToMonoImpl(interleaved, num_frames, + num_channels, deinterleaved); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/avx2/avx2.go b/pkg/apm/webrtc/common_audio/avx2/avx2.go new file mode 100644 index 00000000..d2952ca2 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/avx2/avx2.go @@ -0,0 +1,8 @@ +//go:build console && amd64 + +package avx2 + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -march=haswell -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +import "C" diff --git a/pkg/apm/webrtc/common_audio/avx2/fir_filter_avx2.cc b/pkg/apm/webrtc/common_audio/avx2/fir_filter_avx2.cc new file mode 100644 index 00000000..9cb0f770 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/avx2/fir_filter_avx2.cc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "common_audio/fir_filter_avx2.h" + +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/memory/aligned_malloc.h" + +namespace webrtc { + +FIRFilterAVX2::FIRFilterAVX2(const float* unaligned_coefficients, + size_t unaligned_coefficients_length, + size_t max_input_length) + : // Closest higher multiple of eight. + coefficients_length_((unaligned_coefficients_length + 7) & ~0x07), + state_length_(coefficients_length_ - 1), + coefficients_(static_cast( + AlignedMalloc(sizeof(float) * coefficients_length_, 32))), + state_(static_cast( + AlignedMalloc(sizeof(float) * (max_input_length + state_length_), + 32))) { + // Add zeros at the end of the coefficients. + RTC_DCHECK_GE(coefficients_length_, unaligned_coefficients_length); + size_t padding = coefficients_length_ - unaligned_coefficients_length; + memset(coefficients_.get(), 0, padding * sizeof(coefficients_[0])); + // The coefficients are reversed to compensate for the order in which the + // input samples are acquired (most recent last). + for (size_t i = 0; i < unaligned_coefficients_length; ++i) { + coefficients_[i + padding] = + unaligned_coefficients[unaligned_coefficients_length - i - 1]; + } + memset(state_.get(), 0, + (max_input_length + state_length_) * sizeof(state_[0])); +} + +FIRFilterAVX2::~FIRFilterAVX2() = default; + +void FIRFilterAVX2::Filter(const float* in, size_t length, float* out) { + RTC_DCHECK_GT(length, 0); + + memcpy(&state_[state_length_], in, length * sizeof(*in)); + + // 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]; + float* coef_ptr = coefficients_.get(); + + __m256 m_sum = _mm256_setzero_ps(); + __m256 m_in; + + // Depending on if the pointer is aligned with 32 bytes or not it is loaded + // differently. + if (reinterpret_cast(in_ptr) & 0x1F) { + for (size_t j = 0; j < coefficients_length_; j += 8) { + m_in = _mm256_loadu_ps(in_ptr + j); + m_sum = _mm256_fmadd_ps(m_in, _mm256_load_ps(coef_ptr + j), m_sum); + } + } else { + for (size_t j = 0; j < coefficients_length_; j += 8) { + m_in = _mm256_load_ps(in_ptr + j); + m_sum = _mm256_fmadd_ps(m_in, _mm256_load_ps(coef_ptr + j), m_sum); + } + } + __m128 m128_sum = _mm_add_ps(_mm256_extractf128_ps(m_sum, 0), + _mm256_extractf128_ps(m_sum, 1)); + m128_sum = _mm_add_ps(_mm_movehl_ps(m128_sum, m128_sum), m128_sum); + _mm_store_ss(out + i, + _mm_add_ss(m128_sum, _mm_shuffle_ps(m128_sum, m128_sum, 1))); + } + + // Update current state. + memmove(state_.get(), &state_[length], state_length_ * sizeof(state_[0])); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/channel_buffer.cc b/pkg/apm/webrtc/common_audio/channel_buffer.cc new file mode 100644 index 00000000..b9b8c25e --- /dev/null +++ b/pkg/apm/webrtc/common_audio/channel_buffer.cc @@ -0,0 +1,80 @@ +/* + * 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/channel_buffer.h" + +#include + +#include "common_audio/include/audio_util.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +IFChannelBuffer::IFChannelBuffer(size_t num_frames, + size_t num_channels, + size_t num_bands) + : ivalid_(true), + ibuf_(num_frames, num_channels, num_bands), + fvalid_(true), + fbuf_(num_frames, num_channels, num_bands) {} + +IFChannelBuffer::~IFChannelBuffer() = default; + +ChannelBuffer* IFChannelBuffer::ibuf() { + RefreshI(); + fvalid_ = false; + return &ibuf_; +} + +ChannelBuffer* IFChannelBuffer::fbuf() { + RefreshF(); + ivalid_ = false; + return &fbuf_; +} + +const ChannelBuffer* IFChannelBuffer::ibuf_const() const { + RefreshI(); + return &ibuf_; +} + +const ChannelBuffer* IFChannelBuffer::fbuf_const() const { + RefreshF(); + return &fbuf_; +} + +void IFChannelBuffer::RefreshF() const { + if (!fvalid_) { + RTC_DCHECK(ivalid_); + fbuf_.set_num_channels(ibuf_.num_channels()); + const int16_t* const* int_channels = ibuf_.channels(); + float* const* float_channels = fbuf_.channels(); + for (size_t i = 0; i < ibuf_.num_channels(); ++i) { + for (size_t j = 0; j < ibuf_.num_frames(); ++j) { + float_channels[i][j] = int_channels[i][j]; + } + } + fvalid_ = true; + } +} + +void IFChannelBuffer::RefreshI() const { + if (!ivalid_) { + RTC_DCHECK(fvalid_); + int16_t* const* int_channels = ibuf_.channels(); + ibuf_.set_num_channels(fbuf_.num_channels()); + const float* const* float_channels = fbuf_.channels(); + for (size_t i = 0; i < fbuf_.num_channels(); ++i) { + FloatS16ToS16(float_channels[i], ibuf_.num_frames(), int_channels[i]); + } + ivalid_ = true; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/channel_buffer.h b/pkg/apm/webrtc/common_audio/channel_buffer.h new file mode 100644 index 00000000..efe761cf --- /dev/null +++ b/pkg/apm/webrtc/common_audio/channel_buffer.h @@ -0,0 +1,251 @@ +/* + * 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_CHANNEL_BUFFER_H_ +#define COMMON_AUDIO_CHANNEL_BUFFER_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "common_audio/include/audio_util.h" +#include "rtc_base/checks.h" +#include "rtc_base/gtest_prod_util.h" + +namespace webrtc { + +// TODO: b/335805780 - Remove this method. Instead, use Deinterleave() from +// audio_util.h which requires size checked buffer views. +template +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 +void Interleave(const T* const* deinterleaved, + size_t samples_per_channel, + size_t num_channels, + InterleavedView& 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_`: +// { [ --- b1ch1 --- ] [ --- b2ch1 --- ] [ --- b1ch2 --- ] [ --- b2ch2 --- ] } +// +// The pointer arrays for the same example are as follows: +// +// `channels_`: +// { [ b1ch1* ] [ b1ch2* ] [ b2ch1* ] [ b2ch2* ] } +// +// `bands_`: +// { [ b1ch1* ] [ b2ch1* ] [ b1ch2* ] [ b2ch2* ] } +template +class ChannelBuffer { + public: + ChannelBuffer(size_t num_frames, size_t num_channels, size_t num_bands = 1) + : data_(new T[num_frames * num_channels]()), + channels_(new T*[num_channels * num_bands]), + bands_(new T*[num_channels * num_bands]), + num_frames_(num_frames), + num_frames_per_band_(num_frames / num_bands), + num_allocated_channels_(num_channels), + num_channels_(num_channels), + num_bands_(num_bands), + bands_view_(num_allocated_channels_, + std::vector>(num_bands_)), + channels_view_(num_bands_, + std::vector>(num_allocated_channels_)) { + // Temporarily cast away const_ness to allow populating the array views. + auto* bands_view = + const_cast>>*>(&bands_view_); + auto* channels_view = + const_cast>>*>(&channels_view_); + + for (size_t ch = 0; ch < num_allocated_channels_; ++ch) { + for (size_t band = 0; band < num_bands_; ++band) { + (*channels_view)[band][ch] = + ArrayView(&data_[ch * num_frames_ + band * num_frames_per_band_], + num_frames_per_band_); + (*bands_view)[ch][band] = channels_view_[band][ch]; + channels_[band * num_allocated_channels_ + ch] = + channels_view_[band][ch].data(); + bands_[ch * num_bands_ + band] = + channels_[band * num_allocated_channels_ + ch]; + } + } + } + + // Returns a pointer array to the channels. + // 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_` + + // 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_` + const T* const* channels(size_t band = 0) const { + RTC_DCHECK_LT(band, num_bands_); + return &channels_[band * num_allocated_channels_]; + } + T* const* channels(size_t band = 0) { + const ChannelBuffer* t = this; + return const_cast(t->channels(band)); + } + ArrayView> channels_view(size_t band = 0) { + return channels_view_[band]; + } + ArrayView> channels_view(size_t band = 0) const { + return channels_view_[band]; + } + + // Returns a pointer array to the bands for a specific channel. + // Usage: + // bands(channel)[band][sample]. + // Where: + // 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); + return &bands_[channel * num_bands_]; + } + T* const* bands(size_t channel) { + const ChannelBuffer* t = this; + return const_cast(t->bands(channel)); + } + + ArrayView> bands_view(size_t channel) { + return bands_view_[channel]; + } + ArrayView> bands_view(size_t channel) const { + return bands_view_[channel]; + } + + // 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) + slice[i] = &channels_[i][start_frame]; + return slice; + } + T** Slice(T** slice, size_t start_frame) { + const ChannelBuffer* t = this; + return const_cast(t->Slice(slice, start_frame)); + } + + size_t num_frames() const { return num_frames_; } + size_t num_frames_per_band() const { return num_frames_per_band_; } + size_t num_channels() const { return num_channels_; } + size_t num_bands() const { return num_bands_; } + size_t size() const { return num_frames_ * num_allocated_channels_; } + + void set_num_channels(size_t num_channels) { + RTC_DCHECK_LE(num_channels, num_allocated_channels_); + num_channels_ = num_channels; + } + + void SetDataForTesting(const T* data, size_t size) { + RTC_CHECK_EQ(size, this->size()); + memcpy(data_.get(), data, size * sizeof(*data)); + } + + private: + std::unique_ptr data_; + std::unique_ptr channels_; + std::unique_ptr bands_; + const size_t num_frames_; + const size_t num_frames_per_band_; + // Number of channels the internal buffer holds. + const size_t num_allocated_channels_; + // Number of channels the user sees. + size_t num_channels_; + const size_t num_bands_; + const std::vector>> bands_view_; + const std::vector>> channels_view_; +}; + +// One int16_t and one float ChannelBuffer that are kept in sync. The sync is +// broken when someone requests write access to either ChannelBuffer, and +// reestablished when someone requests the outdated ChannelBuffer. It is +// therefore safe to use the return value of ibuf_const() and fbuf_const() +// until the next call to ibuf() or fbuf(), and the return value of ibuf() and +// fbuf() until the next call to any of the other functions. +class IFChannelBuffer { + public: + IFChannelBuffer(size_t num_frames, size_t num_channels, size_t num_bands = 1); + ~IFChannelBuffer(); + + ChannelBuffer* ibuf(); + ChannelBuffer* fbuf(); + const ChannelBuffer* ibuf_const() const; + const ChannelBuffer* fbuf_const() const; + + size_t num_frames() const { return ibuf_.num_frames(); } + size_t num_frames_per_band() const { return ibuf_.num_frames_per_band(); } + size_t num_channels() const { + return ivalid_ ? ibuf_.num_channels() : fbuf_.num_channels(); + } + void set_num_channels(size_t num_channels) { + ibuf_.set_num_channels(num_channels); + fbuf_.set_num_channels(num_channels); + } + size_t num_bands() const { return ibuf_.num_bands(); } + + private: + void RefreshF() const; + void RefreshI() const; + + mutable bool ivalid_; + mutable ChannelBuffer ibuf_; + mutable bool fvalid_; + mutable ChannelBuffer fbuf_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_CHANNEL_BUFFER_H_ diff --git a/pkg/apm/webrtc/common_audio/common_audio.go b/pkg/apm/webrtc/common_audio/common_audio.go new file mode 100644 index 00000000..38eb6ac8 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/common_audio.go @@ -0,0 +1,21 @@ +//go:build console + +package common_audio + +// #cgo CXXFLAGS: -I${SRCDIR}/.. -I${SRCDIR}/../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +// #cgo CFLAGS: -I${SRCDIR}/.. -I${SRCDIR}/../third_party/abseil-cpp -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CFLAGS: -DWEBRTC_WIN +// #cgo arm64 CFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" + +import ( + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/common_audio/resampler" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/common_audio/signal_processing" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/common_audio/vad" +) diff --git a/pkg/apm/webrtc/common_audio/common_audio_amd64.go b/pkg/apm/webrtc/common_audio/common_audio_amd64.go new file mode 100644 index 00000000..12acc896 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/common_audio_amd64.go @@ -0,0 +1,8 @@ +//go:build console && amd64 + +package common_audio + +import ( + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/common_audio/avx2" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/common_audio/resampler/avx2" +) diff --git a/pkg/apm/webrtc/common_audio/fir_filter.h b/pkg/apm/webrtc/common_audio/fir_filter.h new file mode 100644 index 00000000..e0b18ca4 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/fir_filter.h @@ -0,0 +1,30 @@ +/* + * 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_FIR_FILTER_H_ +#define COMMON_AUDIO_FIR_FILTER_H_ + +#include + +namespace webrtc { + +// Finite Impulse Response filter using floating-point arithmetic. +class FIRFilter { + public: + virtual ~FIRFilter() {} + + // 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; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_FIR_FILTER_H_ diff --git a/pkg/apm/webrtc/common_audio/fir_filter_avx2.h b/pkg/apm/webrtc/common_audio/fir_filter_avx2.h new file mode 100644 index 00000000..893b60bf --- /dev/null +++ b/pkg/apm/webrtc/common_audio/fir_filter_avx2.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef COMMON_AUDIO_FIR_FILTER_AVX2_H_ +#define COMMON_AUDIO_FIR_FILTER_AVX2_H_ + +#include + +#include + +#include "common_audio/fir_filter.h" +#include "rtc_base/memory/aligned_malloc.h" + +namespace webrtc { + +class FIRFilterAVX2 : public FIRFilter { + public: + FIRFilterAVX2(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + ~FIRFilterAVX2() override; + + void Filter(const float* in, size_t length, float* out) override; + + private: + const size_t coefficients_length_; + const size_t state_length_; + std::unique_ptr coefficients_; + std::unique_ptr state_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_FIR_FILTER_AVX2_H_ diff --git a/pkg/apm/webrtc/common_audio/fir_filter_c.cc b/pkg/apm/webrtc/common_audio/fir_filter_c.cc new file mode 100644 index 00000000..dc1c8e0d --- /dev/null +++ b/pkg/apm/webrtc/common_audio/fir_filter_c.cc @@ -0,0 +1,61 @@ +/* + * 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/fir_filter_c.h" + +#include + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +FIRFilterC::~FIRFilterC() {} + +FIRFilterC::FIRFilterC(const float* coefficients, size_t coefficients_length) + : coefficients_length_(coefficients_length), + state_length_(coefficients_length - 1), + coefficients_(new float[coefficients_length_]), + state_(new float[state_length_]) { + for (size_t i = 0; i < coefficients_length_; ++i) { + coefficients_[i] = coefficients[coefficients_length_ - i - 1]; + } + memset(state_.get(), 0, state_length_ * sizeof(state_[0])); +} + +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_` + // taking into account the previous state. + for (size_t i = 0; i < length; ++i) { + out[i] = 0.f; + size_t j; + for (j = 0; state_length_ > i && j < state_length_ - i; ++j) { + out[i] += state_[i + j] * coefficients_[j]; + } + for (; j < coefficients_length_; ++j) { + out[i] += in[j + i - state_length_] * coefficients_[j]; + } + } + + // Update current state. + if (length >= state_length_) { + memcpy(state_.get(), &in[length - state_length_], + state_length_ * sizeof(*in)); + } else { + memmove(state_.get(), &state_[length], + (state_length_ - length) * sizeof(state_[0])); + memcpy(&state_[state_length_ - length], in, length * sizeof(*in)); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/fir_filter_c.h b/pkg/apm/webrtc/common_audio/fir_filter_c.h new file mode 100644 index 00000000..b2ae4c32 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/fir_filter_c.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef COMMON_AUDIO_FIR_FILTER_C_H_ +#define COMMON_AUDIO_FIR_FILTER_C_H_ + +#include + +#include + +#include "common_audio/fir_filter.h" + +namespace webrtc { + +class FIRFilterC : public FIRFilter { + public: + FIRFilterC(const float* coefficients, size_t coefficients_length); + ~FIRFilterC() override; + + void Filter(const float* in, size_t length, float* out) override; + + private: + size_t coefficients_length_; + size_t state_length_; + std::unique_ptr coefficients_; + std::unique_ptr state_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_FIR_FILTER_C_H_ diff --git a/pkg/apm/webrtc/common_audio/fir_filter_factory.cc b/pkg/apm/webrtc/common_audio/fir_filter_factory.cc new file mode 100644 index 00000000..2ecef650 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/fir_filter_factory.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "common_audio/fir_filter_factory.h" + +#include "common_audio/fir_filter_c.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_HAS_NEON) +#include "common_audio/fir_filter_neon.h" +#elif defined(WEBRTC_ARCH_X86_FAMILY) +#include "common_audio/fir_filter_avx2.h" +#include "common_audio/fir_filter_sse.h" +#include "system_wrappers/include/cpu_features_wrapper.h" // kSSE2, WebRtc_G... +#endif + +namespace webrtc { + +FIRFilter* CreateFirFilter(const float* coefficients, + size_t coefficients_length, + size_t max_input_length) { + if (!coefficients || coefficients_length <= 0 || max_input_length <= 0) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } + + FIRFilter* filter = nullptr; +// If we know the minimum architecture at compile time, avoid CPU detection. +#if defined(WEBRTC_ARCH_X86_FAMILY) + // x86 CPU detection required. + if (GetCPUInfo(kAVX2)) { + filter = + new FIRFilterAVX2(coefficients, coefficients_length, max_input_length); + } else if (GetCPUInfo(kSSE2)) { + filter = + new FIRFilterSSE2(coefficients, coefficients_length, max_input_length); + } else { + filter = new FIRFilterC(coefficients, coefficients_length); + } +#elif defined(WEBRTC_HAS_NEON) + filter = + new FIRFilterNEON(coefficients, coefficients_length, max_input_length); +#else + filter = new FIRFilterC(coefficients, coefficients_length); +#endif + + return filter; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/fir_filter_factory.h b/pkg/apm/webrtc/common_audio/fir_filter_factory.h new file mode 100644 index 00000000..e76c3aef --- /dev/null +++ b/pkg/apm/webrtc/common_audio/fir_filter_factory.h @@ -0,0 +1,32 @@ +/* + * 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_FIR_FILTER_FACTORY_H_ +#define COMMON_AUDIO_FIR_FILTER_FACTORY_H_ + +#include + +namespace webrtc { + +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 +// necessary to concatenate the input after the state, and resizing this array +// dynamically is expensive. +FIRFilter* CreateFirFilter(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + +} // namespace webrtc + +#endif // COMMON_AUDIO_FIR_FILTER_FACTORY_H_ diff --git a/pkg/apm/webrtc/common_audio/fir_filter_neon.h b/pkg/apm/webrtc/common_audio/fir_filter_neon.h new file mode 100644 index 00000000..1ffefd80 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/fir_filter_neon.h @@ -0,0 +1,39 @@ +/* + * 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_FIR_FILTER_NEON_H_ +#define COMMON_AUDIO_FIR_FILTER_NEON_H_ + +#include + +#include "common_audio/fir_filter.h" +#include "rtc_base/memory/aligned_malloc.h" + +namespace webrtc { + +class FIRFilterNEON : public FIRFilter { + public: + FIRFilterNEON(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + ~FIRFilterNEON() override; + + void Filter(const float* in, size_t length, float* out) override; + + private: + size_t coefficients_length_; + size_t state_length_; + std::unique_ptr coefficients_; + std::unique_ptr state_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_FIR_FILTER_NEON_H_ diff --git a/pkg/apm/webrtc/common_audio/fir_filter_neon_arm64.cc b/pkg/apm/webrtc/common_audio/fir_filter_neon_arm64.cc new file mode 100644 index 00000000..346cb69f --- /dev/null +++ b/pkg/apm/webrtc/common_audio/fir_filter_neon_arm64.cc @@ -0,0 +1,73 @@ +/* + * 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/fir_filter_neon.h" + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/memory/aligned_malloc.h" + +namespace webrtc { + +FIRFilterNEON::~FIRFilterNEON() {} + +FIRFilterNEON::FIRFilterNEON(const float* coefficients, + size_t coefficients_length, + size_t max_input_length) + : // Closest higher multiple of four. + coefficients_length_((coefficients_length + 3) & ~0x03), + state_length_(coefficients_length_ - 1), + coefficients_(static_cast( + AlignedMalloc(sizeof(float) * coefficients_length_, 16))), + state_(static_cast( + AlignedMalloc(sizeof(float) * (max_input_length + state_length_), + 16))) { + // Add zeros at the end of the coefficients. + size_t padding = coefficients_length_ - coefficients_length; + memset(coefficients_.get(), 0.f, padding * sizeof(coefficients_[0])); + // The coefficients are reversed to compensate for the order in which the + // input samples are acquired (most recent last). + for (size_t i = 0; i < coefficients_length; ++i) { + coefficients_[i + padding] = coefficients[coefficients_length - i - 1]; + } + memset(state_.get(), 0.f, + (max_input_length + state_length_) * sizeof(state_[0])); +} + +void FIRFilterNEON::Filter(const float* in, size_t length, float* out) { + RTC_DCHECK_GT(length, 0); + + memcpy(&state_[state_length_], in, length * sizeof(*in)); + + // 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]; + float* coef_ptr = coefficients_.get(); + + float32x4_t m_sum = vmovq_n_f32(0); + float32x4_t m_in; + + for (size_t j = 0; j < coefficients_length_; j += 4) { + m_in = vld1q_f32(in_ptr + j); + m_sum = vmlaq_f32(m_sum, m_in, vld1q_f32(coef_ptr + j)); + } + + float32x2_t m_half = vadd_f32(vget_high_f32(m_sum), vget_low_f32(m_sum)); + out[i] = vget_lane_f32(vpadd_f32(m_half, m_half), 0); + } + + // Update current state. + memmove(state_.get(), &state_[length], state_length_ * sizeof(state_[0])); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/fir_filter_sse.h b/pkg/apm/webrtc/common_audio/fir_filter_sse.h new file mode 100644 index 00000000..32f4945a --- /dev/null +++ b/pkg/apm/webrtc/common_audio/fir_filter_sse.h @@ -0,0 +1,41 @@ +/* + * 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_FIR_FILTER_SSE_H_ +#define COMMON_AUDIO_FIR_FILTER_SSE_H_ + +#include + +#include + +#include "common_audio/fir_filter.h" +#include "rtc_base/memory/aligned_malloc.h" + +namespace webrtc { + +class FIRFilterSSE2 : public FIRFilter { + public: + FIRFilterSSE2(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + ~FIRFilterSSE2() override; + + void Filter(const float* in, size_t length, float* out) override; + + private: + size_t coefficients_length_; + size_t state_length_; + std::unique_ptr coefficients_; + std::unique_ptr state_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_FIR_FILTER_SSE_H_ diff --git a/pkg/apm/webrtc/common_audio/fir_filter_sse_amd64.cc b/pkg/apm/webrtc/common_audio/fir_filter_sse_amd64.cc new file mode 100644 index 00000000..0e45994a --- /dev/null +++ b/pkg/apm/webrtc/common_audio/fir_filter_sse_amd64.cc @@ -0,0 +1,82 @@ +/* + * 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/fir_filter_sse.h" + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/memory/aligned_malloc.h" + +namespace webrtc { + +FIRFilterSSE2::~FIRFilterSSE2() {} + +FIRFilterSSE2::FIRFilterSSE2(const float* coefficients, + size_t coefficients_length, + size_t max_input_length) + : // Closest higher multiple of four. + coefficients_length_((coefficients_length + 3) & ~0x03), + state_length_(coefficients_length_ - 1), + coefficients_(static_cast( + AlignedMalloc(sizeof(float) * coefficients_length_, 16))), + state_(static_cast( + AlignedMalloc(sizeof(float) * (max_input_length + state_length_), + 16))) { + // Add zeros at the end of the coefficients. + size_t padding = coefficients_length_ - coefficients_length; + memset(coefficients_.get(), 0, padding * sizeof(coefficients_[0])); + // The coefficients are reversed to compensate for the order in which the + // input samples are acquired (most recent last). + for (size_t i = 0; i < coefficients_length; ++i) { + coefficients_[i + padding] = coefficients[coefficients_length - i - 1]; + } + memset(state_.get(), 0, + (max_input_length + state_length_) * sizeof(state_[0])); +} + +void FIRFilterSSE2::Filter(const float* in, size_t length, float* out) { + RTC_DCHECK_GT(length, 0); + + memcpy(&state_[state_length_], in, length * sizeof(*in)); + + // 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]; + float* coef_ptr = coefficients_.get(); + + __m128 m_sum = _mm_setzero_ps(); + __m128 m_in; + + // Depending on if the pointer is aligned with 16 bytes or not it is loaded + // differently. + if (reinterpret_cast(in_ptr) & 0x0F) { + for (size_t j = 0; j < coefficients_length_; j += 4) { + m_in = _mm_loadu_ps(in_ptr + j); + m_sum = _mm_add_ps(m_sum, _mm_mul_ps(m_in, _mm_load_ps(coef_ptr + j))); + } + } else { + for (size_t j = 0; j < coefficients_length_; j += 4) { + m_in = _mm_load_ps(in_ptr + j); + m_sum = _mm_add_ps(m_sum, _mm_mul_ps(m_in, _mm_load_ps(coef_ptr + j))); + } + } + m_sum = _mm_add_ps(_mm_movehl_ps(m_sum, m_sum), m_sum); + _mm_store_ss(out + i, _mm_add_ss(m_sum, _mm_shuffle_ps(m_sum, m_sum, 1))); + } + + // Update current state. + memmove(state_.get(), &state_[length], state_length_ * sizeof(state_[0])); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/include/audio_util.h b/pkg/apm/webrtc/common_audio/include/audio_util.h new file mode 100644 index 00000000..672ebd1a --- /dev/null +++ b/pkg/apm/webrtc/common_audio/include/audio_util.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ +#define COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ + +#include + +#include +#include +#include +#include + +#include "api/audio/audio_view.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +typedef std::numeric_limits 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] +// FloatS16: float [-32768.0, 32768.0] +// Dbfs: float [-20.0*log(10, 32768), 0] = [-90.3, 0] +// The ratio conversion functions use this naming convention: +// Ratio: float (0, +inf) +// Db: float (-inf, +inf) +static inline float S16ToFloat(int16_t v) { + constexpr float kScaling = 1.f / 32768.f; + return v * kScaling; +} + +static inline int16_t FloatS16ToS16(float v) { + v = std::min(v, 32767.f); + v = std::max(v, -32768.f); + return static_cast(v + std::copysign(0.5f, v)); +} + +static inline int16_t FloatToS16(float v) { + v *= 32768.f; + v = std::min(v, 32767.f); + v = std::max(v, -32768.f); + return static_cast(v + std::copysign(0.5f, v)); +} + +static inline float FloatToFloatS16(float v) { + v = std::min(v, 1.f); + v = std::max(v, -1.f); + return v * 32768.f; +} + +static inline float FloatS16ToFloat(float v) { + v = std::min(v, 32768.f); + v = std::max(v, -32768.f); + constexpr float kScaling = 1.f / 32768.f; + return v * kScaling; +} + +void FloatToS16(const float* src, size_t size, int16_t* dest); +void S16ToFloat(const int16_t* src, size_t size, float* dest); +void S16ToFloatS16(const int16_t* src, size_t size, float* dest); +void FloatS16ToS16(const float* src, size_t size, int16_t* dest); +void FloatToFloatS16(const float* src, size_t size, float* dest); +void FloatS16ToFloat(const float* src, size_t size, float* dest); + +inline float DbToRatio(float v) { + return std::pow(10.0f, v / 20.0f); +} + +inline float DbfsToFloatS16(float v) { + static constexpr float kMaximumAbsFloatS16 = -limits_int16::min(); + return DbToRatio(v) * kMaximumAbsFloatS16; +} + +inline float FloatS16ToDbfs(float v) { + RTC_DCHECK_GE(v, 0); + + // kMinDbfs is equal to -20.0 * log10(-limits_int16::min()) + static constexpr float kMinDbfs = -90.30899869919436f; + if (v <= 1.0f) { + return kMinDbfs; + } + // Equal to 20 * log10(v / (-limits_int16::min())) + 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`. +// TODO: b/335805780 - Accept ArrayView. +template +void CopyAudioIfNeeded(const T* const* src, + int num_frames, + int num_channels, + T* const* dest) { + for (int i = 0; i < num_channels; ++i) { + if (src[i] != dest[i]) { + std::copy(src[i], src[i] + num_frames, dest[i]); + } + } +} + +// 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 +void Deinterleave(const InterleavedView& interleaved, + const DeinterleavedView& 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) { + MonoView 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 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 +void Interleave(const DeinterleavedView& deinterleaved, + const InterleavedView& 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 < deinterleaved.samples_per_channel(); ++j) { + interleaved[interleaved_idx] = channel[j]; + interleaved_idx += deinterleaved.num_channels(); + } + } +} + +// Downmixes an interleaved multichannel signal to a single channel by averaging +// all channels. +// TODO: b/335805780 - Accept InterleavedView and DeinterleavedView. +template +void DownmixInterleavedToMonoImpl(const T* interleaved, + size_t num_frames, + int num_channels, + T* deinterleaved) { + RTC_DCHECK_GT(num_channels, 0); + RTC_DCHECK_GT(num_frames, 0); + + const T* const end = interleaved + num_frames * num_channels; + + while (interleaved < end) { + const T* const frame_end = interleaved + num_channels; + + Intermediate value = *interleaved++; + while (interleaved < frame_end) { + value += *interleaved++; + } + + *deinterleaved++ = value / num_channels; + } +} + +// TODO: b/335805780 - Accept InterleavedView and DeinterleavedView. +template +void DownmixInterleavedToMono(const T* interleaved, + size_t num_frames, + int num_channels, + T* deinterleaved); + +// TODO: b/335805780 - Accept InterleavedView and DeinterleavedView. +template <> +void DownmixInterleavedToMono(const int16_t* interleaved, + size_t num_frames, + int num_channels, + int16_t* deinterleaved); + +} // namespace webrtc + +#endif // COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ diff --git a/pkg/apm/webrtc/common_audio/real_fourier.cc b/pkg/apm/webrtc/common_audio/real_fourier.cc new file mode 100644 index 00000000..7365844e --- /dev/null +++ b/pkg/apm/webrtc/common_audio/real_fourier.cc @@ -0,0 +1,51 @@ +/* + * 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::Create(int fft_order) { + return std::unique_ptr(new RealFourierOoura(fft_order)); +} + +int RealFourier::FftOrder(size_t length) { + RTC_CHECK_GT(length, 0U); + return WebRtcSpl_GetSizeInBits(static_cast(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( + AlignedMalloc(sizeof(float) * count, kFftBufferAlignment))); +} + +RealFourier::fft_cplx_scoper RealFourier::AllocCplxBuffer(int count) { + return fft_cplx_scoper(static_cast*>( + AlignedMalloc(sizeof(complex) * count, kFftBufferAlignment))); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/real_fourier.h b/pkg/apm/webrtc/common_audio/real_fourier.h new file mode 100644 index 00000000..78a4fc66 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/real_fourier.h @@ -0,0 +1,76 @@ +/* + * 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 + +#include +#include + +#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 fft_real_scoper; + typedef std::unique_ptr[], 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 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* dest) const = 0; + + // Inverse transform. Same input format as output above, conjugate pairs + // not needed. + virtual void Inverse(const std::complex* src, float* dest) const = 0; + + virtual int order() const = 0; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_REAL_FOURIER_H_ diff --git a/pkg/apm/webrtc/common_audio/real_fourier_ooura.cc b/pkg/apm/webrtc/common_audio/real_fourier_ooura.cc new file mode 100644 index 00000000..9acda549 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/real_fourier_ooura.cc @@ -0,0 +1,91 @@ +/* + * 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 +#include + +#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* array, size_t complex_length) { + std::for_each(array, array + complex_length, + [=](complex& v) { v = std::conj(v); }); +} + +size_t ComputeWorkIpSize(size_t fft_length) { + return static_cast( + 2 + std::ceil(std::sqrt(static_cast(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* 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(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(dest[0].imag(), 0.0f); + dest[0] = complex(dest[0].real(), 0.0f); + // Ooura returns the conjugate of the usual Fourier definition. + Conjugate(dest, complex_length_); +} + +void RealFourierOoura::Inverse(const complex* src, float* dest) const { + { + auto* dest_complex = reinterpret_cast*>(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(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 diff --git a/pkg/apm/webrtc/common_audio/real_fourier_ooura.h b/pkg/apm/webrtc/common_audio/real_fourier_ooura.h new file mode 100644 index 00000000..ae85dfd0 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/real_fourier_ooura.h @@ -0,0 +1,45 @@ +/* + * 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 + +#include +#include + +#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* dest) const override; + void Inverse(const std::complex* 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 work_ip_; + const std::unique_ptr work_w_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_REAL_FOURIER_OOURA_H_ diff --git a/pkg/apm/webrtc/common_audio/resampler/avx2/avx2.go b/pkg/apm/webrtc/common_audio/resampler/avx2/avx2.go new file mode 100644 index 00000000..3c693a2e --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/avx2/avx2.go @@ -0,0 +1,8 @@ +//go:build console && amd64 + +package avx2 + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -march=haswell -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +import "C" diff --git a/pkg/apm/webrtc/common_audio/resampler/avx2/sinc_resampler_avx2.cc b/pkg/apm/webrtc/common_audio/resampler/avx2/sinc_resampler_avx2.cc new file mode 100644 index 00000000..d945a10b --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/avx2/sinc_resampler_avx2.cc @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include +#include + +#include "common_audio/resampler/sinc_resampler.h" + +namespace webrtc { + +float SincResampler::Convolve_AVX2(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor) { + __m256 m_input; + __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 + // these loops has not been tested or benchmarked. + bool aligned_input = (reinterpret_cast(input_ptr) & 0x1F) == 0; + if (!aligned_input) { + for (size_t i = 0; i < kKernelSize; i += 8) { + m_input = _mm256_loadu_ps(input_ptr + i); + m_sums1 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k1 + i), m_sums1); + m_sums2 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k2 + i), m_sums2); + } + } else { + for (size_t i = 0; i < kKernelSize; i += 8) { + m_input = _mm256_load_ps(input_ptr + i); + m_sums1 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k1 + i), m_sums1); + m_sums2 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k2 + i), m_sums2); + } + } + + // Linearly interpolate the two "convolutions". + __m128 m128_sums1 = _mm_add_ps(_mm256_extractf128_ps(m_sums1, 0), + _mm256_extractf128_ps(m_sums1, 1)); + __m128 m128_sums2 = _mm_add_ps(_mm256_extractf128_ps(m_sums2, 0), + _mm256_extractf128_ps(m_sums2, 1)); + m128_sums1 = _mm_mul_ps( + m128_sums1, + _mm_set_ps1(static_cast(1.0 - kernel_interpolation_factor))); + m128_sums2 = _mm_mul_ps( + m128_sums2, _mm_set_ps1(static_cast(kernel_interpolation_factor))); + m128_sums1 = _mm_add_ps(m128_sums1, m128_sums2); + + // Sum components together. + float result; + m128_sums2 = _mm_add_ps(_mm_movehl_ps(m128_sums1, m128_sums1), m128_sums1); + _mm_store_ss(&result, _mm_add_ss(m128_sums2, + _mm_shuffle_ps(m128_sums2, m128_sums2, 1))); + + return result; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/resampler/include/push_resampler.h b/pkg/apm/webrtc/common_audio/resampler/include/push_resampler.h new file mode 100644 index 00000000..394e96b1 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/include/push_resampler.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_ +#define COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_ + +#include +#include + +#include "api/audio/audio_view.h" + +namespace webrtc { + +class PushSincResampler; + +// Wraps PushSincResampler to provide stereo support. +// Note: This implementation assumes 10ms buffer sizes throughout. +template +class PushResampler final { + public: + PushResampler(); + 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(InterleavedView src, InterleavedView dst); + // For when a deinterleaved/mono channel already exists and we can skip the + // deinterleaved operation. + int Resample(MonoView src, MonoView dst); + + private: + // 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); + + // Buffers used for when a deinterleaving step is necessary. + std::unique_ptr source_; + std::unique_ptr destination_; + DeinterleavedView source_view_; + DeinterleavedView destination_view_; + + std::vector> resamplers_; +}; +} // namespace webrtc + +#endif // COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_ diff --git a/pkg/apm/webrtc/common_audio/resampler/include/resampler.h b/pkg/apm/webrtc/common_audio/resampler/include/resampler.h new file mode 100644 index 00000000..41940f9a --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/include/resampler.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * A wrapper for resampling a numerous amount of sampling combinations. + */ + +#ifndef COMMON_AUDIO_RESAMPLER_INCLUDE_RESAMPLER_H_ +#define COMMON_AUDIO_RESAMPLER_INCLUDE_RESAMPLER_H_ + +#include +#include + +namespace webrtc { + +// All methods return 0 on success and -1 on failure. +class Resampler { + public: + Resampler(); + Resampler(int inFreq, int outFreq, size_t num_channels); + ~Resampler(); + + // Reset all states + int Reset(int inFreq, int outFreq, size_t num_channels); + + // Reset all states if any parameter has changed + int ResetIfNeeded(int inFreq, int outFreq, size_t num_channels); + + // Resample samplesIn to samplesOut. + int Push(const int16_t* samplesIn, + size_t lengthIn, + int16_t* samplesOut, + size_t maxLen, + size_t& outLen); // NOLINT: to avoid changing APIs + + private: + enum ResamplerMode { + kResamplerMode1To1, + kResamplerMode1To2, + kResamplerMode1To3, + kResamplerMode1To4, + kResamplerMode1To6, + kResamplerMode1To12, + kResamplerMode2To3, + kResamplerMode2To11, + kResamplerMode4To11, + kResamplerMode8To11, + kResamplerMode11To16, + kResamplerMode11To32, + kResamplerMode2To1, + kResamplerMode3To1, + kResamplerMode4To1, + kResamplerMode6To1, + kResamplerMode12To1, + kResamplerMode3To2, + kResamplerMode11To2, + kResamplerMode11To4, + kResamplerMode11To8 + }; + + // Computes the resampler mode for a given sampling frequency pair. + // Returns -1 for unsupported frequency pairs. + static int ComputeResamplerMode(int in_freq_hz, + int out_freq_hz, + ResamplerMode* mode); + + // Generic pointers since we don't know what states we'll need + void* state1_; + void* state2_; + void* state3_; + + // Storage if needed + int16_t* in_buffer_; + int16_t* out_buffer_; + size_t in_buffer_size_; + size_t out_buffer_size_; + size_t in_buffer_size_max_; + size_t out_buffer_size_max_; + + int my_in_frequency_khz_; + int my_out_frequency_khz_; + ResamplerMode my_mode_; + size_t num_channels_; + + // Extra instance for stereo + Resampler* helper_left_; + Resampler* helper_right_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_RESAMPLER_INCLUDE_RESAMPLER_H_ diff --git a/pkg/apm/webrtc/common_audio/resampler/push_resampler.cc b/pkg/apm/webrtc/common_audio/resampler/push_resampler.cc new file mode 100644 index 00000000..2e75679c --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/push_resampler.cc @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "common_audio/resampler/include/push_resampler.h" + +#include +#include + +#include + +#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 { +// 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 +PushResampler::PushResampler() = default; + +template +PushResampler::PushResampler(size_t src_samples_per_channel, + size_t dst_samples_per_channel, + size_t num_channels) { + EnsureInitialized(src_samples_per_channel, dst_samples_per_channel, + num_channels); +} + +template +PushResampler::~PushResampler() = default; + +template +void PushResampler::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(source_.get(), src_samples_per_channel, + num_channels); + destination_view_ = DeinterleavedView( + 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( + src_samples_per_channel, dst_samples_per_channel); + } +} + +template +int PushResampler::Resample(InterleavedView src, + InterleavedView 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. + CopySamples(dst, src); + return static_cast(src.data().size()); + } + + Deinterleave(src, source_view_); + + 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)); + } + + Interleave(destination_view_, dst); + return static_cast(dst.size()); +} + +template +int PushResampler::Resample(MonoView src, MonoView dst) { + RTC_DCHECK_EQ(resamplers_.size(), 1); + RTC_DCHECK_EQ(SamplesPerChannel(src), SamplesPerChannel(source_view_)); + RTC_DCHECK_EQ(SamplesPerChannel(dst), SamplesPerChannel(destination_view_)); + + if (SamplesPerChannel(src) == SamplesPerChannel(dst)) { + CopySamples(dst, src); + return static_cast(src.size()); + } + + return resamplers_[0]->Resample(src, dst); +} + +// Explictly generate required instantiations. +template class PushResampler; +template class PushResampler; + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/resampler/push_sinc_resampler.cc b/pkg/apm/webrtc/common_audio/resampler/push_sinc_resampler.cc new file mode 100644 index 00000000..75bf33a7 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/push_sinc_resampler.cc @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "common_audio/resampler/push_sinc_resampler.h" + +#include + +#include "common_audio/include/audio_util.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +PushSincResampler::PushSincResampler(size_t source_frames, + size_t destination_frames) + : resampler_(new SincResampler(source_frames * 1.0 / destination_frames, + source_frames, + this)), + source_ptr_(nullptr), + source_ptr_int_(nullptr), + destination_frames_(destination_frames), + first_pass_(true), + source_available_(0) {} + +PushSincResampler::~PushSincResampler() {} + +size_t PushSincResampler::Resample(const int16_t* source, + size_t source_length, + int16_t* destination, + size_t /* destination_capacity */) { + if (!float_buffer_.get()) + float_buffer_.reset(new float[destination_frames_]); + + source_ptr_int_ = source; + // Pass nullptr as the float source to have Run() read from the int16 source. + Resample(nullptr, source_length, float_buffer_.get(), destination_frames_); + FloatS16ToS16(float_buffer_.get(), destination_frames_, destination); + source_ptr_int_ = nullptr; + return destination_frames_; +} + +size_t PushSincResampler::Resample(const float* source, + size_t source_length, + float* destination, + size_t destination_capacity) { + RTC_CHECK_EQ(source_length, resampler_->request_frames()); + RTC_CHECK_GE(destination_capacity, destination_frames_); + // Cache the source pointer. Calling Resample() will immediately trigger + // the Run() callback whereupon we provide the cached value. + source_ptr_ = source; + source_available_ = source_length; + + // On the first pass, we call Resample() twice. During the first call, we + // provide dummy input and discard the output. This is done to prime the + // SincResampler buffer with the correct delay (half the kernel size), thereby + // ensuring that all later Resample() calls will only result in one input + // 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 + // 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`. + if (first_pass_) + resampler_->Resample(resampler_->ChunkSize(), destination); + + resampler_->Resample(destination_frames_, destination); + source_ptr_ = nullptr; + return destination_frames_; +} + +void PushSincResampler::Run(size_t frames, float* destination) { + // Ensure we are only asked for the available samples. This would fail if + // Run() was triggered more than once per Resample() call. + RTC_CHECK_EQ(source_available_, frames); + + if (first_pass_) { + // Provide dummy input on the first pass, the output of which will be + // discarded, as described in Resample(). + std::memset(destination, 0, frames * sizeof(*destination)); + first_pass_ = false; + return; + } + + if (source_ptr_) { + std::memcpy(destination, source_ptr_, frames * sizeof(*destination)); + } else { + for (size_t i = 0; i < frames; ++i) + destination[i] = static_cast(source_ptr_int_[i]); + } + source_available_ -= frames; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/resampler/push_sinc_resampler.h b/pkg/apm/webrtc/common_audio/resampler/push_sinc_resampler.h new file mode 100644 index 00000000..b2c71555 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/push_sinc_resampler.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_ +#define COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_ + +#include +#include + +#include + +#include "api/audio/audio_view.h" +#include "common_audio/resampler/sinc_resampler.h" + +namespace webrtc { + +// A thin wrapper over SincResampler to provide a push-based interface as +// required by WebRTC. SincResampler uses a pull-based interface, and will +// use SincResamplerCallback::Run() to request data upon a call to Resample(). +// These Run() calls will happen on the same thread Resample() is called on. +class PushSincResampler : public SincResamplerCallback { + public: + // Provide the size of the source and destination blocks in samples. These + // must correspond to the same time duration (typically 10 ms) as the sample + // ratio is inferred from them. + PushSincResampler(size_t source_frames, size_t destination_frames); + ~PushSincResampler() override; + + 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`). + template + size_t Resample(const MonoView& source, const MonoView& 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, + size_t destination_capacity); + size_t Resample(const float* source, + size_t source_frames, + float* destination, + size_t destination_capacity); + + // Delay due to the filter kernel. Essentially, the time after which an input + // sample will appear in the resampled output. + static float AlgorithmicDelaySeconds(int source_rate_hz) { + return 1.f / source_rate_hz * SincResampler::kKernelSize / 2; + } + + protected: + // Implements SincResamplerCallback. + void Run(size_t frames, float* destination) override; + + private: + friend class PushSincResamplerTest; + SincResampler* get_resampler_for_testing() { return resampler_.get(); } + + std::unique_ptr resampler_; + std::unique_ptr float_buffer_; + const float* source_ptr_; + const int16_t* source_ptr_int_; + const size_t destination_frames_; + + // True on the first call to Resample(), to prime the SincResampler buffer. + bool first_pass_; + + // Used to assert we are only requested for as much data as is available. + size_t source_available_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_ diff --git a/pkg/apm/webrtc/common_audio/resampler/resampler.cc b/pkg/apm/webrtc/common_audio/resampler/resampler.cc new file mode 100644 index 00000000..0fdb2490 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/resampler.cc @@ -0,0 +1,923 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * A wrapper for resampling a numerous amount of sampling combinations. + */ + +#include "common_audio/resampler/include/resampler.h" + +#include +#include +#include + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +Resampler::Resampler() + : state1_(nullptr), + state2_(nullptr), + state3_(nullptr), + in_buffer_(nullptr), + out_buffer_(nullptr), + in_buffer_size_(0), + out_buffer_size_(0), + in_buffer_size_max_(0), + out_buffer_size_max_(0), + my_in_frequency_khz_(0), + my_out_frequency_khz_(0), + my_mode_(kResamplerMode1To1), + num_channels_(0), + helper_left_(nullptr), + helper_right_(nullptr) {} + +Resampler::Resampler(int inFreq, int outFreq, size_t num_channels) + : Resampler() { + Reset(inFreq, outFreq, num_channels); +} + +Resampler::~Resampler() { + if (state1_) { + free(state1_); + } + if (state2_) { + free(state2_); + } + if (state3_) { + free(state3_); + } + if (in_buffer_) { + free(in_buffer_); + } + if (out_buffer_) { + free(out_buffer_); + } + if (helper_left_) { + delete helper_left_; + } + if (helper_right_) { + delete helper_right_; + } +} + +int Resampler::ResetIfNeeded(int inFreq, int outFreq, size_t num_channels) { + int tmpInFreq_kHz = inFreq / 1000; + int tmpOutFreq_kHz = outFreq / 1000; + + if ((tmpInFreq_kHz != my_in_frequency_khz_) || + (tmpOutFreq_kHz != my_out_frequency_khz_) || + (num_channels != num_channels_)) { + return Reset(inFreq, outFreq, num_channels); + } else { + return 0; + } +} + +int Resampler::Reset(int inFreq, int outFreq, size_t num_channels) { + if (num_channels != 1 && num_channels != 2) { + RTC_LOG(LS_WARNING) + << "Reset() called with unsupported channel count, num_channels = " + << num_channels; + return -1; + } + ResamplerMode mode; + if (ComputeResamplerMode(inFreq, outFreq, &mode) != 0) { + RTC_LOG(LS_WARNING) + << "Reset() called with unsupported sample rates, inFreq = " << inFreq + << ", outFreq = " << outFreq; + return -1; + } + // Reinitialize internal state for the frequencies and sample rates. + num_channels_ = num_channels; + my_mode_ = mode; + + if (state1_) { + free(state1_); + state1_ = nullptr; + } + if (state2_) { + free(state2_); + state2_ = nullptr; + } + if (state3_) { + free(state3_); + state3_ = nullptr; + } + if (in_buffer_) { + free(in_buffer_); + in_buffer_ = nullptr; + } + if (out_buffer_) { + free(out_buffer_); + out_buffer_ = nullptr; + } + if (helper_left_) { + delete helper_left_; + helper_left_ = nullptr; + } + if (helper_right_) { + delete helper_right_; + helper_right_ = nullptr; + } + + in_buffer_size_ = 0; + out_buffer_size_ = 0; + in_buffer_size_max_ = 0; + out_buffer_size_max_ = 0; + + // We need to track what domain we're in. + my_in_frequency_khz_ = inFreq / 1000; + my_out_frequency_khz_ = outFreq / 1000; + + if (num_channels_ == 2) { + // Create two mono resamplers. + helper_left_ = new Resampler(inFreq, outFreq, 1); + helper_right_ = new Resampler(inFreq, outFreq, 1); + } + + // Now create the states we need. + switch (my_mode_) { + case kResamplerMode1To1: + // No state needed; + break; + case kResamplerMode1To2: + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode1To3: + state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); + WebRtcSpl_ResetResample16khzTo48khz( + static_cast(state1_)); + break; + case kResamplerMode1To4: + // 1:2 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + // 2:4 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode1To6: + // 1:2 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + // 2:6 + state2_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); + WebRtcSpl_ResetResample16khzTo48khz( + static_cast(state2_)); + break; + case kResamplerMode1To12: + // 1:2 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + // 2:4 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + // 4:12 + state3_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); + WebRtcSpl_ResetResample16khzTo48khz( + static_cast(state3_)); + break; + case kResamplerMode2To3: + // 2:6 + state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); + WebRtcSpl_ResetResample16khzTo48khz( + static_cast(state1_)); + // 6:3 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode2To11: + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + + state2_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz)); + WebRtcSpl_ResetResample8khzTo22khz( + static_cast(state2_)); + break; + case kResamplerMode4To11: + state1_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz)); + WebRtcSpl_ResetResample8khzTo22khz( + static_cast(state1_)); + break; + case kResamplerMode8To11: + state1_ = malloc(sizeof(WebRtcSpl_State16khzTo22khz)); + WebRtcSpl_ResetResample16khzTo22khz( + static_cast(state1_)); + break; + case kResamplerMode11To16: + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + + state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); + WebRtcSpl_ResetResample22khzTo16khz( + static_cast(state2_)); + break; + case kResamplerMode11To32: + // 11 -> 22 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + + // 22 -> 16 + state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); + WebRtcSpl_ResetResample22khzTo16khz( + static_cast(state2_)); + + // 16 -> 32 + state3_ = malloc(8 * sizeof(int32_t)); + memset(state3_, 0, 8 * sizeof(int32_t)); + + break; + case kResamplerMode2To1: + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode3To1: + state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); + WebRtcSpl_ResetResample48khzTo16khz( + static_cast(state1_)); + break; + case kResamplerMode4To1: + // 4:2 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + // 2:1 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode6To1: + // 6:2 + state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); + WebRtcSpl_ResetResample48khzTo16khz( + static_cast(state1_)); + // 2:1 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode12To1: + // 12:4 + state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); + WebRtcSpl_ResetResample48khzTo16khz( + static_cast(state1_)); + // 4:2 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + // 2:1 + state3_ = malloc(8 * sizeof(int32_t)); + memset(state3_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode3To2: + // 3:6 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + // 6:2 + state2_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); + WebRtcSpl_ResetResample48khzTo16khz( + static_cast(state2_)); + break; + case kResamplerMode11To2: + state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz)); + WebRtcSpl_ResetResample22khzTo8khz( + static_cast(state1_)); + + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + + break; + case kResamplerMode11To4: + state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz)); + WebRtcSpl_ResetResample22khzTo8khz( + static_cast(state1_)); + break; + case kResamplerMode11To8: + state1_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); + WebRtcSpl_ResetResample22khzTo16khz( + static_cast(state1_)); + break; + } + + return 0; +} + +int Resampler::ComputeResamplerMode(int in_freq_hz, + int out_freq_hz, + ResamplerMode* mode) { + // Start with a math exercise, Euclid's algorithm to find the gcd: + int a = in_freq_hz; + int b = out_freq_hz; + int c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + // b is now the gcd; + + // Scale with GCD + const int reduced_in_freq = in_freq_hz / b; + const int reduced_out_freq = out_freq_hz / b; + + if (reduced_in_freq == reduced_out_freq) { + *mode = kResamplerMode1To1; + } else if (reduced_in_freq == 1) { + switch (reduced_out_freq) { + case 2: + *mode = kResamplerMode1To2; + break; + case 3: + *mode = kResamplerMode1To3; + break; + case 4: + *mode = kResamplerMode1To4; + break; + case 6: + *mode = kResamplerMode1To6; + break; + case 12: + *mode = kResamplerMode1To12; + break; + default: + return -1; + } + } else if (reduced_out_freq == 1) { + switch (reduced_in_freq) { + case 2: + *mode = kResamplerMode2To1; + break; + case 3: + *mode = kResamplerMode3To1; + break; + case 4: + *mode = kResamplerMode4To1; + break; + case 6: + *mode = kResamplerMode6To1; + break; + case 12: + *mode = kResamplerMode12To1; + break; + default: + return -1; + } + } else if ((reduced_in_freq == 2) && (reduced_out_freq == 3)) { + *mode = kResamplerMode2To3; + } else if ((reduced_in_freq == 2) && (reduced_out_freq == 11)) { + *mode = kResamplerMode2To11; + } else if ((reduced_in_freq == 4) && (reduced_out_freq == 11)) { + *mode = kResamplerMode4To11; + } else if ((reduced_in_freq == 8) && (reduced_out_freq == 11)) { + *mode = kResamplerMode8To11; + } else if ((reduced_in_freq == 3) && (reduced_out_freq == 2)) { + *mode = kResamplerMode3To2; + } else if ((reduced_in_freq == 11) && (reduced_out_freq == 2)) { + *mode = kResamplerMode11To2; + } else if ((reduced_in_freq == 11) && (reduced_out_freq == 4)) { + *mode = kResamplerMode11To4; + } else if ((reduced_in_freq == 11) && (reduced_out_freq == 16)) { + *mode = kResamplerMode11To16; + } else if ((reduced_in_freq == 11) && (reduced_out_freq == 32)) { + *mode = kResamplerMode11To32; + } else if ((reduced_in_freq == 11) && (reduced_out_freq == 8)) { + *mode = kResamplerMode11To8; + } else { + return -1; + } + return 0; +} + +// Synchronous resampling, all output samples are written to samplesOut +int Resampler::Push(const int16_t* samplesIn, + size_t lengthIn, + int16_t* samplesOut, + size_t maxLen, + size_t& outLen) { + if (num_channels_ == 2) { + // Split up the signal and call the helper object for each channel + int16_t* left = + static_cast(malloc(lengthIn * sizeof(int16_t) / 2)); + int16_t* right = + static_cast(malloc(lengthIn * sizeof(int16_t) / 2)); + int16_t* out_left = + static_cast(malloc(maxLen / 2 * sizeof(int16_t))); + int16_t* out_right = + static_cast(malloc(maxLen / 2 * sizeof(int16_t))); + int res = 0; + for (size_t i = 0; i < lengthIn; i += 2) { + left[i >> 1] = samplesIn[i]; + right[i >> 1] = samplesIn[i + 1]; + } + + // It's OK to overwrite the local parameter, since it's just a copy + lengthIn = lengthIn / 2; + + size_t actualOutLen_left = 0; + size_t actualOutLen_right = 0; + // Do resampling for right channel + res |= helper_left_->Push(left, lengthIn, out_left, maxLen / 2, + actualOutLen_left); + res |= helper_right_->Push(right, lengthIn, out_right, maxLen / 2, + actualOutLen_right); + if (res || (actualOutLen_left != actualOutLen_right)) { + free(left); + free(right); + free(out_left); + free(out_right); + return -1; + } + + // Reassemble the signal + for (size_t i = 0; i < actualOutLen_left; i++) { + samplesOut[i * 2] = out_left[i]; + samplesOut[i * 2 + 1] = out_right[i]; + } + outLen = 2 * actualOutLen_left; + + free(left); + free(right); + free(out_left); + free(out_right); + + return 0; + } + + // Containers for temp samples + int16_t* tmp; + int16_t* tmp_2; + // tmp data for resampling routines + int32_t* tmp_mem; + + switch (my_mode_) { + case kResamplerMode1To1: + memcpy(samplesOut, samplesIn, lengthIn * sizeof(int16_t)); + outLen = lengthIn; + break; + case kResamplerMode1To2: + if (maxLen < (lengthIn * 2)) { + return -1; + } + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, + static_cast(state1_)); + outLen = lengthIn * 2; + return 0; + case kResamplerMode1To3: + + // We can only handle blocks of 160 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 160) != 0) { + return -1; + } + if (maxLen < (lengthIn * 3)) { + return -1; + } + tmp_mem = static_cast(malloc(336 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 160) { + WebRtcSpl_Resample16khzTo48khz( + samplesIn + i, samplesOut + i * 3, + static_cast(state1_), tmp_mem); + } + outLen = lengthIn * 3; + free(tmp_mem); + return 0; + case kResamplerMode1To4: + if (maxLen < (lengthIn * 4)) { + return -1; + } + + tmp = static_cast(malloc(sizeof(int16_t) * 2 * lengthIn)); + // 1:2 + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + // 2:4 + WebRtcSpl_UpsampleBy2(tmp, lengthIn * 2, samplesOut, + static_cast(state2_)); + outLen = lengthIn * 4; + free(tmp); + return 0; + case kResamplerMode1To6: + // We can only handle blocks of 80 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 80) != 0) { + return -1; + } + if (maxLen < (lengthIn * 6)) { + return -1; + } + + // 1:2 + + tmp_mem = static_cast(malloc(336 * sizeof(int32_t))); + tmp = static_cast(malloc(sizeof(int16_t) * 2 * lengthIn)); + + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + outLen = lengthIn * 2; + + for (size_t i = 0; i < outLen; i += 160) { + WebRtcSpl_Resample16khzTo48khz( + tmp + i, samplesOut + i * 3, + static_cast(state2_), tmp_mem); + } + outLen = outLen * 3; + free(tmp_mem); + free(tmp); + + return 0; + case kResamplerMode1To12: + // We can only handle blocks of 40 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 40) != 0) { + return -1; + } + if (maxLen < (lengthIn * 12)) { + return -1; + } + + tmp_mem = static_cast(malloc(336 * sizeof(int32_t))); + tmp = static_cast(malloc(sizeof(int16_t) * 4 * lengthIn)); + // 1:2 + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, + static_cast(state1_)); + outLen = lengthIn * 2; + // 2:4 + WebRtcSpl_UpsampleBy2(samplesOut, outLen, tmp, + static_cast(state2_)); + outLen = outLen * 2; + // 4:12 + for (size_t i = 0; i < outLen; i += 160) { + // WebRtcSpl_Resample16khzTo48khz() takes a block of 160 samples + // as input and outputs a resampled block of 480 samples. The + // data is now actually in 32 kHz sampling rate, despite the + // function name, and with a resampling factor of three becomes + // 96 kHz. + WebRtcSpl_Resample16khzTo48khz( + tmp + i, samplesOut + i * 3, + static_cast(state3_), tmp_mem); + } + outLen = outLen * 3; + free(tmp_mem); + free(tmp); + + return 0; + case kResamplerMode2To3: + if (maxLen < (lengthIn * 3 / 2)) { + return -1; + } + // 2:6 + // We can only handle blocks of 160 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 160) != 0) { + return -1; + } + tmp = static_cast(malloc(sizeof(int16_t) * lengthIn * 3)); + tmp_mem = static_cast(malloc(336 * sizeof(int32_t))); + for (size_t i = 0; i < lengthIn; i += 160) { + WebRtcSpl_Resample16khzTo48khz( + samplesIn + i, tmp + i * 3, + static_cast(state1_), tmp_mem); + } + lengthIn = lengthIn * 3; + // 6:3 + WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut, + static_cast(state2_)); + outLen = lengthIn / 2; + free(tmp); + free(tmp_mem); + return 0; + case kResamplerMode2To11: + + // We can only handle blocks of 80 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 80) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 11) / 2)) { + return -1; + } + tmp = static_cast(malloc(sizeof(int16_t) * 2 * lengthIn)); + // 1:2 + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + lengthIn *= 2; + + tmp_mem = static_cast(malloc(98 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 80) { + WebRtcSpl_Resample8khzTo22khz( + tmp + i, samplesOut + (i * 11) / 4, + static_cast(state2_), tmp_mem); + } + outLen = (lengthIn * 11) / 4; + free(tmp_mem); + free(tmp); + return 0; + case kResamplerMode4To11: + + // We can only handle blocks of 80 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 80) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 11) / 4)) { + return -1; + } + tmp_mem = static_cast(malloc(98 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 80) { + WebRtcSpl_Resample8khzTo22khz( + samplesIn + i, samplesOut + (i * 11) / 4, + static_cast(state1_), tmp_mem); + } + outLen = (lengthIn * 11) / 4; + free(tmp_mem); + return 0; + case kResamplerMode8To11: + // We can only handle blocks of 160 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 160) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 11) / 8)) { + return -1; + } + tmp_mem = static_cast(malloc(88 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 160) { + WebRtcSpl_Resample16khzTo22khz( + samplesIn + i, samplesOut + (i * 11) / 8, + static_cast(state1_), tmp_mem); + } + outLen = (lengthIn * 11) / 8; + free(tmp_mem); + return 0; + + case kResamplerMode11To16: + // We can only handle blocks of 110 samples + if ((lengthIn % 110) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 16) / 11)) { + return -1; + } + + tmp_mem = static_cast(malloc(104 * sizeof(int32_t))); + tmp = static_cast(malloc((sizeof(int16_t) * lengthIn * 2))); + + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + + for (size_t i = 0; i < (lengthIn * 2); i += 220) { + WebRtcSpl_Resample22khzTo16khz( + tmp + i, samplesOut + (i / 220) * 160, + static_cast(state2_), tmp_mem); + } + + outLen = (lengthIn * 16) / 11; + + free(tmp_mem); + free(tmp); + return 0; + + case kResamplerMode11To32: + + // We can only handle blocks of 110 samples + if ((lengthIn % 110) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 32) / 11)) { + return -1; + } + + tmp_mem = static_cast(malloc(104 * sizeof(int32_t))); + tmp = static_cast(malloc((sizeof(int16_t) * lengthIn * 2))); + + // 11 -> 22 kHz in samplesOut + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, + static_cast(state1_)); + + // 22 -> 16 in tmp + for (size_t i = 0; i < (lengthIn * 2); i += 220) { + WebRtcSpl_Resample22khzTo16khz( + samplesOut + i, tmp + (i / 220) * 160, + static_cast(state2_), tmp_mem); + } + + // 16 -> 32 in samplesOut + WebRtcSpl_UpsampleBy2(tmp, (lengthIn * 16) / 11, samplesOut, + static_cast(state3_)); + + outLen = (lengthIn * 32) / 11; + + free(tmp_mem); + free(tmp); + return 0; + + case kResamplerMode2To1: + if (maxLen < (lengthIn / 2)) { + return -1; + } + WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, samplesOut, + static_cast(state1_)); + outLen = lengthIn / 2; + return 0; + case kResamplerMode3To1: + // We can only handle blocks of 480 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 480) != 0) { + return -1; + } + if (maxLen < (lengthIn / 3)) { + return -1; + } + tmp_mem = static_cast(malloc(496 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 480) { + WebRtcSpl_Resample48khzTo16khz( + samplesIn + i, samplesOut + i / 3, + static_cast(state1_), tmp_mem); + } + outLen = lengthIn / 3; + free(tmp_mem); + return 0; + case kResamplerMode4To1: + if (maxLen < (lengthIn / 4)) { + return -1; + } + tmp = static_cast(malloc(sizeof(int16_t) * lengthIn / 2)); + // 4:2 + WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + // 2:1 + WebRtcSpl_DownsampleBy2(tmp, lengthIn / 2, samplesOut, + static_cast(state2_)); + outLen = lengthIn / 4; + free(tmp); + return 0; + + case kResamplerMode6To1: + // We can only handle blocks of 480 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 480) != 0) { + return -1; + } + if (maxLen < (lengthIn / 6)) { + return -1; + } + + tmp_mem = static_cast(malloc(496 * sizeof(int32_t))); + tmp = static_cast(malloc((sizeof(int16_t) * lengthIn) / 3)); + + for (size_t i = 0; i < lengthIn; i += 480) { + WebRtcSpl_Resample48khzTo16khz( + samplesIn + i, tmp + i / 3, + static_cast(state1_), tmp_mem); + } + outLen = lengthIn / 3; + free(tmp_mem); + WebRtcSpl_DownsampleBy2(tmp, outLen, samplesOut, + static_cast(state2_)); + free(tmp); + outLen = outLen / 2; + return 0; + case kResamplerMode12To1: + // We can only handle blocks of 480 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 480) != 0) { + return -1; + } + if (maxLen < (lengthIn / 12)) { + return -1; + } + + tmp_mem = static_cast(malloc(496 * sizeof(int32_t))); + tmp = static_cast(malloc((sizeof(int16_t) * lengthIn) / 3)); + tmp_2 = static_cast(malloc((sizeof(int16_t) * lengthIn) / 6)); + // 12:4 + for (size_t i = 0; i < lengthIn; i += 480) { + // WebRtcSpl_Resample48khzTo16khz() takes a block of 480 samples + // as input and outputs a resampled block of 160 samples. The + // data is now actually in 96 kHz sampling rate, despite the + // function name, and with a resampling factor of 1/3 becomes + // 32 kHz. + WebRtcSpl_Resample48khzTo16khz( + samplesIn + i, tmp + i / 3, + static_cast(state1_), tmp_mem); + } + outLen = lengthIn / 3; + free(tmp_mem); + // 4:2 + WebRtcSpl_DownsampleBy2(tmp, outLen, tmp_2, + static_cast(state2_)); + outLen = outLen / 2; + free(tmp); + // 2:1 + WebRtcSpl_DownsampleBy2(tmp_2, outLen, samplesOut, + static_cast(state3_)); + free(tmp_2); + outLen = outLen / 2; + return 0; + case kResamplerMode3To2: + if (maxLen < (lengthIn * 2 / 3)) { + return -1; + } + // 3:6 + tmp = static_cast(malloc(sizeof(int16_t) * lengthIn * 2)); + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + lengthIn *= 2; + // 6:2 + // We can only handle blocks of 480 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 480) != 0) { + free(tmp); + return -1; + } + tmp_mem = static_cast(malloc(496 * sizeof(int32_t))); + for (size_t i = 0; i < lengthIn; i += 480) { + WebRtcSpl_Resample48khzTo16khz( + tmp + i, samplesOut + i / 3, + static_cast(state2_), tmp_mem); + } + outLen = lengthIn / 3; + free(tmp); + free(tmp_mem); + return 0; + case kResamplerMode11To2: + // We can only handle blocks of 220 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 220) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 2) / 11)) { + return -1; + } + tmp_mem = static_cast(malloc(126 * sizeof(int32_t))); + tmp = + static_cast(malloc((lengthIn * 4) / 11 * sizeof(int16_t))); + + for (size_t i = 0; i < lengthIn; i += 220) { + WebRtcSpl_Resample22khzTo8khz( + samplesIn + i, tmp + (i * 4) / 11, + static_cast(state1_), tmp_mem); + } + lengthIn = (lengthIn * 4) / 11; + + WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut, + static_cast(state2_)); + outLen = lengthIn / 2; + + free(tmp_mem); + free(tmp); + return 0; + case kResamplerMode11To4: + // We can only handle blocks of 220 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 220) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 4) / 11)) { + return -1; + } + tmp_mem = static_cast(malloc(126 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 220) { + WebRtcSpl_Resample22khzTo8khz( + samplesIn + i, samplesOut + (i * 4) / 11, + static_cast(state1_), tmp_mem); + } + outLen = (lengthIn * 4) / 11; + free(tmp_mem); + return 0; + case kResamplerMode11To8: + // We can only handle blocks of 160 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 220) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 8) / 11)) { + return -1; + } + tmp_mem = static_cast(malloc(104 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 220) { + WebRtcSpl_Resample22khzTo16khz( + samplesIn + i, samplesOut + (i * 8) / 11, + static_cast(state1_), tmp_mem); + } + outLen = (lengthIn * 8) / 11; + free(tmp_mem); + return 0; + } + return 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/resampler/resampler.go b/pkg/apm/webrtc/common_audio/resampler/resampler.go new file mode 100644 index 00000000..86a19059 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/resampler.go @@ -0,0 +1,10 @@ +//go:build console + +package resampler + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/common_audio/resampler/sinc_resampler.cc b/pkg/apm/webrtc/common_audio/resampler/sinc_resampler.cc new file mode 100644 index 00000000..66a99b61 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/sinc_resampler.cc @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Modified from the Chromium original: +// src/media/base/sinc_resampler.cc + +// Initial input buffer layout, dividing into regions r0_ to r4_ (note: r0_, r3_ +// and r4_ will move after the first load): +// +// |----------------|-----------------------------------------|----------------| +// +// request_frames_ +// <---------------------------------------------------------> +// r0_ (during first load) +// +// kKernelSize / 2 kKernelSize / 2 kKernelSize / 2 kKernelSize / 2 +// <---------------> <---------------> <---------------> <---------------> +// r1_ r2_ r3_ r4_ +// +// block_size_ == r4_ - r2_ +// <---------------------------------------> +// +// request_frames_ +// <------------------ ... -----------------> +// r0_ (during second load) +// +// On the second request r0_ slides to the right by kKernelSize / 2 and r3_, r4_ +// and block_size_ are reinitialized via step (3) in the algorithm below. +// +// These new regions remain constant until a Flush() occurs. While complicated, +// this allows us to reduce jitter by always requesting the same amount from the +// provided callback. +// +// The algorithm: +// +// 1) Allocate input_buffer of size: request_frames_ + kKernelSize; this ensures +// there's enough room to read request_frames_ from the callback into region +// r0_ (which will move between the first and subsequent passes). +// +// 2) Let r1_, r2_ each represent half the kernel centered around r0_: +// +// r0_ = input_buffer_ + kKernelSize / 2 +// r1_ = input_buffer_ +// r2_ = r0_ +// +// r0_ is always request_frames_ in size. r1_, r2_ are kKernelSize / 2 in +// size. r1_ must be zero initialized to avoid convolution with garbage (see +// step (5) for why). +// +// 3) Let r3_, r4_ each represent half the kernel right aligned with the end of +// r0_ and choose block_size_ as the distance in frames between r4_ and r2_: +// +// r3_ = r0_ + request_frames_ - kKernelSize +// r4_ = r0_ + request_frames_ - kKernelSize / 2 +// block_size_ = r4_ - r2_ = request_frames_ - kKernelSize / 2 +// +// 4) Consume request_frames_ frames into r0_. +// +// 5) Position kernel centered at start of r2_ and generate output frames until +// the kernel is centered at the start of r4_ or we've finished generating +// all the output frames. +// +// 6) Wrap left over data from the r3_ to r1_ and r4_ to r2_. +// +// 7) If we're on the second load, in order to avoid overwriting the frames we +// just wrapped from r4_ we need to slide r0_ to the right by the size of +// r4_, which is kKernelSize / 2: +// +// r0_ = r0_ + kKernelSize / 2 = input_buffer_ + kKernelSize +// +// r3_, r4_, and block_size_ then need to be reinitialized, so goto (3). +// +// 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. + +// MSVC++ requires this to be set before any other includes to get M_PI. +#define _USE_MATH_DEFINES + +#include "common_audio/resampler/sinc_resampler.h" + +#include +#include +#include + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" // kSSE2, WebRtc_G... + +namespace webrtc { + +namespace { + +double SincScaleFactor(double io_ratio) { + // `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; + + // The sinc function is an idealized brick-wall filter, but since we're + // windowing it the transition from pass to stop does not happen right away. + // So we should adjust the low pass filter cutoff slightly downward to avoid + // some aliasing at the very high-end. + // TODO(crogers): this value is empirical and to be more exact should vary + // depending on kKernelSize. + sinc_scale_factor *= 0.9; + + return sinc_scale_factor; +} + +} // namespace + +const size_t SincResampler::kKernelSize; + +// If we know the minimum architecture at compile time, avoid CPU detection. +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/FMA3 supported. + if (GetCPUInfo(kAVX2) && GetCPUInfo(kFMA3)) + convolve_proc_ = Convolve_AVX2; + else if (GetCPUInfo(kSSE2)) + convolve_proc_ = Convolve_SSE; + else + convolve_proc_ = Convolve_C; +#else + // Unknown architecture. + convolve_proc_ = Convolve_C; +#endif +} + +SincResampler::SincResampler(double io_sample_rate_ratio, + size_t request_frames, + SincResamplerCallback* read_cb) + : io_sample_rate_ratio_(io_sample_rate_ratio), + read_cb_(read_cb), + request_frames_(request_frames), + input_buffer_size_(request_frames_ + kKernelSize), + // Create input buffers with a 32-byte alignment for SIMD optimizations. + kernel_storage_(static_cast( + AlignedMalloc(sizeof(float) * kKernelStorageSize, 32))), + kernel_pre_sinc_storage_(static_cast( + AlignedMalloc(sizeof(float) * kKernelStorageSize, 32))), + kernel_window_storage_(static_cast( + AlignedMalloc(sizeof(float) * kKernelStorageSize, 32))), + input_buffer_(static_cast( + AlignedMalloc(sizeof(float) * input_buffer_size_, 32))), + convolve_proc_(nullptr), + r1_(input_buffer_.get()), + r2_(input_buffer_.get() + kKernelSize / 2) { + InitializeCPUSpecificFeatures(); + RTC_DCHECK(convolve_proc_); + RTC_DCHECK_GT(request_frames_, 0); + Flush(); + RTC_DCHECK_GT(block_size_, kKernelSize); + + memset(kernel_storage_.get(), 0, + sizeof(*kernel_storage_.get()) * kKernelStorageSize); + memset(kernel_pre_sinc_storage_.get(), 0, + sizeof(*kernel_pre_sinc_storage_.get()) * kKernelStorageSize); + memset(kernel_window_storage_.get(), 0, + sizeof(*kernel_window_storage_.get()) * kKernelStorageSize); + + InitializeKernel(); +} + +SincResampler::~SincResampler() {} + +void SincResampler::UpdateRegions(bool second_load) { + // Setup various region pointers in the buffer (see diagram above). If we're + // on the second load we need to slide r0_ to the right by kKernelSize / 2. + r0_ = input_buffer_.get() + (second_load ? kKernelSize : kKernelSize / 2); + r3_ = r0_ + request_frames_ - kKernelSize; + r4_ = r0_ + request_frames_ - kKernelSize / 2; + block_size_ = r4_ - r2_; + + // r1_ at the beginning of the buffer. + RTC_DCHECK_EQ(r1_, input_buffer_.get()); + // r1_ left of r2_, r4_ left of r3_ and size correct. + RTC_DCHECK_EQ(r2_ - r1_, r4_ - r3_); + // r2_ left of r3. + RTC_DCHECK_LT(r2_, r3_); +} + +void SincResampler::InitializeKernel() { + // Blackman window parameters. + static const double kAlpha = 0.16; + static const double kA0 = 0.5 * (1.0 - kAlpha); + static const double kA1 = 0.5; + static const double kA2 = 0.5 * kAlpha; + + // Generates a set of windowed sinc() kernels. + // We generate a range of sub-sample offsets from 0.0 to 1.0. + const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_); + for (size_t offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) { + const float subsample_offset = + static_cast(offset_idx) / kKernelOffsetCount; + + for (size_t i = 0; i < kKernelSize; ++i) { + const size_t idx = i + offset_idx * kKernelSize; + const float pre_sinc = static_cast( + M_PI * (static_cast(i) - static_cast(kKernelSize / 2) - + subsample_offset)); + kernel_pre_sinc_storage_[idx] = pre_sinc; + + // Compute Blackman window, matching the offset of the sinc(). + const float x = (i - subsample_offset) / kKernelSize; + const float window = static_cast(kA0 - kA1 * cos(2.0 * M_PI * x) + + kA2 * cos(4.0 * M_PI * x)); + kernel_window_storage_[idx] = window; + + // Compute the sinc with offset, then window the sinc() function and store + // at the correct offset. + kernel_storage_[idx] = static_cast( + window * ((pre_sinc == 0) + ? sinc_scale_factor + : (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); + } + } +} + +void SincResampler::SetRatio(double io_sample_rate_ratio) { + if (fabs(io_sample_rate_ratio_ - io_sample_rate_ratio) < + std::numeric_limits::epsilon()) { + return; + } + + io_sample_rate_ratio_ = io_sample_rate_ratio; + + // Optimize reinitialization by reusing values which are independent of + // `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) { + const size_t idx = i + offset_idx * kKernelSize; + const float window = kernel_window_storage_[idx]; + const float pre_sinc = kernel_pre_sinc_storage_[idx]; + + kernel_storage_[idx] = static_cast( + window * ((pre_sinc == 0) + ? sinc_scale_factor + : (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); + } + } +} + +void SincResampler::Resample(size_t frames, float* destination) { + size_t remaining_frames = frames; + + // Step (1) -- Prime the input buffer at the start of the input stream. + if (!buffer_primed_ && remaining_frames) { + read_cb_->Run(request_frames_, r0_); + buffer_primed_ = true; + } + + // Step (2) -- Resample! const what we can outside of the loop for speed. It + // actually has an impact on ARM performance. See inner loop comment below. + 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. + // + // Note: The loop construct here can severely impact performance on ARM + // or when built with clang. See https://codereview.chromium.org/18566009/ + for (int i = static_cast( + ceil((block_size_ - virtual_source_idx_) / current_io_ratio)); + i > 0; --i) { + RTC_DCHECK_LT(virtual_source_idx_, block_size_); + + // `virtual_source_idx_` lies in between two kernel offsets so figure out + // what they are. + const int source_idx = static_cast(virtual_source_idx_); + const double subsample_remainder = virtual_source_idx_ - source_idx; + + const double virtual_offset_idx = + subsample_remainder * kKernelOffsetCount; + const int offset_idx = static_cast(virtual_offset_idx); + + // We'll compute "convolutions" for the two kernels which straddle + // `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 + // true so long as kKernelSize is a multiple of 32. + RTC_DCHECK_EQ(0, reinterpret_cast(k1) % 32); + RTC_DCHECK_EQ(0, reinterpret_cast(k2) % 32); + + // 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". + const double kernel_interpolation_factor = + virtual_offset_idx - offset_idx; + *destination++ = + convolve_proc_(input_ptr, k1, k2, kernel_interpolation_factor); + + // Advance the virtual index. + virtual_source_idx_ += current_io_ratio; + + if (!--remaining_frames) + return; + } + + // Wrap back around to the start. + virtual_source_idx_ -= block_size_; + + // Step (3) -- Copy r3_, r4_ to r1_, r2_. + // This wraps the last input frames back to the start of the buffer. + memcpy(r1_, r3_, sizeof(*input_buffer_.get()) * kKernelSize); + + // Step (4) -- Reinitialize regions if necessary. + if (r0_ == r2_) + UpdateRegions(true); + + // Step (5) -- Refresh the buffer with more input. + read_cb_->Run(request_frames_, r0_); + } +} + +#undef CONVOLVE_FUNC + +size_t SincResampler::ChunkSize() const { + return static_cast(block_size_ / io_sample_rate_ratio_); +} + +void SincResampler::Flush() { + virtual_source_idx_ = 0; + buffer_primed_ = false; + memset(input_buffer_.get(), 0, + sizeof(*input_buffer_.get()) * input_buffer_size_); + UpdateRegions(false); +} + +float SincResampler::Convolve_C(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor) { + float sum1 = 0; + float sum2 = 0; + + // Generate a single output sample. Unrolling this loop hurt performance in + // local testing. + size_t n = kKernelSize; + while (n--) { + sum1 += *input_ptr * *k1++; + sum2 += *input_ptr++ * *k2++; + } + + // Linearly interpolate the two "convolutions". + return static_cast((1.0 - kernel_interpolation_factor) * sum1 + + kernel_interpolation_factor * sum2); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/resampler/sinc_resampler.h b/pkg/apm/webrtc/common_audio/resampler/sinc_resampler.h new file mode 100644 index 00000000..c6a43abd --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/sinc_resampler.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Modified from the Chromium original here: +// src/media/base/sinc_resampler.h + +#ifndef COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_ +#define COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_ + +#include + +#include + +#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 +// are available to satisfy the request. +class SincResamplerCallback { + public: + virtual ~SincResamplerCallback() {} + virtual void Run(size_t frames, float* destination) = 0; +}; + +// SincResampler is a high-quality single-channel sample-rate converter. +class SincResampler { + public: + // The kernel size can be adjusted for quality (higher is better) at the + // expense of performance. Must be a multiple of 32. + // TODO(dalecurtis): Test performance to see if we can jack this up to 64+. + static const size_t kKernelSize = 32; + + // Default request size. Affects how often and for how much SincResampler + // calls back for input. Must be greater than kKernelSize. + static const size_t kDefaultRequestSize = 512; + + // The kernel offset count is used for interpolation and is the number of + // sub-sample kernel shifts. Can be adjusted for quality (higher is better) + // at the expense of allocating more memory. + static const size_t kKernelOffsetCount = 32; + 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 + // greater than kKernelSize. Specify kDefaultRequestSize if there are no + // request size constraints. + SincResampler(double io_sample_rate_ratio, + size_t request_frames, + SincResamplerCallback* read_cb); + virtual ~SincResampler(); + + 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. + size_t ChunkSize() const; + + size_t request_frames() const { return request_frames_; } + + // Flush all buffered data and reset internal indices. Not thread safe, do + // not call while Resample() is in progress. + void Flush(); + + // 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_`. + void SetRatio(double io_sample_rate_ratio); + + float* get_kernel_for_testing() { return kernel_storage_.get(); } + + private: + FRIEND_TEST_ALL_PREFIXES(SincResamplerTest, Convolve); + FRIEND_TEST_ALL_PREFIXES(SincResamplerTest, ConvolveBenchmark); + + void InitializeKernel(); + void UpdateRegions(bool second_load); + + // 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. + void InitializeCPUSpecificFeatures(); + + // 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, + const float* k2, + double kernel_interpolation_factor); +#if defined(WEBRTC_ARCH_X86_FAMILY) + static float Convolve_SSE(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor); + static float Convolve_AVX2(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor); +#elif defined(WEBRTC_HAS_NEON) + static float Convolve_NEON(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor); +#endif + + // The ratio of input / output sample rates. + double io_sample_rate_ratio_; + + // An index on the source input buffer with sub-sample precision. It must be + // double precision to avoid drift. + double virtual_source_idx_; + + // The buffer is primed once at the very beginning of processing. + bool buffer_primed_; + + // Source of data for resampling. + SincResamplerCallback* read_cb_; + + // The size (in samples) to request from each `read_cb_` execution. + const size_t request_frames_; + + // The number of source frames processed per pass. + size_t block_size_; + + // The size (in samples) of the internal buffer used by the resampler. + const size_t input_buffer_size_; + + // Contains kKernelOffsetCount kernels back-to-back, each of size kKernelSize. + // The kernel offsets are sub-sample shifts of a windowed sinc shifted from + // 0.0 to 1.0 sample. + std::unique_ptr kernel_storage_; + std::unique_ptr kernel_pre_sinc_storage_; + std::unique_ptr kernel_window_storage_; + + // Data from the source is copied into this buffer for each processing pass. + std::unique_ptr 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. + 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 + // the top of the .cc file for more information. + float* r0_; + float* const r1_; + float* const r2_; + float* r3_; + float* r4_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_ diff --git a/pkg/apm/webrtc/common_audio/resampler/sinc_resampler_neon_arm64.cc b/pkg/apm/webrtc/common_audio/resampler/sinc_resampler_neon_arm64.cc new file mode 100644 index 00000000..9ee918bc --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/sinc_resampler_neon_arm64.cc @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Modified from the Chromium original: +// src/media/base/sinc_resampler.cc + +#include + +#include "common_audio/resampler/sinc_resampler.h" + +namespace webrtc { + +float SincResampler::Convolve_NEON(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor) { + float32x4_t m_input; + float32x4_t m_sums1 = vmovq_n_f32(0); + float32x4_t m_sums2 = vmovq_n_f32(0); + + const float* upper = input_ptr + kKernelSize; + for (; input_ptr < upper;) { + m_input = vld1q_f32(input_ptr); + input_ptr += 4; + m_sums1 = vmlaq_f32(m_sums1, m_input, vld1q_f32(k1)); + k1 += 4; + m_sums2 = vmlaq_f32(m_sums2, m_input, vld1q_f32(k2)); + k2 += 4; + } + + // Linearly interpolate the two "convolutions". + m_sums1 = vmlaq_f32( + vmulq_f32(m_sums1, vmovq_n_f32(1.0 - kernel_interpolation_factor)), + m_sums2, vmovq_n_f32(kernel_interpolation_factor)); + + // Sum components together. + float32x2_t m_half = vadd_f32(vget_high_f32(m_sums1), vget_low_f32(m_sums1)); + return vget_lane_f32(vpadd_f32(m_half, m_half), 0); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/resampler/sinc_resampler_sse_amd64.cc b/pkg/apm/webrtc/common_audio/resampler/sinc_resampler_sse_amd64.cc new file mode 100644 index 00000000..30a8d1b2 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/sinc_resampler_sse_amd64.cc @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Modified from the Chromium original: +// src/media/base/simd/sinc_resampler_sse.cc + +#include +#include +#include + +#include "common_audio/resampler/sinc_resampler.h" + +namespace webrtc { + +float SincResampler::Convolve_SSE(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor) { + __m128 m_input; + __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 + // these loops hurt performance in local testing. + if (reinterpret_cast(input_ptr) & 0x0F) { + for (size_t i = 0; i < kKernelSize; i += 4) { + m_input = _mm_loadu_ps(input_ptr + i); + m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i))); + m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i))); + } + } else { + for (size_t i = 0; i < kKernelSize; i += 4) { + m_input = _mm_load_ps(input_ptr + i); + m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i))); + m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i))); + } + } + + // Linearly interpolate the two "convolutions". + m_sums1 = _mm_mul_ps( + m_sums1, + _mm_set_ps1(static_cast(1.0 - kernel_interpolation_factor))); + m_sums2 = _mm_mul_ps( + m_sums2, _mm_set_ps1(static_cast(kernel_interpolation_factor))); + m_sums1 = _mm_add_ps(m_sums1, m_sums2); + + // Sum components together. + float result; + m_sums2 = _mm_add_ps(_mm_movehl_ps(m_sums1, m_sums1), m_sums1); + _mm_store_ss(&result, + _mm_add_ss(m_sums2, _mm_shuffle_ps(m_sums2, m_sums2, 1))); + + return result; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc b/pkg/apm/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc new file mode 100644 index 00000000..2afdd1be --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// MSVC++ requires this to be set before any other includes to get M_PI. +#define _USE_MATH_DEFINES + +#include "common_audio/resampler/sinusoidal_linear_chirp_source.h" + +#include + +namespace webrtc { + +SinusoidalLinearChirpSource::SinusoidalLinearChirpSource(int sample_rate, + size_t samples, + double max_frequency, + double delay_samples) + : sample_rate_(sample_rate), + total_samples_(samples), + max_frequency_(max_frequency), + current_index_(0), + delay_samples_(delay_samples) { + // Chirp rate. + double duration = static_cast(total_samples_) / sample_rate_; + k_ = (max_frequency_ - kMinFrequency) / duration; +} + +void SinusoidalLinearChirpSource::Run(size_t frames, float* destination) { + for (size_t i = 0; i < frames; ++i, ++current_index_) { + // Filter out frequencies higher than Nyquist. + if (Frequency(current_index_) > 0.5 * sample_rate_) { + destination[i] = 0; + } else { + // Calculate time in seconds. + if (current_index_ < delay_samples_) { + destination[i] = 0; + } else { + // Sinusoidal linear chirp. + double t = (current_index_ - delay_samples_) / sample_rate_; + destination[i] = sin(2 * M_PI * (kMinFrequency * t + (k_ / 2) * t * t)); + } + } + } +} + +double SinusoidalLinearChirpSource::Frequency(size_t position) { + return kMinFrequency + (position - delay_samples_) * + (max_frequency_ - kMinFrequency) / total_samples_; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h b/pkg/apm/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h new file mode 100644 index 00000000..ccd11bbd --- /dev/null +++ b/pkg/apm/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Modified from the Chromium original here: +// src/media/base/sinc_resampler_unittest.cc + +#ifndef COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ +#define COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ + +#include "common_audio/resampler/sinc_resampler.h" + +namespace webrtc { + +// Fake audio source for testing the resampler. Generates a sinusoidal linear +// chirp (http://en.wikipedia.org/wiki/Chirp) which can be tuned to stress the +// 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 + // source. It will produce zeros until non-negative time is reached. + SinusoidalLinearChirpSource(int sample_rate, + size_t samples, + double max_frequency, + double delay_samples); + + ~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: + static constexpr int kMinFrequency = 5; + + int sample_rate_; + size_t total_samples_; + double max_frequency_; + double k_; + size_t current_index_; + double delay_samples_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ diff --git a/pkg/apm/webrtc/common_audio/ring_buffer.c b/pkg/apm/webrtc/common_audio/ring_buffer.c new file mode 100644 index 00000000..a3fabd05 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/ring_buffer.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// A ring buffer to hold arbitrary data. Provides no thread safety. Unless +// otherwise specified, functions return 0 on success and -1 on error. + +#include "common_audio/ring_buffer.h" + +#include // size_t +#include +#include + +// 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 +// smaller. +static size_t GetBufferReadRegions(RingBuffer* buf, + size_t element_count, + void** data_ptr_1, + size_t* data_ptr_bytes_1, + void** data_ptr_2, + size_t* data_ptr_bytes_2) { + const size_t readable_elements = WebRtc_available_read(buf); + const size_t read_elements = + (readable_elements < element_count ? readable_elements : element_count); + const size_t margin = buf->element_count - buf->read_pos; + + // Check to see if read is not contiguous. + if (read_elements > margin) { + // Write data in two blocks that wrap the buffer. + *data_ptr_1 = buf->data + buf->read_pos * buf->element_size; + *data_ptr_bytes_1 = margin * buf->element_size; + *data_ptr_2 = buf->data; + *data_ptr_bytes_2 = (read_elements - margin) * buf->element_size; + } else { + *data_ptr_1 = buf->data + buf->read_pos * buf->element_size; + *data_ptr_bytes_1 = read_elements * buf->element_size; + *data_ptr_2 = NULL; + *data_ptr_bytes_2 = 0; + } + + return read_elements; +} + +RingBuffer* WebRtc_CreateBuffer(size_t element_count, size_t element_size) { + RingBuffer* self = NULL; + if (element_count == 0 || element_size == 0) { + return NULL; + } + + self = malloc(sizeof(RingBuffer)); + if (!self) { + return NULL; + } + + self->data = malloc(element_count * element_size); + if (!self->data) { + free(self); + self = NULL; + return NULL; + } + + self->element_count = element_count; + self->element_size = element_size; + WebRtc_InitBuffer(self); + + return self; +} + +void WebRtc_InitBuffer(RingBuffer* self) { + self->read_pos = 0; + self->write_pos = 0; + self->rw_wrap = SAME_WRAP; + + // Initialize buffer to zeros + memset(self->data, 0, self->element_count * self->element_size); +} + +void WebRtc_FreeBuffer(void* handle) { + RingBuffer* self = (RingBuffer*)handle; + if (!self) { + return; + } + + free(self->data); + free(self); +} + +size_t WebRtc_ReadBuffer(RingBuffer* self, + void** data_ptr, + void* data, + size_t element_count) { + if (self == NULL) { + return 0; + } + if (data == NULL) { + return 0; + } + + { + void* buf_ptr_1 = NULL; + void* buf_ptr_2 = NULL; + size_t buf_ptr_bytes_1 = 0; + size_t buf_ptr_bytes_2 = 0; + const size_t read_count = + GetBufferReadRegions(self, element_count, &buf_ptr_1, &buf_ptr_bytes_1, + &buf_ptr_2, &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. + 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; + } else if (!data_ptr) { + // No wrap, but a memcpy was requested. + memcpy(data, buf_ptr_1, buf_ptr_bytes_1); + } + if (data_ptr) { + // `buf_ptr_1` == `data` in the case of a wrap. + *data_ptr = read_count == 0 ? NULL : buf_ptr_1; + } + + // Update read position + WebRtc_MoveReadPtr(self, (int)read_count); + + return read_count; + } +} + +size_t WebRtc_WriteBuffer(RingBuffer* self, + const void* data, + size_t element_count) { + if (!self) { + return 0; + } + if (!data) { + return 0; + } + + { + const size_t free_elements = WebRtc_available_write(self); + const size_t write_elements = + (free_elements < element_count ? free_elements : element_count); + size_t n = write_elements; + const size_t margin = self->element_count - self->write_pos; + + if (write_elements > margin) { + // Buffer wrap around when writing. + memcpy(self->data + self->write_pos * self->element_size, data, + margin * self->element_size); + self->write_pos = 0; + n -= margin; + self->rw_wrap = DIFF_WRAP; + } + memcpy(self->data + self->write_pos * self->element_size, + ((const char*)data) + ((write_elements - n) * self->element_size), + n * self->element_size); + self->write_pos += n; + + return write_elements; + } +} + +int WebRtc_MoveReadPtr(RingBuffer* self, int element_count) { + if (!self) { + return 0; + } + + { + // We need to be able to take care of negative changes, hence use "int" + // instead of "size_t". + const int free_elements = (int)WebRtc_available_write(self); + const int readable_elements = (int)WebRtc_available_read(self); + int read_pos = (int)self->read_pos; + + if (element_count > readable_elements) { + element_count = readable_elements; + } + if (element_count < -free_elements) { + element_count = -free_elements; + } + + read_pos += element_count; + if (read_pos > (int)self->element_count) { + // Buffer wrap around. Restart read position and wrap indicator. + read_pos -= (int)self->element_count; + self->rw_wrap = SAME_WRAP; + } + if (read_pos < 0) { + // Buffer wrap around. Restart read position and wrap indicator. + read_pos += (int)self->element_count; + self->rw_wrap = DIFF_WRAP; + } + + self->read_pos = (size_t)read_pos; + + return element_count; + } +} + +size_t WebRtc_available_read(const RingBuffer* self) { + if (!self) { + return 0; + } + + if (self->rw_wrap == SAME_WRAP) { + return self->write_pos - self->read_pos; + } else { + return self->element_count - self->read_pos + self->write_pos; + } +} + +size_t WebRtc_available_write(const RingBuffer* self) { + if (!self) { + return 0; + } + + return self->element_count - WebRtc_available_read(self); +} diff --git a/pkg/apm/webrtc/common_audio/ring_buffer.h b/pkg/apm/webrtc/common_audio/ring_buffer.h new file mode 100644 index 00000000..99f9547f --- /dev/null +++ b/pkg/apm/webrtc/common_audio/ring_buffer.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// A ring buffer to hold arbitrary data. Provides no thread safety. Unless +// otherwise specified, functions return 0 on success and -1 on error. + +#ifndef COMMON_AUDIO_RING_BUFFER_H_ +#define COMMON_AUDIO_RING_BUFFER_H_ + +// TODO(https://issues.webrtc.org/379542219): Remove when AECm gets removed. + +#ifdef __cplusplus +extern "C" { +#endif + +#include // size_t + +enum Wrap { SAME_WRAP, DIFF_WRAP }; + +typedef struct RingBuffer { + size_t read_pos; + size_t write_pos; + size_t element_count; + size_t element_size; + enum Wrap rw_wrap; + char* data; +} RingBuffer; + +// Creates and initializes the buffer. Returns null on failure. +RingBuffer* WebRtc_CreateBuffer(size_t element_count, size_t element_size); +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 +// only guaranteed to be valid until the next call to WebRtc_WriteBuffer(). +// +// To force a copying to `data`, pass a null `data_ptr`. +// +// Returns number of elements read. +size_t WebRtc_ReadBuffer(RingBuffer* handle, + void** data_ptr, + void* data, + size_t element_count); + +// 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 +// 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); + +// Returns number of available elements to read. +size_t WebRtc_available_read(const RingBuffer* handle); + +// Returns number of available elements for write. +size_t WebRtc_available_write(const RingBuffer* handle); + +#ifdef __cplusplus +} +#endif + +#endif // COMMON_AUDIO_RING_BUFFER_H_ diff --git a/pkg/apm/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c b/pkg/apm/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c new file mode 100644 index 00000000..d5b0bd27 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_AutoCorrToReflCoef(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +void WebRtcSpl_AutoCorrToReflCoef(const int32_t* R, int use_order, int16_t* K) { + int i, n; + int16_t tmp; + const int32_t* rptr; + int32_t L_num, L_den; + int16_t *acfptr, *pptr, *wptr, *p1ptr, *w1ptr, ACF[WEBRTC_SPL_MAX_LPC_ORDER], + P[WEBRTC_SPL_MAX_LPC_ORDER], W[WEBRTC_SPL_MAX_LPC_ORDER]; + + // Initialize loop and pointers. + acfptr = ACF; + rptr = R; + pptr = P; + p1ptr = &P[1]; + w1ptr = &W[1]; + wptr = w1ptr; + + // First loop; n=0. Determine shifting. + tmp = WebRtcSpl_NormW32(*R); + *acfptr = (int16_t)((*rptr++ << tmp) >> 16); + *pptr++ = *acfptr++; + + // Initialize ACF, P and W. + for (i = 1; i <= use_order; i++) { + *acfptr = (int16_t)((*rptr++ << tmp) >> 16); + *wptr++ = *acfptr; + *pptr++ = *acfptr++; + } + + // Compute reflection coefficients. + for (n = 1; n <= use_order; n++, K++) { + tmp = WEBRTC_SPL_ABS_W16(*p1ptr); + if (*P < tmp) { + for (i = n; i <= use_order; i++) + *K++ = 0; + + return; + } + + // Division: WebRtcSpl_div(tmp, *P) + *K = 0; + if (tmp != 0) { + L_num = tmp; + L_den = *P; + i = 15; + while (i--) { + (*K) <<= 1; + L_num <<= 1; + if (L_num >= L_den) { + L_num -= L_den; + (*K)++; + } + } + if (*p1ptr > 0) + *K = -*K; + } + + // Last iteration; don't do Schur recursion. + if (n == use_order) + return; + + // Schur recursion. + pptr = P; + wptr = w1ptr; + tmp = (int16_t)(((int32_t)*p1ptr * (int32_t)*K + 16384) >> 15); + *pptr = WebRtcSpl_AddSatW16(*pptr, tmp); + pptr++; + for (i = 1; i <= use_order - n; i++) { + tmp = (int16_t)(((int32_t)*wptr * (int32_t)*K + 16384) >> 15); + *pptr = WebRtcSpl_AddSatW16(*(pptr + 1), tmp); + pptr++; + tmp = (int16_t)(((int32_t)*pptr * (int32_t)*K + 16384) >> 15); + *wptr = WebRtcSpl_AddSatW16(*wptr, tmp); + wptr++; + } + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/auto_correlation.c b/pkg/apm/webrtc/common_audio/signal_processing/auto_correlation.c new file mode 100644 index 00000000..d9e74e3f --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/auto_correlation.c @@ -0,0 +1,64 @@ +/* + * 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 "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" + +size_t WebRtcSpl_AutoCorrelation(const int16_t* in_vector, + size_t in_vector_length, + size_t order, + int32_t* result, + int* scale) { + int32_t sum = 0; + size_t i = 0, j = 0; + int16_t smax = 0; + int scaling = 0; + + RTC_DCHECK_LE(order, in_vector_length); + + // Find the maximum absolute value of the samples. + smax = WebRtcSpl_MaxAbsValueW16(in_vector, in_vector_length); + + // In order to avoid overflow when computing the sum we should scale the + // samples so that (in_vector_length * smax * smax) will not overflow. + if (smax == 0) { + scaling = 0; + } else { + // Number of bits in the sum loop. + int nbits = WebRtcSpl_GetSizeInBits((uint32_t)in_vector_length); + // Number of bits to normalize smax. + int t = WebRtcSpl_NormW32(WEBRTC_SPL_MUL(smax, smax)); + + if (t > nbits) { + scaling = 0; + } else { + scaling = nbits - t; + } + } + + // Perform the actual correlation calculation. + for (i = 0; i < order + 1; i++) { + sum = 0; + /* Unroll the loop to improve performance. */ + for (j = 0; i + j + 3 < in_vector_length; j += 4) { + sum += (in_vector[j + 0] * in_vector[i + j + 0]) >> scaling; + sum += (in_vector[j + 1] * in_vector[i + j + 1]) >> scaling; + sum += (in_vector[j + 2] * in_vector[i + j + 2]) >> scaling; + sum += (in_vector[j + 3] * in_vector[i + j + 3]) >> scaling; + } + for (; j < in_vector_length - i; j++) { + sum += (in_vector[j] * in_vector[i + j]) >> scaling; + } + *result++ = sum; + } + + *scale = scaling; + return order + 1; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/complex_bit_reverse.c b/pkg/apm/webrtc/common_audio/signal_processing/complex_bit_reverse.c new file mode 100644 index 00000000..c26e232e --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/complex_bit_reverse.c @@ -0,0 +1,106 @@ +/* + * 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 "common_audio/signal_processing/include/signal_processing_library.h" + +/* Tables for data buffer indexes that are bit reversed and thus need to be + * swapped. Note that, index_7[{0, 2, 4, ...}] are for the left side of the swap + * operations, while index_7[{1, 3, 5, ...}] are for the right side of the + * operation. Same for index_8. + */ + +/* Indexes for the case of stages == 7. */ +static const int16_t index_7[112] = { + 1, 64, 2, 32, 3, 96, 4, 16, 5, 80, 6, 48, 7, 112, 9, 72, + 10, 40, 11, 104, 12, 24, 13, 88, 14, 56, 15, 120, 17, 68, 18, 36, + 19, 100, 21, 84, 22, 52, 23, 116, 25, 76, 26, 44, 27, 108, 29, 92, + 30, 60, 31, 124, 33, 66, 35, 98, 37, 82, 38, 50, 39, 114, 41, 74, + 43, 106, 45, 90, 46, 58, 47, 122, 49, 70, 51, 102, 53, 86, 55, 118, + 57, 78, 59, 110, 61, 94, 63, 126, 67, 97, 69, 81, 71, 113, 75, 105, + 77, 89, 79, 121, 83, 101, 87, 117, 91, 109, 95, 125, 103, 115, 111, 123}; + +/* Indexes for the case of stages == 8. */ +static const int16_t index_8[240] = { + 1, 128, 2, 64, 3, 192, 4, 32, 5, 160, 6, 96, 7, 224, 8, + 16, 9, 144, 10, 80, 11, 208, 12, 48, 13, 176, 14, 112, 15, 240, + 17, 136, 18, 72, 19, 200, 20, 40, 21, 168, 22, 104, 23, 232, 25, + 152, 26, 88, 27, 216, 28, 56, 29, 184, 30, 120, 31, 248, 33, 132, + 34, 68, 35, 196, 37, 164, 38, 100, 39, 228, 41, 148, 42, 84, 43, + 212, 44, 52, 45, 180, 46, 116, 47, 244, 49, 140, 50, 76, 51, 204, + 53, 172, 54, 108, 55, 236, 57, 156, 58, 92, 59, 220, 61, 188, 62, + 124, 63, 252, 65, 130, 67, 194, 69, 162, 70, 98, 71, 226, 73, 146, + 74, 82, 75, 210, 77, 178, 78, 114, 79, 242, 81, 138, 83, 202, 85, + 170, 86, 106, 87, 234, 89, 154, 91, 218, 93, 186, 94, 122, 95, 250, + 97, 134, 99, 198, 101, 166, 103, 230, 105, 150, 107, 214, 109, 182, 110, + 118, 111, 246, 113, 142, 115, 206, 117, 174, 119, 238, 121, 158, 123, 222, + 125, 190, 127, 254, 131, 193, 133, 161, 135, 225, 137, 145, 139, 209, 141, + 177, 143, 241, 147, 201, 149, 169, 151, 233, 155, 217, 157, 185, 159, 249, + 163, 197, 167, 229, 171, 213, 173, 181, 175, 245, 179, 205, 183, 237, 187, + 221, 191, 253, 199, 227, 203, 211, 207, 243, 215, 235, 223, 251, 239, 247}; + +void WebRtcSpl_ComplexBitReverse(int16_t* __restrict complex_data, int stages) { + /* For any specific value of stages, we know exactly the indexes that are + * bit reversed. Currently (Feb. 2012) in WebRTC the only possible values of + * stages are 7 and 8, so we use tables to save unnecessary iterations and + * calculations for these two cases. + */ + if (stages == 7 || stages == 8) { + int m = 0; + int length = 112; + const int16_t* index = index_7; + + if (stages == 8) { + length = 240; + index = index_8; + } + + /* Decimation in time. Swap the elements with bit-reversed indexes. */ + for (m = 0; m < length; m += 2) { + /* We declare a int32_t* type pointer, to load both the 16-bit real + * and imaginary elements from complex_data in one instruction, reducing + * complexity. + */ + int32_t* complex_data_ptr = (int32_t*)complex_data; + int32_t temp = 0; + + temp = complex_data_ptr[index[m]]; /* Real and imaginary */ + complex_data_ptr[index[m]] = complex_data_ptr[index[m + 1]]; + complex_data_ptr[index[m + 1]] = temp; + } + } else { + int m = 0, mr = 0, l = 0; + int n = 1 << stages; + int nn = n - 1; + + /* Decimation in time - re-order data */ + for (m = 1; m <= nn; ++m) { + int32_t* complex_data_ptr = (int32_t*)complex_data; + int32_t temp = 0; + + /* Find out indexes that are bit-reversed. */ + l = n; + do { + l >>= 1; + } while (l > nn - mr); + mr = (mr & (l - 1)) + l; + + if (mr <= m) { + continue; + } + + /* Swap the elements with bit-reversed indexes. + * This is similar to the loop in the stages == 7 or 8 cases. + */ + temp = complex_data_ptr[m]; /* Real and imaginary */ + complex_data_ptr[m] = complex_data_ptr[mr]; + complex_data_ptr[mr] = temp; + } + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/complex_fft.c b/pkg/apm/webrtc/common_audio/signal_processing/complex_fft.c new file mode 100644 index 00000000..d9d970d3 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/complex_fft.c @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_ComplexFFT(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/complex_fft_tables.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/system/arch.h" + +#define CFFTSFT 14 +#define CFFTRND 1 +#define CFFTRND2 16384 + +#define CIFFTSFT 14 +#define CIFFTRND 1 + +int WebRtcSpl_ComplexFFT(int16_t frfi[], int stages, int mode) { + int i, j, l, k, istep, n, m; + int16_t wr, wi; + int32_t tr32, ti32, qr32, qi32; + + /* The 1024-value is a constant given from the size of kSinTable1024[], + * and should not be changed depending on the input parameter 'stages' + */ + n = 1 << stages; + if (n > 1024) + return -1; + + l = 1; + k = 10 - 1; /* Constant for given kSinTable1024[]. Do not change + depending on the input parameter 'stages' */ + + if (mode == 0) { + // mode==0: Low-complexity and Low-accuracy mode + while (l < n) { + istep = l << 1; + + for (m = 0; m < l; ++m) { + j = m << k; + + /* The 256-value is a constant given as 1/4 of the size of + * kSinTable1024[], and should not be changed depending on the input + * parameter 'stages'. It will result in 0 <= j < N_SINE_WAVE/2 + */ + wr = kSinTable1024[j + 256]; + wi = -kSinTable1024[j]; + + for (i = m; i < n; i += istep) { + j = i + l; + + tr32 = (wr * frfi[2 * j] - wi * frfi[2 * j + 1]) >> 15; + + ti32 = (wr * frfi[2 * j + 1] + wi * frfi[2 * j]) >> 15; + + qr32 = (int32_t)frfi[2 * i]; + qi32 = (int32_t)frfi[2 * i + 1]; + frfi[2 * j] = (int16_t)((qr32 - tr32) >> 1); + frfi[2 * j + 1] = (int16_t)((qi32 - ti32) >> 1); + frfi[2 * i] = (int16_t)((qr32 + tr32) >> 1); + frfi[2 * i + 1] = (int16_t)((qi32 + ti32) >> 1); + } + } + + --k; + l = istep; + } + + } else { + // mode==1: High-complexity and High-accuracy mode + while (l < n) { + istep = l << 1; + + for (m = 0; m < l; ++m) { + j = m << k; + + /* The 256-value is a constant given as 1/4 of the size of + * kSinTable1024[], and should not be changed depending on the input + * parameter 'stages'. It will result in 0 <= j < N_SINE_WAVE/2 + */ + wr = kSinTable1024[j + 256]; + wi = -kSinTable1024[j]; + +#ifdef WEBRTC_ARCH_ARM_V7 + int32_t wri = 0; + __asm __volatile("pkhbt %0, %1, %2, lsl #16" + : "=r"(wri) + : "r"((int32_t)wr), "r"((int32_t)wi)); +#endif + + for (i = m; i < n; i += istep) { + j = i + l; + +#ifdef WEBRTC_ARCH_ARM_V7 + register int32_t frfi_r; + __asm __volatile( + "pkhbt %[frfi_r], %[frfi_even], %[frfi_odd]," + " lsl #16\n\t" + "smlsd %[tr32], %[wri], %[frfi_r], %[cfftrnd]\n\t" + "smladx %[ti32], %[wri], %[frfi_r], %[cfftrnd]\n\t" + : [frfi_r] "=&r"(frfi_r), [tr32] "=&r"(tr32), [ti32] "=r"(ti32) + : [frfi_even] "r"((int32_t)frfi[2 * j]), + [frfi_odd] "r"((int32_t)frfi[2 * j + 1]), [wri] "r"(wri), + [cfftrnd] "r"(CFFTRND)); +#else + tr32 = wr * frfi[2 * j] - wi * frfi[2 * j + 1] + CFFTRND; + + ti32 = wr * frfi[2 * j + 1] + wi * frfi[2 * j] + CFFTRND; +#endif + + tr32 >>= 15 - CFFTSFT; + ti32 >>= 15 - CFFTSFT; + + qr32 = ((int32_t)frfi[2 * i]) * (1 << CFFTSFT); + qi32 = ((int32_t)frfi[2 * i + 1]) * (1 << CFFTSFT); + + frfi[2 * j] = (int16_t)((qr32 - tr32 + CFFTRND2) >> (1 + CFFTSFT)); + frfi[2 * j + 1] = + (int16_t)((qi32 - ti32 + CFFTRND2) >> (1 + CFFTSFT)); + frfi[2 * i] = (int16_t)((qr32 + tr32 + CFFTRND2) >> (1 + CFFTSFT)); + frfi[2 * i + 1] = + (int16_t)((qi32 + ti32 + CFFTRND2) >> (1 + CFFTSFT)); + } + } + + --k; + l = istep; + } + } + return 0; +} + +int WebRtcSpl_ComplexIFFT(int16_t frfi[], int stages, int mode) { + size_t i, j, l, istep, n, m; + int k, scale, shift; + int16_t wr, wi; + int32_t tr32, ti32, qr32, qi32; + int32_t tmp32, round2; + + /* The 1024-value is a constant given from the size of kSinTable1024[], + * and should not be changed depending on the input parameter 'stages' + */ + n = ((size_t)1) << stages; + if (n > 1024) + return -1; + + scale = 0; + + l = 1; + k = 10 - 1; /* Constant for given kSinTable1024[]. Do not change + depending on the input parameter 'stages' */ + + while (l < n) { + // variable scaling, depending upon data + shift = 0; + round2 = 8192; + + tmp32 = WebRtcSpl_MaxAbsValueW16(frfi, 2 * n); + if (tmp32 > 13573) { + shift++; + scale++; + round2 <<= 1; + } + if (tmp32 > 27146) { + shift++; + scale++; + round2 <<= 1; + } + + istep = l << 1; + + if (mode == 0) { + // mode==0: Low-complexity and Low-accuracy mode + for (m = 0; m < l; ++m) { + j = m << k; + + /* The 256-value is a constant given as 1/4 of the size of + * kSinTable1024[], and should not be changed depending on the input + * parameter 'stages'. It will result in 0 <= j < N_SINE_WAVE/2 + */ + wr = kSinTable1024[j + 256]; + wi = kSinTable1024[j]; + + for (i = m; i < n; i += istep) { + j = i + l; + + tr32 = (wr * frfi[2 * j] - wi * frfi[2 * j + 1]) >> 15; + + ti32 = (wr * frfi[2 * j + 1] + wi * frfi[2 * j]) >> 15; + + qr32 = (int32_t)frfi[2 * i]; + qi32 = (int32_t)frfi[2 * i + 1]; + frfi[2 * j] = (int16_t)((qr32 - tr32) >> shift); + frfi[2 * j + 1] = (int16_t)((qi32 - ti32) >> shift); + frfi[2 * i] = (int16_t)((qr32 + tr32) >> shift); + frfi[2 * i + 1] = (int16_t)((qi32 + ti32) >> shift); + } + } + } else { + // mode==1: High-complexity and High-accuracy mode + + for (m = 0; m < l; ++m) { + j = m << k; + + /* The 256-value is a constant given as 1/4 of the size of + * kSinTable1024[], and should not be changed depending on the input + * parameter 'stages'. It will result in 0 <= j < N_SINE_WAVE/2 + */ + wr = kSinTable1024[j + 256]; + wi = kSinTable1024[j]; + +#ifdef WEBRTC_ARCH_ARM_V7 + int32_t wri = 0; + __asm __volatile("pkhbt %0, %1, %2, lsl #16" + : "=r"(wri) + : "r"((int32_t)wr), "r"((int32_t)wi)); +#endif + + for (i = m; i < n; i += istep) { + j = i + l; + +#ifdef WEBRTC_ARCH_ARM_V7 + register int32_t frfi_r; + __asm __volatile( + "pkhbt %[frfi_r], %[frfi_even], %[frfi_odd], lsl #16\n\t" + "smlsd %[tr32], %[wri], %[frfi_r], %[cifftrnd]\n\t" + "smladx %[ti32], %[wri], %[frfi_r], %[cifftrnd]\n\t" + : [frfi_r] "=&r"(frfi_r), [tr32] "=&r"(tr32), [ti32] "=r"(ti32) + : [frfi_even] "r"((int32_t)frfi[2 * j]), + [frfi_odd] "r"((int32_t)frfi[2 * j + 1]), [wri] "r"(wri), + [cifftrnd] "r"(CIFFTRND)); +#else + + tr32 = wr * frfi[2 * j] - wi * frfi[2 * j + 1] + CIFFTRND; + + ti32 = wr * frfi[2 * j + 1] + wi * frfi[2 * j] + CIFFTRND; +#endif + tr32 >>= 15 - CIFFTSFT; + ti32 >>= 15 - CIFFTSFT; + + qr32 = ((int32_t)frfi[2 * i]) * (1 << CIFFTSFT); + qi32 = ((int32_t)frfi[2 * i + 1]) * (1 << CIFFTSFT); + + frfi[2 * j] = (int16_t)((qr32 - tr32 + round2) >> (shift + CIFFTSFT)); + frfi[2 * j + 1] = + (int16_t)((qi32 - ti32 + round2) >> (shift + CIFFTSFT)); + frfi[2 * i] = (int16_t)((qr32 + tr32 + round2) >> (shift + CIFFTSFT)); + frfi[2 * i + 1] = + (int16_t)((qi32 + ti32 + round2) >> (shift + CIFFTSFT)); + } + } + } + --k; + l = istep; + } + return scale; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/complex_fft_tables.h b/pkg/apm/webrtc/common_audio/signal_processing/complex_fft_tables.h new file mode 100644 index 00000000..90fac072 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/complex_fft_tables.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_COMPLEX_FFT_TABLES_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_COMPLEX_FFT_TABLES_H_ + +#include + +static const int16_t kSinTable1024[] = { + 0, 201, 402, 603, 804, 1005, 1206, 1406, 1607, + 1808, 2009, 2209, 2410, 2610, 2811, 3011, 3211, 3411, + 3611, 3811, 4011, 4210, 4409, 4608, 4807, 5006, 5205, + 5403, 5601, 5799, 5997, 6195, 6392, 6589, 6786, 6982, + 7179, 7375, 7571, 7766, 7961, 8156, 8351, 8545, 8739, + 8932, 9126, 9319, 9511, 9703, 9895, 10087, 10278, 10469, + 10659, 10849, 11038, 11227, 11416, 11604, 11792, 11980, 12166, + 12353, 12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827, + 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268, 15446, + 15623, 15799, 15975, 16150, 16325, 16499, 16672, 16845, 17017, + 17189, 17360, 17530, 17699, 17868, 18036, 18204, 18371, 18537, + 18702, 18867, 19031, 19194, 19357, 19519, 19680, 19840, 20000, + 20159, 20317, 20474, 20631, 20787, 20942, 21096, 21249, 21402, + 21554, 21705, 21855, 22004, 22153, 22301, 22448, 22594, 22739, + 22883, 23027, 23169, 23311, 23452, 23592, 23731, 23869, 24006, + 24143, 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201, + 25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198, 26318, + 26437, 26556, 26673, 26789, 26905, 27019, 27132, 27244, 27355, + 27466, 27575, 27683, 27790, 27896, 28001, 28105, 28208, 28309, + 28410, 28510, 28608, 28706, 28802, 28897, 28992, 29085, 29177, + 29268, 29358, 29446, 29534, 29621, 29706, 29790, 29873, 29955, + 30036, 30116, 30195, 30272, 30349, 30424, 30498, 30571, 30643, + 30713, 30783, 30851, 30918, 30984, 31049, 31113, 31175, 31236, + 31297, 31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735, + 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097, 32137, + 32176, 32213, 32249, 32284, 32318, 32350, 32382, 32412, 32441, + 32468, 32495, 32520, 32544, 32567, 32588, 32609, 32628, 32646, + 32662, 32678, 32692, 32705, 32717, 32727, 32736, 32744, 32751, + 32757, 32761, 32764, 32766, 32767, 32766, 32764, 32761, 32757, + 32751, 32744, 32736, 32727, 32717, 32705, 32692, 32678, 32662, + 32646, 32628, 32609, 32588, 32567, 32544, 32520, 32495, 32468, + 32441, 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176, + 32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833, 31785, + 31735, 31684, 31633, 31580, 31525, 31470, 31413, 31356, 31297, + 31236, 31175, 31113, 31049, 30984, 30918, 30851, 30783, 30713, + 30643, 30571, 30498, 30424, 30349, 30272, 30195, 30116, 30036, + 29955, 29873, 29790, 29706, 29621, 29534, 29446, 29358, 29268, + 29177, 29085, 28992, 28897, 28802, 28706, 28608, 28510, 28410, + 28309, 28208, 28105, 28001, 27896, 27790, 27683, 27575, 27466, + 27355, 27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437, + 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456, 25329, + 25201, 25072, 24942, 24811, 24679, 24546, 24413, 24278, 24143, + 24006, 23869, 23731, 23592, 23452, 23311, 23169, 23027, 22883, + 22739, 22594, 22448, 22301, 22153, 22004, 21855, 21705, 21554, + 21402, 21249, 21096, 20942, 20787, 20631, 20474, 20317, 20159, + 20000, 19840, 19680, 19519, 19357, 19194, 19031, 18867, 18702, + 18537, 18371, 18204, 18036, 17868, 17699, 17530, 17360, 17189, + 17017, 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623, + 15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191, 14009, + 13827, 13645, 13462, 13278, 13094, 12909, 12724, 12539, 12353, + 12166, 11980, 11792, 11604, 11416, 11227, 11038, 10849, 10659, + 10469, 10278, 10087, 9895, 9703, 9511, 9319, 9126, 8932, + 8739, 8545, 8351, 8156, 7961, 7766, 7571, 7375, 7179, + 6982, 6786, 6589, 6392, 6195, 5997, 5799, 5601, 5403, + 5205, 5006, 4807, 4608, 4409, 4210, 4011, 3811, 3611, + 3411, 3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808, + 1607, 1406, 1206, 1005, 804, 603, 402, 201, 0, + -201, -402, -603, -804, -1005, -1206, -1406, -1607, -1808, + -2009, -2209, -2410, -2610, -2811, -3011, -3211, -3411, -3611, + -3811, -4011, -4210, -4409, -4608, -4807, -5006, -5205, -5403, + -5601, -5799, -5997, -6195, -6392, -6589, -6786, -6982, -7179, + -7375, -7571, -7766, -7961, -8156, -8351, -8545, -8739, -8932, + -9126, -9319, -9511, -9703, -9895, -10087, -10278, -10469, -10659, + -10849, -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353, + -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827, -14009, + -14191, -14372, -14552, -14732, -14911, -15090, -15268, -15446, -15623, + -15799, -15975, -16150, -16325, -16499, -16672, -16845, -17017, -17189, + -17360, -17530, -17699, -17868, -18036, -18204, -18371, -18537, -18702, + -18867, -19031, -19194, -19357, -19519, -19680, -19840, -20000, -20159, + -20317, -20474, -20631, -20787, -20942, -21096, -21249, -21402, -21554, + -21705, -21855, -22004, -22153, -22301, -22448, -22594, -22739, -22883, + -23027, -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143, + -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201, -25329, + -25456, -25582, -25707, -25831, -25954, -26077, -26198, -26318, -26437, + -26556, -26673, -26789, -26905, -27019, -27132, -27244, -27355, -27466, + -27575, -27683, -27790, -27896, -28001, -28105, -28208, -28309, -28410, + -28510, -28608, -28706, -28802, -28897, -28992, -29085, -29177, -29268, + -29358, -29446, -29534, -29621, -29706, -29790, -29873, -29955, -30036, + -30116, -30195, -30272, -30349, -30424, -30498, -30571, -30643, -30713, + -30783, -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297, + -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735, -31785, + -31833, -31880, -31926, -31970, -32014, -32056, -32097, -32137, -32176, + -32213, -32249, -32284, -32318, -32350, -32382, -32412, -32441, -32468, + -32495, -32520, -32544, -32567, -32588, -32609, -32628, -32646, -32662, + -32678, -32692, -32705, -32717, -32727, -32736, -32744, -32751, -32757, + -32761, -32764, -32766, -32767, -32766, -32764, -32761, -32757, -32751, + -32744, -32736, -32727, -32717, -32705, -32692, -32678, -32662, -32646, + -32628, -32609, -32588, -32567, -32544, -32520, -32495, -32468, -32441, + -32412, -32382, -32350, -32318, -32284, -32249, -32213, -32176, -32137, + -32097, -32056, -32014, -31970, -31926, -31880, -31833, -31785, -31735, + -31684, -31633, -31580, -31525, -31470, -31413, -31356, -31297, -31236, + -31175, -31113, -31049, -30984, -30918, -30851, -30783, -30713, -30643, + -30571, -30498, -30424, -30349, -30272, -30195, -30116, -30036, -29955, + -29873, -29790, -29706, -29621, -29534, -29446, -29358, -29268, -29177, + -29085, -28992, -28897, -28802, -28706, -28608, -28510, -28410, -28309, + -28208, -28105, -28001, -27896, -27790, -27683, -27575, -27466, -27355, + -27244, -27132, -27019, -26905, -26789, -26673, -26556, -26437, -26318, + -26198, -26077, -25954, -25831, -25707, -25582, -25456, -25329, -25201, + -25072, -24942, -24811, -24679, -24546, -24413, -24278, -24143, -24006, + -23869, -23731, -23592, -23452, -23311, -23169, -23027, -22883, -22739, + -22594, -22448, -22301, -22153, -22004, -21855, -21705, -21554, -21402, + -21249, -21096, -20942, -20787, -20631, -20474, -20317, -20159, -20000, + -19840, -19680, -19519, -19357, -19194, -19031, -18867, -18702, -18537, + -18371, -18204, -18036, -17868, -17699, -17530, -17360, -17189, -17017, + -16845, -16672, -16499, -16325, -16150, -15975, -15799, -15623, -15446, + -15268, -15090, -14911, -14732, -14552, -14372, -14191, -14009, -13827, + -13645, -13462, -13278, -13094, -12909, -12724, -12539, -12353, -12166, + -11980, -11792, -11604, -11416, -11227, -11038, -10849, -10659, -10469, + -10278, -10087, -9895, -9703, -9511, -9319, -9126, -8932, -8739, + -8545, -8351, -8156, -7961, -7766, -7571, -7375, -7179, -6982, + -6786, -6589, -6392, -6195, -5997, -5799, -5601, -5403, -5205, + -5006, -4807, -4608, -4409, -4210, -4011, -3811, -3611, -3411, + -3211, -3011, -2811, -2610, -2410, -2209, -2009, -1808, -1607, + -1406, -1206, -1005, -804, -603, -402, -201}; + +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_COMPLEX_FFT_TABLES_H_ diff --git a/pkg/apm/webrtc/common_audio/signal_processing/copy_set_operations.c b/pkg/apm/webrtc/common_audio/signal_processing/copy_set_operations.c new file mode 100644 index 00000000..059b0a19 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/copy_set_operations.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the implementation of functions + * WebRtcSpl_MemSetW16() + * WebRtcSpl_MemSetW32() + * WebRtcSpl_MemCpyReversedOrder() + * WebRtcSpl_CopyFromEndW16() + * WebRtcSpl_ZerosArrayW16() + * WebRtcSpl_ZerosArrayW32() + * + * The description header can be found in signal_processing_library.h + * + */ + +#include + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +void WebRtcSpl_MemSetW16(int16_t* ptr, int16_t set_value, size_t length) { + size_t j; + int16_t* arrptr = ptr; + + for (j = length; j > 0; j--) { + *arrptr++ = set_value; + } +} + +void WebRtcSpl_MemSetW32(int32_t* ptr, int32_t set_value, size_t length) { + size_t j; + int32_t* arrptr = ptr; + + for (j = length; j > 0; j--) { + *arrptr++ = set_value; + } +} + +void WebRtcSpl_MemCpyReversedOrder(int16_t* dest, + int16_t* source, + size_t length) { + size_t j; + int16_t* destPtr = dest; + int16_t* sourcePtr = source; + + for (j = 0; j < length; j++) { + *destPtr-- = *sourcePtr++; + } +} + +void WebRtcSpl_CopyFromEndW16(const int16_t* vector_in, + size_t length, + size_t samples, + int16_t* vector_out) { + // Copy the last of the input vector to vector_out + WEBRTC_SPL_MEMCPY_W16(vector_out, &vector_in[length - samples], samples); +} + +void WebRtcSpl_ZerosArrayW16(int16_t* vector, size_t length) { + WebRtcSpl_MemSetW16(vector, 0, length); +} + +void WebRtcSpl_ZerosArrayW32(int32_t* vector, size_t length) { + WebRtcSpl_MemSetW32(vector, 0, length); +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/cross_correlation.c b/pkg/apm/webrtc/common_audio/signal_processing/cross_correlation.c new file mode 100644 index 00000000..c6267c92 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/cross_correlation.c @@ -0,0 +1,30 @@ +/* + * 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 "common_audio/signal_processing/include/signal_processing_library.h" + +/* C version of WebRtcSpl_CrossCorrelation() for generic platforms. */ +void WebRtcSpl_CrossCorrelationC(int32_t* cross_correlation, + const int16_t* seq1, + const int16_t* seq2, + size_t dim_seq, + size_t dim_cross_correlation, + int right_shifts, + int step_seq2) { + size_t i = 0, j = 0; + + for (i = 0; i < dim_cross_correlation; i++) { + int32_t corr = 0; + for (j = 0; j < dim_seq; j++) + corr += (seq1[j] * seq2[j]) >> right_shifts; + seq2 += step_seq2; + *cross_correlation++ = corr; + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/cross_correlation_neon_arm64.c b/pkg/apm/webrtc/common_audio/signal_processing/cross_correlation_neon_arm64.c new file mode 100644 index 00000000..409e7343 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/cross_correlation_neon_arm64.c @@ -0,0 +1,85 @@ +/* + * 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 + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/system/arch.h" + +static inline void DotProductWithScaleNeon(int32_t* cross_correlation, + const int16_t* vector1, + const int16_t* vector2, + size_t length, + int scaling) { + size_t i = 0; + size_t len1 = length >> 3; + size_t len2 = length & 7; + int64x2_t sum0 = vdupq_n_s64(0); + int64x2_t sum1 = vdupq_n_s64(0); + + for (i = len1; i > 0; i -= 1) { + int16x8_t seq1_16x8 = vld1q_s16(vector1); + int16x8_t seq2_16x8 = vld1q_s16(vector2); +#if defined(WEBRTC_ARCH_ARM64) + int32x4_t tmp0 = + vmull_s16(vget_low_s16(seq1_16x8), vget_low_s16(seq2_16x8)); + int32x4_t tmp1 = vmull_high_s16(seq1_16x8, seq2_16x8); +#else + int32x4_t tmp0 = + vmull_s16(vget_low_s16(seq1_16x8), vget_low_s16(seq2_16x8)); + int32x4_t tmp1 = + vmull_s16(vget_high_s16(seq1_16x8), vget_high_s16(seq2_16x8)); +#endif + sum0 = vpadalq_s32(sum0, tmp0); + sum1 = vpadalq_s32(sum1, tmp1); + vector1 += 8; + vector2 += 8; + } + + // Calculate the rest of the samples. + int64_t sum_res = 0; + for (i = len2; i > 0; i -= 1) { + sum_res += WEBRTC_SPL_MUL_16_16(*vector1, *vector2); + vector1++; + vector2++; + } + + sum0 = vaddq_s64(sum0, sum1); +#if defined(WEBRTC_ARCH_ARM64) + int64_t sum2 = vaddvq_s64(sum0); + *cross_correlation = (int32_t)((sum2 + sum_res) >> scaling); +#else + int64x1_t shift = vdup_n_s64(-scaling); + int64x1_t sum2 = vadd_s64(vget_low_s64(sum0), vget_high_s64(sum0)); + sum2 = vadd_s64(sum2, vdup_n_s64(sum_res)); + sum2 = vshl_s64(sum2, shift); + vst1_lane_s32(cross_correlation, vreinterpret_s32_s64(sum2), 0); +#endif +} + +/* NEON version of WebRtcSpl_CrossCorrelation() for ARM32/64 platforms. */ +void WebRtcSpl_CrossCorrelationNeon(int32_t* cross_correlation, + const int16_t* seq1, + const int16_t* seq2, + size_t dim_seq, + size_t dim_cross_correlation, + int right_shifts, + int step_seq2) { + int i = 0; + + for (i = 0; i < (int)dim_cross_correlation; i++) { + const int16_t* seq1_ptr = seq1; + const int16_t* seq2_ptr = seq2 + (step_seq2 * i); + + DotProductWithScaleNeon(cross_correlation, seq1_ptr, seq2_ptr, dim_seq, + right_shifts); + cross_correlation++; + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/division_operations.c b/pkg/apm/webrtc/common_audio/signal_processing/division_operations.c new file mode 100644 index 00000000..d0fbc247 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/division_operations.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains implementations of the divisions + * WebRtcSpl_DivU32U16() + * WebRtcSpl_DivW32W16() + * WebRtcSpl_DivW32W16ResW16() + * WebRtcSpl_DivResultInQ31() + * WebRtcSpl_DivW32HiLow() + * + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/sanitizer.h" + +uint32_t WebRtcSpl_DivU32U16(uint32_t num, uint16_t den) { + // Guard against division with 0 + if (den != 0) { + return (uint32_t)(num / den); + } else { + return (uint32_t)0xFFFFFFFF; + } +} + +int32_t WebRtcSpl_DivW32W16(int32_t num, int16_t den) { + // Guard against division with 0 + if (den != 0) { + return (int32_t)(num / den); + } else { + return (int32_t)0x7FFFFFFF; + } +} + +int16_t WebRtcSpl_DivW32W16ResW16(int32_t num, int16_t den) { + // Guard against division with 0 + if (den != 0) { + return (int16_t)(num / den); + } else { + return (int16_t)0x7FFF; + } +} + +int32_t WebRtcSpl_DivResultInQ31(int32_t num, int32_t den) { + int32_t L_num = num; + int32_t L_den = den; + int32_t div = 0; + int k = 31; + int change_sign = 0; + + if (num == 0) + return 0; + + if (num < 0) { + change_sign++; + L_num = -num; + } + if (den < 0) { + change_sign++; + L_den = -den; + } + while (k--) { + div <<= 1; + L_num <<= 1; + if (L_num >= L_den) { + L_num -= L_den; + div++; + } + } + if (change_sign == 1) { + div = -div; + } + return div; +} + +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; + + approx = (int16_t)WebRtcSpl_DivW32W16((int32_t)0x1FFFFFFF, den_hi); + // result in Q14 (Note: 3FFFFFFF = 0.5 in Q30) + + // tmpW32 = 1/den = approx * (2.0 - den * approx) (in Q30) + tmpW32 = (den_hi * approx << 1) + ((den_low * approx >> 15) << 1); + // tmpW32 = den * approx + + // 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); + tmp_low = (int16_t)((tmpW32 - ((int32_t)tmp_hi << 16)) >> 1); + + // tmpW32 = 1/den in Q29 + tmpW32 = (tmp_hi * approx + (tmp_low * approx >> 15)) << 1; + + // 1/den in hi and low format + tmp_hi = (int16_t)(tmpW32 >> 16); + tmp_low = (int16_t)((tmpW32 - ((int32_t)tmp_hi << 16)) >> 1); + + // Store num in hi and low format + num_hi = (int16_t)(num >> 16); + num_low = (int16_t)((num - ((int32_t)num_hi << 16)) >> 1); + + // num * (1/den) by 32 bit multiplication (result in Q28) + + tmpW32 = + num_hi * tmp_hi + (num_hi * tmp_low >> 15) + (num_low * tmp_hi >> 15); + + // Put result in Q31 (convert from Q28) + tmpW32 = WEBRTC_SPL_LSHIFT_W32(tmpW32, 3); + + return tmpW32; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/dot_product_with_scale.cc b/pkg/apm/webrtc/common_audio/signal_processing/dot_product_with_scale.cc new file mode 100644 index 00000000..56e0bcd5 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/dot_product_with_scale.cc @@ -0,0 +1,34 @@ +/* + * 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 "common_audio/signal_processing/dot_product_with_scale.h" + +#include "rtc_base/numerics/safe_conversions.h" + +int32_t WebRtcSpl_DotProductWithScale(const int16_t* vector1, + const int16_t* vector2, + size_t length, + int scaling) { + int64_t sum = 0; + size_t i = 0; + + /* Unroll the loop to improve performance. */ + for (i = 0; i + 3 < length; i += 4) { + sum += (vector1[i + 0] * vector2[i + 0]) >> scaling; + sum += (vector1[i + 1] * vector2[i + 1]) >> scaling; + sum += (vector1[i + 2] * vector2[i + 2]) >> scaling; + sum += (vector1[i + 3] * vector2[i + 3]) >> scaling; + } + for (; i < length; i++) { + sum += (vector1[i] * vector2[i]) >> scaling; + } + + return webrtc::saturated_cast(sum); +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/dot_product_with_scale.h b/pkg/apm/webrtc/common_audio/signal_processing/dot_product_with_scale.h new file mode 100644 index 00000000..9f0d922a --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/dot_product_with_scale.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_DOT_PRODUCT_WITH_SCALE_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_DOT_PRODUCT_WITH_SCALE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Calculates the dot product between two (int16_t) vectors. +// +// Input: +// - vector1 : Vector 1 +// - vector2 : Vector 2 +// - 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`) +// +// Return value : The dot product in Q(-scaling) +int32_t WebRtcSpl_DotProductWithScale(const int16_t* vector1, + const int16_t* vector2, + size_t length, + int scaling); + +#ifdef __cplusplus +} +#endif // __cplusplus +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_DOT_PRODUCT_WITH_SCALE_H_ diff --git a/pkg/apm/webrtc/common_audio/signal_processing/downsample_fast.c b/pkg/apm/webrtc/common_audio/signal_processing/downsample_fast.c new file mode 100644 index 00000000..c9d9021e --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/downsample_fast.c @@ -0,0 +1,64 @@ +/* + * 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 "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" +#include "rtc_base/sanitizer.h" + +// TODO(Bjornv): Change the function parameter order to WebRTC code style. +// C version of WebRtcSpl_DownsampleFast() for generic platforms. +int WebRtcSpl_DownsampleFastC(const int16_t* data_in, + size_t data_in_length, + int16_t* data_out, + size_t data_out_length, + const int16_t* __restrict coefficients, + size_t coefficients_length, + int factor, + size_t delay) { + int16_t* const original_data_out = data_out; + size_t i = 0; + size_t j = 0; + int32_t out_s32 = 0; + size_t endpos = delay + factor * (data_out_length - 1) + 1; + + // Return error if any of the running conditions doesn't meet. + if (data_out_length == 0 || coefficients_length == 0 || + data_in_length < endpos) { + return -1; + } + + rtc_MsanCheckInitialized(coefficients, sizeof(coefficients[0]), + coefficients_length); + + for (i = delay; i < endpos; i += factor) { + out_s32 = 2048; // Round value, 0.5 in Q12. + + for (j = 0; j < coefficients_length; j++) { + // Negative overflow is permitted here, because this is + // auto-regressive filters, and the state for each batch run is + // stored in the "negative" positions of the output vector. + rtc_MsanCheckInitialized(&data_in[(ptrdiff_t)i - (ptrdiff_t)j], + sizeof(data_in[0]), 1); + // out_s32 is in Q12 domain. + out_s32 += coefficients[j] * data_in[(ptrdiff_t)i - (ptrdiff_t)j]; + } + + out_s32 >>= 12; // Q0. + + // Saturate and store the output. + *data_out++ = WebRtcSpl_SatW32ToW16(out_s32); + } + + RTC_DCHECK_EQ(original_data_out + data_out_length, data_out); + rtc_MsanCheckInitialized(original_data_out, sizeof(original_data_out[0]), + data_out_length); + + return 0; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/downsample_fast_neon_arm64.c b/pkg/apm/webrtc/common_audio/signal_processing/downsample_fast_neon_arm64.c new file mode 100644 index 00000000..e97ca478 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/downsample_fast_neon_arm64.c @@ -0,0 +1,223 @@ +/* + * 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 + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" + +// NEON intrinsics version of WebRtcSpl_DownsampleFast() +// for ARM 32-bit/64-bit platforms. +int WebRtcSpl_DownsampleFastNeon(const int16_t* data_in, + size_t data_in_length, + int16_t* data_out, + size_t data_out_length, + const int16_t* __restrict coefficients, + size_t coefficients_length, + int factor, + size_t delay) { + // Using signed indexes to be able to compute negative i-j that + // is used to index data_in. + int i = 0; + int j = 0; + int32_t out_s32 = 0; + int endpos = delay + factor * (data_out_length - 1) + 1; + size_t res = data_out_length & 0x7; + int endpos1 = endpos - factor * res; + + // Return error if any of the running conditions doesn't meet. + if (data_out_length == 0 || coefficients_length == 0 || + (int)data_in_length < endpos) { + return -1; + } + + RTC_DCHECK_GE(endpos, 0); + RTC_DCHECK_GE(endpos1, 0); + + // First part, unroll the loop 8 times, with 3 subcases + // (factor == 2, 4, others). + switch (factor) { + case 2: { + for (i = delay; i < endpos1; i += 16) { + // Round value, 0.5 in Q12. + int32x4_t out32x4_0 = vdupq_n_s32(2048); + int32x4_t out32x4_1 = vdupq_n_s32(2048); + +#if defined(WEBRTC_ARCH_ARM64) + // Unroll the loop 2 times. + for (j = 0; j < (int)coefficients_length - 1; j += 2) { + int32x2_t coeff32 = vld1_dup_s32((int32_t*)&coefficients[j]); + int16x4_t coeff16x4 = vreinterpret_s16_s32(coeff32); + int16x8x2_t in16x8x2 = vld2q_s16(&data_in[i - j - 1]); + + // Mul and accumulate low 64-bit data. + int16x4_t in16x4_0 = vget_low_s16(in16x8x2.val[0]); + int16x4_t in16x4_1 = vget_low_s16(in16x8x2.val[1]); + out32x4_0 = vmlal_lane_s16(out32x4_0, in16x4_0, coeff16x4, 1); + out32x4_0 = vmlal_lane_s16(out32x4_0, in16x4_1, coeff16x4, 0); + + // Mul and accumulate high 64-bit data. + // TODO: vget_high_s16 need extra cost on ARM64. This could be + // replaced by vmlal_high_lane_s16. But for the interface of + // vmlal_high_lane_s16, there is a bug in gcc 4.9. + // This issue need to be tracked in the future. + int16x4_t in16x4_2 = vget_high_s16(in16x8x2.val[0]); + int16x4_t in16x4_3 = vget_high_s16(in16x8x2.val[1]); + out32x4_1 = vmlal_lane_s16(out32x4_1, in16x4_2, coeff16x4, 1); + out32x4_1 = vmlal_lane_s16(out32x4_1, in16x4_3, coeff16x4, 0); + } + + for (; j < (int)coefficients_length; j++) { + int16x4_t coeff16x4 = vld1_dup_s16(&coefficients[j]); + int16x8x2_t in16x8x2 = vld2q_s16(&data_in[i - j]); + + // Mul and accumulate low 64-bit data. + int16x4_t in16x4_0 = vget_low_s16(in16x8x2.val[0]); + out32x4_0 = vmlal_lane_s16(out32x4_0, in16x4_0, coeff16x4, 0); + + // Mul and accumulate high 64-bit data. + // TODO: vget_high_s16 need extra cost on ARM64. This could be + // replaced by vmlal_high_lane_s16. But for the interface of + // vmlal_high_lane_s16, there is a bug in gcc 4.9. + // This issue need to be tracked in the future. + int16x4_t in16x4_1 = vget_high_s16(in16x8x2.val[0]); + out32x4_1 = vmlal_lane_s16(out32x4_1, in16x4_1, coeff16x4, 0); + } +#else + // On ARMv7, the loop unrolling 2 times results in performance + // regression. + for (j = 0; j < (int)coefficients_length; j++) { + int16x4_t coeff16x4 = vld1_dup_s16(&coefficients[j]); + int16x8x2_t in16x8x2 = vld2q_s16(&data_in[i - j]); + + // Mul and accumulate. + int16x4_t in16x4_0 = vget_low_s16(in16x8x2.val[0]); + int16x4_t in16x4_1 = vget_high_s16(in16x8x2.val[0]); + out32x4_0 = vmlal_lane_s16(out32x4_0, in16x4_0, coeff16x4, 0); + out32x4_1 = vmlal_lane_s16(out32x4_1, in16x4_1, coeff16x4, 0); + } +#endif + + // Saturate and store the output. + int16x4_t out16x4_0 = vqshrn_n_s32(out32x4_0, 12); + int16x4_t out16x4_1 = vqshrn_n_s32(out32x4_1, 12); + vst1q_s16(data_out, vcombine_s16(out16x4_0, out16x4_1)); + data_out += 8; + } + break; + } + case 4: { + for (i = delay; i < endpos1; i += 32) { + // Round value, 0.5 in Q12. + int32x4_t out32x4_0 = vdupq_n_s32(2048); + int32x4_t out32x4_1 = vdupq_n_s32(2048); + + // Unroll the loop 4 times. + for (j = 0; j < (int)coefficients_length - 3; j += 4) { + int16x4_t coeff16x4 = vld1_s16(&coefficients[j]); + int16x8x4_t in16x8x4 = vld4q_s16(&data_in[i - j - 3]); + + // Mul and accumulate low 64-bit data. + int16x4_t in16x4_0 = vget_low_s16(in16x8x4.val[0]); + int16x4_t in16x4_2 = vget_low_s16(in16x8x4.val[1]); + int16x4_t in16x4_4 = vget_low_s16(in16x8x4.val[2]); + int16x4_t in16x4_6 = vget_low_s16(in16x8x4.val[3]); + out32x4_0 = vmlal_lane_s16(out32x4_0, in16x4_0, coeff16x4, 3); + out32x4_0 = vmlal_lane_s16(out32x4_0, in16x4_2, coeff16x4, 2); + out32x4_0 = vmlal_lane_s16(out32x4_0, in16x4_4, coeff16x4, 1); + out32x4_0 = vmlal_lane_s16(out32x4_0, in16x4_6, coeff16x4, 0); + + // Mul and accumulate high 64-bit data. + // TODO: vget_high_s16 need extra cost on ARM64. This could be + // replaced by vmlal_high_lane_s16. But for the interface of + // vmlal_high_lane_s16, there is a bug in gcc 4.9. + // This issue need to be tracked in the future. + int16x4_t in16x4_1 = vget_high_s16(in16x8x4.val[0]); + int16x4_t in16x4_3 = vget_high_s16(in16x8x4.val[1]); + int16x4_t in16x4_5 = vget_high_s16(in16x8x4.val[2]); + int16x4_t in16x4_7 = vget_high_s16(in16x8x4.val[3]); + out32x4_1 = vmlal_lane_s16(out32x4_1, in16x4_1, coeff16x4, 3); + out32x4_1 = vmlal_lane_s16(out32x4_1, in16x4_3, coeff16x4, 2); + out32x4_1 = vmlal_lane_s16(out32x4_1, in16x4_5, coeff16x4, 1); + out32x4_1 = vmlal_lane_s16(out32x4_1, in16x4_7, coeff16x4, 0); + } + + for (; j < (int)coefficients_length; j++) { + int16x4_t coeff16x4 = vld1_dup_s16(&coefficients[j]); + int16x8x4_t in16x8x4 = vld4q_s16(&data_in[i - j]); + + // Mul and accumulate low 64-bit data. + int16x4_t in16x4_0 = vget_low_s16(in16x8x4.val[0]); + out32x4_0 = vmlal_lane_s16(out32x4_0, in16x4_0, coeff16x4, 0); + + // Mul and accumulate high 64-bit data. + // TODO: vget_high_s16 need extra cost on ARM64. This could be + // replaced by vmlal_high_lane_s16. But for the interface of + // vmlal_high_lane_s16, there is a bug in gcc 4.9. + // This issue need to be tracked in the future. + int16x4_t in16x4_1 = vget_high_s16(in16x8x4.val[0]); + out32x4_1 = vmlal_lane_s16(out32x4_1, in16x4_1, coeff16x4, 0); + } + + // Saturate and store the output. + int16x4_t out16x4_0 = vqshrn_n_s32(out32x4_0, 12); + int16x4_t out16x4_1 = vqshrn_n_s32(out32x4_1, 12); + vst1q_s16(data_out, vcombine_s16(out16x4_0, out16x4_1)); + data_out += 8; + } + break; + } + default: { + for (i = delay; i < endpos1; i += factor * 8) { + // Round value, 0.5 in Q12. + int32x4_t out32x4_0 = vdupq_n_s32(2048); + int32x4_t out32x4_1 = vdupq_n_s32(2048); + + for (j = 0; j < (int)coefficients_length; j++) { + int16x4_t coeff16x4 = vld1_dup_s16(&coefficients[j]); + int16x4_t in16x4_0 = vld1_dup_s16(&data_in[i - j]); + in16x4_0 = vld1_lane_s16(&data_in[i + factor - j], in16x4_0, 1); + in16x4_0 = vld1_lane_s16(&data_in[i + factor * 2 - j], in16x4_0, 2); + in16x4_0 = vld1_lane_s16(&data_in[i + factor * 3 - j], in16x4_0, 3); + int16x4_t in16x4_1 = vld1_dup_s16(&data_in[i + factor * 4 - j]); + in16x4_1 = vld1_lane_s16(&data_in[i + factor * 5 - j], in16x4_1, 1); + in16x4_1 = vld1_lane_s16(&data_in[i + factor * 6 - j], in16x4_1, 2); + in16x4_1 = vld1_lane_s16(&data_in[i + factor * 7 - j], in16x4_1, 3); + + // Mul and accumulate. + out32x4_0 = vmlal_lane_s16(out32x4_0, in16x4_0, coeff16x4, 0); + out32x4_1 = vmlal_lane_s16(out32x4_1, in16x4_1, coeff16x4, 0); + } + + // Saturate and store the output. + int16x4_t out16x4_0 = vqshrn_n_s32(out32x4_0, 12); + int16x4_t out16x4_1 = vqshrn_n_s32(out32x4_1, 12); + vst1q_s16(data_out, vcombine_s16(out16x4_0, out16x4_1)); + data_out += 8; + } + break; + } + } + + // Second part, do the rest iterations (if any). + for (; i < endpos; i += factor) { + out_s32 = 2048; // Round value, 0.5 in Q12. + + for (j = 0; j < (int)coefficients_length; j++) { + out_s32 = WebRtc_MulAccumW16(coefficients[j], data_in[i - j], out_s32); + } + + // Saturate and store the output. + out_s32 >>= 12; + *data_out++ = WebRtcSpl_SatW32ToW16(out_s32); + } + + return 0; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/energy.c b/pkg/apm/webrtc/common_audio/signal_processing/energy.c new file mode 100644 index 00000000..f69ec1e4 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/energy.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_Energy(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +int32_t WebRtcSpl_Energy(int16_t* vector, + size_t vector_length, + int* scale_factor) { + int32_t en = 0; + size_t i; + int scaling = + WebRtcSpl_GetScalingSquare(vector, vector_length, vector_length); + size_t looptimes = vector_length; + int16_t* vectorptr = vector; + + for (i = 0; i < looptimes; i++) { + en += (*vectorptr * *vectorptr) >> scaling; + vectorptr++; + } + *scale_factor = scaling; + + return en; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/filter_ar.c b/pkg/apm/webrtc/common_audio/signal_processing/filter_ar.c new file mode 100644 index 00000000..bab1973b --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/filter_ar.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_FilterAR(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" + +size_t WebRtcSpl_FilterAR(const int16_t* a, + size_t a_length, + const int16_t* x, + size_t x_length, + int16_t* state, + size_t state_length, + int16_t* state_low, + int16_t* filtered, + int16_t* filtered_low) { + int64_t o; + int32_t oLOW; + size_t i, j, stop; + const int16_t* x_ptr = &x[0]; + int16_t* filteredFINAL_ptr = filtered; + int16_t* filteredFINAL_LOW_ptr = filtered_low; + + for (i = 0; i < x_length; i++) { + // Calculate filtered[i] and filtered_low[i] + const int16_t* a_ptr = &a[1]; + // The index can become negative, but the arrays will never be indexed + // with it when negative. Nevertheless, the index cannot be a size_t + // because of this. + int filtered_ix = (int)i - 1; + int16_t* state_ptr = &state[state_length - 1]; + int16_t* state_low_ptr = &state_low[state_length - 1]; + + o = (int32_t)(*x_ptr++) * (1 << 12); + oLOW = (int32_t)0; + + stop = (i < a_length) ? i + 1 : a_length; + for (j = 1; j < stop; j++) { + RTC_DCHECK_GE(filtered_ix, 0); + o -= *a_ptr * filtered[filtered_ix]; + oLOW -= *a_ptr++ * filtered_low[filtered_ix]; + --filtered_ix; + } + for (j = i + 1; j < a_length; j++) { + o -= *a_ptr * *state_ptr--; + oLOW -= *a_ptr++ * *state_low_ptr--; + } + + o += (oLOW >> 12); + *filteredFINAL_ptr = (int16_t)((o + (int32_t)2048) >> 12); + *filteredFINAL_LOW_ptr++ = + (int16_t)(o - ((int32_t)(*filteredFINAL_ptr++) * (1 << 12))); + } + + // Save the filter state + if (x_length >= state_length) { + WebRtcSpl_CopyFromEndW16(filtered, x_length, a_length - 1, state); + WebRtcSpl_CopyFromEndW16(filtered_low, x_length, a_length - 1, state_low); + } else { + for (i = 0; i < state_length - x_length; i++) { + state[i] = state[i + x_length]; + state_low[i] = state_low[i + x_length]; + } + for (i = 0; i < x_length; i++) { + state[state_length - x_length + i] = filtered[i]; + state_low[state_length - x_length + i] = filtered_low[i]; + } + } + + return x_length; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/filter_ar_fast_q12.c b/pkg/apm/webrtc/common_audio/signal_processing/filter_ar_fast_q12.c new file mode 100644 index 00000000..eceef4cf --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/filter_ar_fast_q12.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" + +// TODO(bjornv): Change the return type to report errors. + +void WebRtcSpl_FilterARFastQ12(const int16_t* data_in, + int16_t* data_out, + const int16_t* __restrict coefficients, + size_t coefficients_length, + size_t data_length) { + size_t i = 0; + size_t j = 0; + + RTC_DCHECK_GT(data_length, 0); + RTC_DCHECK_GT(coefficients_length, 1); + + for (i = 0; i < data_length; i++) { + int64_t output = 0; + int64_t sum = 0; + + for (j = coefficients_length - 1; j > 0; j--) { + // Negative overflow is permitted here, because this is + // auto-regressive filters, and the state for each batch run is + // stored in the "negative" positions of the output vector. + sum += coefficients[j] * data_out[(ptrdiff_t)i - (ptrdiff_t)j]; + } + + output = coefficients[0] * data_in[i]; + output -= sum; + + // Saturate and store the output. + output = WEBRTC_SPL_SAT(134215679, output, -134217728); + data_out[i] = (int16_t)((output + 2048) >> 12); + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/filter_ma_fast_q12.c b/pkg/apm/webrtc/common_audio/signal_processing/filter_ma_fast_q12.c new file mode 100644 index 00000000..57f5929b --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/filter_ma_fast_q12.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_FilterMAFastQ12(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/sanitizer.h" + +void WebRtcSpl_FilterMAFastQ12(const int16_t* in_ptr, + int16_t* out_ptr, + const int16_t* B, + size_t B_length, + size_t length) { + size_t i, j; + + rtc_MsanCheckInitialized(B, sizeof(B[0]), B_length); + rtc_MsanCheckInitialized(in_ptr - B_length + 1, sizeof(in_ptr[0]), + B_length + length - 1); + + for (i = 0; i < length; i++) { + int32_t o = 0; + + for (j = 0; j < B_length; j++) { + // Negative overflow is permitted here, because this is + // auto-regressive filters, and the state for each batch run is + // stored in the "negative" positions of the output vector. + o += B[j] * in_ptr[(ptrdiff_t)i - (ptrdiff_t)j]; + } + + // If output is higher than 32768, saturate it. Same with negative side + // 2^27 = 134217728, which corresponds to 32768 in Q12 + + // Saturate the output + o = WEBRTC_SPL_SAT((int32_t)134215679, o, (int32_t)-134217728); + + *out_ptr++ = (int16_t)((o + (int32_t)2048) >> 12); + } + return; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/get_hanning_window.c b/pkg/apm/webrtc/common_audio/signal_processing/get_hanning_window.c new file mode 100644 index 00000000..0a6aa5bc --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/get_hanning_window.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_GetHanningWindow(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +// Hanning table with 256 entries +static const int16_t kHanningTable[] = { + 1, 2, 6, 10, 15, 22, 30, 39, 50, 62, 75, + 89, 104, 121, 138, 157, 178, 199, 222, 246, 271, 297, + 324, 353, 383, 413, 446, 479, 513, 549, 586, 624, 663, + 703, 744, 787, 830, 875, 920, 967, 1015, 1064, 1114, 1165, + 1218, 1271, 1325, 1381, 1437, 1494, 1553, 1612, 1673, 1734, 1796, + 1859, 1924, 1989, 2055, 2122, 2190, 2259, 2329, 2399, 2471, 2543, + 2617, 2691, 2765, 2841, 2918, 2995, 3073, 3152, 3232, 3312, 3393, + 3475, 3558, 3641, 3725, 3809, 3895, 3980, 4067, 4154, 4242, 4330, + 4419, 4509, 4599, 4689, 4781, 4872, 4964, 5057, 5150, 5244, 5338, + 5432, 5527, 5622, 5718, 5814, 5910, 6007, 6104, 6202, 6299, 6397, + 6495, 6594, 6693, 6791, 6891, 6990, 7090, 7189, 7289, 7389, 7489, + 7589, 7690, 7790, 7890, 7991, 8091, 8192, 8293, 8393, 8494, 8594, + 8694, 8795, 8895, 8995, 9095, 9195, 9294, 9394, 9493, 9593, 9691, + 9790, 9889, 9987, 10085, 10182, 10280, 10377, 10474, 10570, 10666, 10762, + 10857, 10952, 11046, 11140, 11234, 11327, 11420, 11512, 11603, 11695, 11785, + 11875, 11965, 12054, 12142, 12230, 12317, 12404, 12489, 12575, 12659, 12743, + 12826, 12909, 12991, 13072, 13152, 13232, 13311, 13389, 13466, 13543, 13619, + 13693, 13767, 13841, 13913, 13985, 14055, 14125, 14194, 14262, 14329, 14395, + 14460, 14525, 14588, 14650, 14711, 14772, 14831, 14890, 14947, 15003, 15059, + 15113, 15166, 15219, 15270, 15320, 15369, 15417, 15464, 15509, 15554, 15597, + 15640, 15681, 15721, 15760, 15798, 15835, 15871, 15905, 15938, 15971, 16001, + 16031, 16060, 16087, 16113, 16138, 16162, 16185, 16206, 16227, 16246, 16263, + 16280, 16295, 16309, 16322, 16334, 16345, 16354, 16362, 16369, 16374, 16378, + 16382, 16383, 16384}; + +void WebRtcSpl_GetHanningWindow(int16_t* v, size_t size) { + size_t jj; + int16_t* vptr1; + + int32_t index; + int32_t factor = ((int32_t)0x40000000); + + factor = WebRtcSpl_DivW32W16(factor, (int16_t)size); + if (size < 513) + index = (int32_t)-0x200000; + else + index = (int32_t)-0x100000; + vptr1 = v; + + for (jj = 0; jj < size; jj++) { + index += factor; + (*vptr1++) = kHanningTable[index >> 22]; + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/get_scaling_square.c b/pkg/apm/webrtc/common_audio/signal_processing/get_scaling_square.c new file mode 100644 index 00000000..4b4986b7 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/get_scaling_square.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_GetScalingSquare(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +int16_t WebRtcSpl_GetScalingSquare(int16_t* in_vector, + size_t in_vector_length, + size_t times) { + int16_t nbits = WebRtcSpl_GetSizeInBits((uint32_t)times); + size_t i; + int16_t smax = -1; + int16_t sabs; + int16_t* sptr = in_vector; + int16_t t; + size_t looptimes = in_vector_length; + + for (i = looptimes; i > 0; i--) { + sabs = (*sptr > 0 ? *sptr++ : -*sptr++); + smax = (sabs > smax ? sabs : smax); + } + t = WebRtcSpl_NormW32(WEBRTC_SPL_MUL(smax, smax)); + + if (smax == 0) { + return 0; // Since norm(0) returns 0 + } else { + return (t > nbits) ? 0 : nbits - t; + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/include/real_fft.h b/pkg/apm/webrtc/common_audio/signal_processing/include/real_fft.h new file mode 100644 index 00000000..a0da5096 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/include/real_fft.h @@ -0,0 +1,96 @@ +/* + * 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 COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_REAL_FFT_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_REAL_FFT_H_ + +#include + +// For ComplexFFT(), the maximum fft order is 10; +// WebRTC APM uses orders of only 7 and 8. +enum { kMaxFFTOrder = 10 }; + +struct RealFFT; + +#ifdef __cplusplus +extern "C" { +#endif + +struct RealFFT* WebRtcSpl_CreateRealFFT(int order); +void WebRtcSpl_FreeRealFFT(struct RealFFT* self); + +// Compute an FFT for a real-valued signal of length of 2^order, +// where 1 < order <= MAX_FFT_ORDER. Transform length is determined by the +// specification structure, which must be initialized prior to calling the FFT +// function with WebRtcSpl_CreateRealFFT(). +// The relationship between the input and output sequences can +// be expressed in terms of the DFT, i.e.: +// x[n] = (2^(-scalefactor)/N) . SUM[k=0,...,N-1] X[k].e^(jnk.2.pi/N) +// n=0,1,2,...N-1 +// N=2^order. +// The conjugate-symmetric output sequence is represented using a CCS vector, +// which is of length N+2, and is organized as follows: +// Index: 0 1 2 3 4 5 . . . N-2 N-1 N N+1 +// Component: R0 0 R1 I1 R2 I2 . . . R[N/2-1] I[N/2-1] R[N/2] 0 +// where R[n] and I[n], respectively, denote the real and imaginary components +// for FFT bin 'n'. Bins are numbered from 0 to N/2, where N is the FFT length. +// Bin index 0 corresponds to the DC component, and bin index N/2 corresponds to +// the foldover frequency. +// +// Input Arguments: +// self - pointer to preallocated and initialized FFT specification structure. +// real_data_in - the input signal. For an ARM Neon platform, it must be +// aligned on a 32-byte boundary. +// +// Output Arguments: +// complex_data_out - the output complex signal with (2^order + 2) 16-bit +// elements. For an ARM Neon platform, it must be different +// from real_data_in, and aligned on a 32-byte boundary. +// +// Return Value: +// 0 - FFT calculation is successful. +// -1 - Error with bad arguments (null pointers). +int WebRtcSpl_RealForwardFFT(struct RealFFT* self, + const int16_t* real_data_in, + int16_t* complex_data_out); + +// Compute the inverse FFT for a conjugate-symmetric input sequence of length of +// 2^order, where 1 < order <= MAX_FFT_ORDER. Transform length is determined by +// the specification structure, which must be initialized prior to calling the +// FFT function with WebRtcSpl_CreateRealFFT(). +// For a transform of length M, the input sequence is represented using a packed +// CCS vector of length M+2, which is explained in the comments for +// WebRtcSpl_RealForwardFFTC above. +// +// Input Arguments: +// self - pointer to preallocated and initialized FFT specification structure. +// complex_data_in - the input complex signal with (2^order + 2) 16-bit +// elements. For an ARM Neon platform, it must be aligned on +// a 32-byte boundary. +// +// Output Arguments: +// real_data_out - the output real signal. For an ARM Neon platform, it must +// be different to complex_data_in, and aligned on a 32-byte +// boundary. +// +// Return Value: +// 0 or a positive number - a value that the elements in the `real_data_out` +// should be shifted left with in order to get +// correct physical values. +// -1 - Error with bad arguments (null pointers). +int WebRtcSpl_RealInverseFFT(struct RealFFT* self, + const int16_t* complex_data_in, + int16_t* real_data_out); + +#ifdef __cplusplus +} +#endif + +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_REAL_FFT_H_ diff --git a/pkg/apm/webrtc/common_audio/signal_processing/include/signal_processing_library.h b/pkg/apm/webrtc/common_audio/signal_processing/include/signal_processing_library.h new file mode 100644 index 00000000..72c5fc40 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/include/signal_processing_library.h @@ -0,0 +1,1626 @@ +/* + * 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. + */ + +/* + * This header file includes all of the fix point signal processing library + * (SPL) function descriptions and declarations. For specific function calls, + * see bottom of file. + */ + +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SIGNAL_PROCESSING_LIBRARY_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SIGNAL_PROCESSING_LIBRARY_H_ + +#include + +#include "common_audio/signal_processing/dot_product_with_scale.h" + +// Macros specific for the fixed point implementation +#define WEBRTC_SPL_WORD16_MAX 32767 +#define WEBRTC_SPL_WORD16_MIN -32768 +#define WEBRTC_SPL_WORD32_MAX (int32_t)0x7fffffff +#define WEBRTC_SPL_WORD32_MIN (int32_t)0x80000000 +#define WEBRTC_SPL_MAX_LPC_ORDER 14 +#define WEBRTC_SPL_MIN(A, B) (A < B ? A : B) // Get min value +#define WEBRTC_SPL_MAX(A, B) (A > B ? A : B) // Get max value +// TODO(kma/bjorn): For the next two macros, investigate how to correct the code +// for inputs of a = WEBRTC_SPL_WORD16_MIN or WEBRTC_SPL_WORD32_MIN. +#define WEBRTC_SPL_ABS_W16(a) (((int16_t)a >= 0) ? ((int16_t)a) : -((int16_t)a)) +#define WEBRTC_SPL_ABS_W32(a) (((int32_t)a >= 0) ? ((int32_t)a) : -((int32_t)a)) + +#define WEBRTC_SPL_MUL(a, b) ((int32_t)((int32_t)(a) * (int32_t)(b))) +#define WEBRTC_SPL_UMUL(a, b) ((uint32_t)((uint32_t)(a) * (uint32_t)(b))) +#define WEBRTC_SPL_UMUL_32_16(a, b) ((uint32_t)((uint32_t)(a) * (uint16_t)(b))) +#define WEBRTC_SPL_MUL_16_U16(a, b) ((int32_t)(int16_t)(a) * (uint16_t)(b)) + +// clang-format off +// clang-format would choose some indentation +// leading to presubmit error (cpplint.py) +#ifndef WEBRTC_ARCH_ARM_V7 +// For ARMv7 platforms, these are inline functions in spl_inl_armv7.h +#ifndef MIPS32_LE +// For MIPS platforms, these are inline functions in spl_inl_mips.h +#define WEBRTC_SPL_MUL_16_16(a, b) ((int32_t)(((int16_t)(a)) * ((int16_t)(b)))) +#define WEBRTC_SPL_MUL_16_32_RSFT16(a, b) \ + (WEBRTC_SPL_MUL_16_16(a, b >> 16) + \ + ((WEBRTC_SPL_MUL_16_16(a, (b & 0xffff) >> 1) + 0x4000) >> 15)) +#endif +#endif + +#define WEBRTC_SPL_MUL_16_32_RSFT11(a, b) \ + (WEBRTC_SPL_MUL_16_16(a, (b) >> 16) * (1 << 5) + \ + (((WEBRTC_SPL_MUL_16_U16(a, (uint16_t)(b)) >> 1) + 0x0200) >> 10)) +#define WEBRTC_SPL_MUL_16_32_RSFT14(a, b) \ + (WEBRTC_SPL_MUL_16_16(a, (b) >> 16) * (1 << 2) + \ + (((WEBRTC_SPL_MUL_16_U16(a, (uint16_t)(b)) >> 1) + 0x1000) >> 13)) +#define WEBRTC_SPL_MUL_16_32_RSFT15(a, b) \ + ((WEBRTC_SPL_MUL_16_16(a, (b) >> 16) * (1 << 1)) + \ + (((WEBRTC_SPL_MUL_16_U16(a, (uint16_t)(b)) >> 1) + 0x2000) >> 14)) +// clang-format on + +#define WEBRTC_SPL_MUL_16_16_RSFT(a, b, c) (WEBRTC_SPL_MUL_16_16(a, b) >> (c)) + +#define WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(a, b, c) \ + ((WEBRTC_SPL_MUL_16_16(a, b) + ((int32_t)(((int32_t)1) << ((c)-1)))) >> (c)) + +// C + the 32 most significant bits of A * B +#define WEBRTC_SPL_SCALEDIFF32(A, B, C) \ + (C + (B >> 16) * A + (((uint32_t)(B & 0x0000FFFF) * A) >> 16)) + +#define WEBRTC_SPL_SAT(a, b, c) (b > a ? a : b < c ? c : b) + +// Shifting with negative numbers allowed +// Positive means left shift +#define WEBRTC_SPL_SHIFT_W32(x, c) ((c) >= 0 ? (x) * (1 << (c)) : (x) >> -(c)) + +// Shifting with negative numbers not allowed +// We cannot do casting here due to signed/unsigned problem +#define WEBRTC_SPL_LSHIFT_W32(x, c) ((x) << (c)) + +#define WEBRTC_SPL_RSHIFT_U32(x, c) ((uint32_t)(x) >> (c)) + +#define WEBRTC_SPL_RAND(a) ((int16_t)((((int16_t)a * 18816) >> 7) & 0x00007fff)) + +#ifdef __cplusplus +extern "C" { +#endif + +#define WEBRTC_SPL_MEMCPY_W16(v1, v2, length) \ + memcpy(v1, v2, (length) * sizeof(int16_t)) + +// inline functions: +#include "common_audio/signal_processing/include/spl_inl.h" + +// third party math functions +#include "common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h" + +int16_t WebRtcSpl_GetScalingSquare(int16_t* in_vector, + size_t in_vector_length, + size_t times); + +// Copy and set operations. Implementation in copy_set_operations.c. +// Descriptions at bottom of file. +void WebRtcSpl_MemSetW16(int16_t* vector, + int16_t set_value, + size_t vector_length); +void WebRtcSpl_MemSetW32(int32_t* vector, + int32_t set_value, + size_t vector_length); +void WebRtcSpl_MemCpyReversedOrder(int16_t* out_vector, + int16_t* in_vector, + size_t vector_length); +void WebRtcSpl_CopyFromEndW16(const int16_t* in_vector, + size_t in_vector_length, + size_t samples, + int16_t* out_vector); +void WebRtcSpl_ZerosArrayW16(int16_t* vector, size_t vector_length); +void WebRtcSpl_ZerosArrayW32(int32_t* vector, size_t vector_length); +// End: Copy and set operations. + +// Minimum and maximum operation functions and their pointers. +// Implementation in min_max_operations.c. + +// Returns the largest absolute value in a signed 16-bit vector. +// +// Input: +// - vector : 16-bit input vector. +// - length : Number of samples in vector. +// +// Return value : Maximum absolute value in vector. +typedef int16_t (*MaxAbsValueW16)(const int16_t* vector, size_t length); +extern const MaxAbsValueW16 WebRtcSpl_MaxAbsValueW16; +int16_t WebRtcSpl_MaxAbsValueW16C(const int16_t* vector, size_t length); +#if defined(WEBRTC_HAS_NEON) +int16_t WebRtcSpl_MaxAbsValueW16Neon(const int16_t* vector, size_t length); +#endif +#if defined(MIPS32_LE) +int16_t WebRtcSpl_MaxAbsValueW16_mips(const int16_t* vector, size_t length); +#endif + +// Returns the largest absolute value in a signed 32-bit vector. +// +// Input: +// - vector : 32-bit input vector. +// - length : Number of samples in vector. +// +// Return value : Maximum absolute value in vector. +typedef int32_t (*MaxAbsValueW32)(const int32_t* vector, size_t length); +extern const MaxAbsValueW32 WebRtcSpl_MaxAbsValueW32; +int32_t WebRtcSpl_MaxAbsValueW32C(const int32_t* vector, size_t length); +#if defined(WEBRTC_HAS_NEON) +int32_t WebRtcSpl_MaxAbsValueW32Neon(const int32_t* vector, size_t length); +#endif +#if defined(MIPS_DSP_R1_LE) +int32_t WebRtcSpl_MaxAbsValueW32_mips(const int32_t* vector, size_t length); +#endif + +// Returns the maximum value of a 16-bit vector. +// +// Input: +// - vector : 16-bit input vector. +// - length : Number of samples in vector. +// +// Return value : Maximum sample value in `vector`. +typedef int16_t (*MaxValueW16)(const int16_t* vector, size_t length); +extern const MaxValueW16 WebRtcSpl_MaxValueW16; +int16_t WebRtcSpl_MaxValueW16C(const int16_t* vector, size_t length); +#if defined(WEBRTC_HAS_NEON) +int16_t WebRtcSpl_MaxValueW16Neon(const int16_t* vector, size_t length); +#endif +#if defined(MIPS32_LE) +int16_t WebRtcSpl_MaxValueW16_mips(const int16_t* vector, size_t length); +#endif + +// Returns the maximum value of a 32-bit vector. +// +// Input: +// - vector : 32-bit input vector. +// - length : Number of samples in vector. +// +// Return value : Maximum sample value in `vector`. +typedef int32_t (*MaxValueW32)(const int32_t* vector, size_t length); +extern const MaxValueW32 WebRtcSpl_MaxValueW32; +int32_t WebRtcSpl_MaxValueW32C(const int32_t* vector, size_t length); +#if defined(WEBRTC_HAS_NEON) +int32_t WebRtcSpl_MaxValueW32Neon(const int32_t* vector, size_t length); +#endif +#if defined(MIPS32_LE) +int32_t WebRtcSpl_MaxValueW32_mips(const int32_t* vector, size_t length); +#endif + +// Returns the minimum value of a 16-bit vector. +// +// Input: +// - vector : 16-bit input vector. +// - length : Number of samples in vector. +// +// Return value : Minimum sample value in `vector`. +typedef int16_t (*MinValueW16)(const int16_t* vector, size_t length); +extern const MinValueW16 WebRtcSpl_MinValueW16; +int16_t WebRtcSpl_MinValueW16C(const int16_t* vector, size_t length); +#if defined(WEBRTC_HAS_NEON) +int16_t WebRtcSpl_MinValueW16Neon(const int16_t* vector, size_t length); +#endif +#if defined(MIPS32_LE) +int16_t WebRtcSpl_MinValueW16_mips(const int16_t* vector, size_t length); +#endif + +// Returns the minimum value of a 32-bit vector. +// +// Input: +// - vector : 32-bit input vector. +// - length : Number of samples in vector. +// +// Return value : Minimum sample value in `vector`. +typedef int32_t (*MinValueW32)(const int32_t* vector, size_t length); +extern const MinValueW32 WebRtcSpl_MinValueW32; +int32_t WebRtcSpl_MinValueW32C(const int32_t* vector, size_t length); +#if defined(WEBRTC_HAS_NEON) +int32_t WebRtcSpl_MinValueW32Neon(const int32_t* vector, size_t length); +#endif +#if defined(MIPS32_LE) +int32_t WebRtcSpl_MinValueW32_mips(const int32_t* vector, size_t length); +#endif + +// Returns both the minimum and maximum values of a 16-bit vector. +// +// Input: +// - vector : 16-bit input vector. +// - length : Number of samples in vector. +// Ouput: +// - max_val : Maximum sample value in `vector`. +// - min_val : Minimum sample value in `vector`. +void WebRtcSpl_MinMaxW16(const int16_t* vector, + size_t length, + int16_t* min_val, + int16_t* max_val); +#if defined(WEBRTC_HAS_NEON) +void WebRtcSpl_MinMaxW16Neon(const int16_t* vector, + size_t length, + int16_t* min_val, + int16_t* max_val); +#endif + +// Returns the vector index to the largest absolute value of a 16-bit vector. +// +// Input: +// - vector : 16-bit input vector. +// - length : Number of samples in vector. +// +// Return value : Index to the maximum absolute value in vector. +// If there are multiple equal maxima, return the index of the +// first. -32768 will always have precedence over 32767 (despite +// -32768 presenting an int16 absolute value of 32767). +size_t WebRtcSpl_MaxAbsIndexW16(const int16_t* vector, size_t length); + +// Returns the element with the largest absolute value of a 16-bit vector. Note +// that this function can return a negative value. +// +// Input: +// - vector : 16-bit input vector. +// - length : Number of samples in vector. +// +// Return value : The element with the largest absolute value. Note that this +// may be a negative value. +int16_t WebRtcSpl_MaxAbsElementW16(const int16_t* vector, size_t length); + +// Returns the vector index to the maximum sample value of a 16-bit vector. +// +// Input: +// - vector : 16-bit input vector. +// - length : Number of samples in vector. +// +// Return value : Index to the maximum value in vector (if multiple +// indexes have the maximum, return the first). +size_t WebRtcSpl_MaxIndexW16(const int16_t* vector, size_t length); + +// Returns the vector index to the maximum sample value of a 32-bit vector. +// +// Input: +// - vector : 32-bit input vector. +// - length : Number of samples in vector. +// +// Return value : Index to the maximum value in vector (if multiple +// indexes have the maximum, return the first). +size_t WebRtcSpl_MaxIndexW32(const int32_t* vector, size_t length); + +// Returns the vector index to the minimum sample value of a 16-bit vector. +// +// Input: +// - vector : 16-bit input vector. +// - length : Number of samples in vector. +// +// Return value : Index to the mimimum value in vector (if multiple +// indexes have the minimum, return the first). +size_t WebRtcSpl_MinIndexW16(const int16_t* vector, size_t length); + +// Returns the vector index to the minimum sample value of a 32-bit vector. +// +// Input: +// - vector : 32-bit input vector. +// - length : Number of samples in vector. +// +// Return value : Index to the mimimum value in vector (if multiple +// indexes have the minimum, return the first). +size_t WebRtcSpl_MinIndexW32(const int32_t* vector, size_t length); + +// End: Minimum and maximum operations. + +// Vector scaling operations. Implementation in vector_scaling_operations.c. +// Description at bottom of file. +void WebRtcSpl_VectorBitShiftW16(int16_t* out_vector, + size_t vector_length, + const int16_t* in_vector, + int16_t right_shifts); +void WebRtcSpl_VectorBitShiftW32(int32_t* out_vector, + size_t vector_length, + const int32_t* in_vector, + int16_t right_shifts); +void WebRtcSpl_VectorBitShiftW32ToW16(int16_t* out_vector, + size_t vector_length, + const int32_t* in_vector, + int right_shifts); +void WebRtcSpl_ScaleVector(const int16_t* in_vector, + int16_t* out_vector, + int16_t gain, + size_t vector_length, + int16_t right_shifts); +void WebRtcSpl_ScaleVectorWithSat(const int16_t* in_vector, + int16_t* out_vector, + int16_t gain, + size_t vector_length, + int16_t right_shifts); +void WebRtcSpl_ScaleAndAddVectors(const int16_t* in_vector1, + int16_t gain1, + int right_shifts1, + const int16_t* in_vector2, + int16_t gain2, + int right_shifts2, + int16_t* out_vector, + size_t vector_length); + +// The functions (with related pointer) perform the vector operation: +// out_vector[k] = ((scale1 * in_vector1[k]) + (scale2 * in_vector2[k]) +// + round_value) >> right_shifts, +// where round_value = (1 << right_shifts) >> 1. +// +// Input: +// - in_vector1 : Input vector 1 +// - in_vector1_scale : Gain to be used for vector 1 +// - in_vector2 : Input vector 2 +// - in_vector2_scale : Gain to be used for vector 2 +// - right_shifts : Number of right bit shifts to be applied +// - length : Number of elements in the input vectors +// +// Output: +// - out_vector : Output vector +// Return value : 0 if OK, -1 if (in_vector1 == null +// || in_vector2 == null || out_vector == null +// || length <= 0 || right_shift < 0). +typedef int (*ScaleAndAddVectorsWithRound)(const int16_t* in_vector1, + int16_t in_vector1_scale, + const int16_t* in_vector2, + int16_t in_vector2_scale, + int right_shifts, + int16_t* out_vector, + size_t length); +extern const ScaleAndAddVectorsWithRound WebRtcSpl_ScaleAndAddVectorsWithRound; +int WebRtcSpl_ScaleAndAddVectorsWithRoundC(const int16_t* in_vector1, + int16_t in_vector1_scale, + const int16_t* in_vector2, + int16_t in_vector2_scale, + int right_shifts, + int16_t* out_vector, + size_t length); +#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, + int16_t in_vector2_scale, + int right_shifts, + int16_t* out_vector, + size_t length); +#endif +// End: Vector scaling operations. + +// +// WebRtcSpl_ReverseOrderMultArrayElements(...) +// +// Performs the vector operation: +// out_vector[n] = (in_vector[n]*window[-n])>>right_shifts +// +// Input: +// - in_vector : Input vector +// - window : Window vector (should be reversed). The pointer +// should be set to the last value in the vector +// - right_shifts : Number of right bit shift to be applied after the +// multiplication +// - vector_length : Number of elements in `in_vector` +// +// Output: +// - out_vector : Output vector (can be same as `in_vector`) +// +void WebRtcSpl_ReverseOrderMultArrayElements(int16_t* out_vector, + const int16_t* in_vector, + const int16_t* window, + size_t vector_length, + int16_t right_shifts); + +// +// WebRtcSpl_ElementwiseVectorMult(...) +// +// Performs the vector operation: +// out_vector[n] = (in_vector[n]*window[n])>>right_shifts +// +// Input: +// - in_vector : Input vector +// - window : Window vector. +// - right_shifts : Number of right bit shift to be applied after the +// multiplication +// - vector_length : Number of elements in `in_vector` +// +// Output: +// - out_vector : Output vector (can be same as `in_vector`) +// +void WebRtcSpl_ElementwiseVectorMult(int16_t* out_vector, + const int16_t* in_vector, + const int16_t* window, + size_t vector_length, + int16_t right_shifts); + +// +// WebRtcSpl_AddVectorsAndShift(...) +// +// Performs the vector operation: +// out_vector[k] = (in_vector1[k] + in_vector2[k])>>right_shifts +// +// Input: +// - in_vector1 : Input vector 1 +// - in_vector2 : Input vector 2 +// - right_shifts : Number of right bit shift to be applied after the +// multiplication +// - vector_length : Number of elements in `in_vector1` and `in_vector2` +// +// Output: +// - out_vector : Output vector (can be same as `in_vector1`) +// +void WebRtcSpl_AddVectorsAndShift(int16_t* out_vector, + const int16_t* in_vector1, + const int16_t* in_vector2, + size_t vector_length, + int16_t right_shifts); + +// +// WebRtcSpl_AddAffineVectorToVector(...) +// +// Adds an affine transformed vector to another vector `out_vector`, i.e, +// performs +// out_vector[k] += (in_vector[k]*gain+add_constant)>>right_shifts +// +// Input: +// - in_vector : Input vector +// - gain : Gain value, used to multiply the in vector with +// - add_constant : Constant value to add (usually 1<<(right_shifts-1), +// but others can be used as well +// - right_shifts : Number of right bit shifts (0-16) +// - vector_length : Number of samples in `in_vector` and `out_vector` +// +// Output: +// - out_vector : Vector with the output +// +void WebRtcSpl_AddAffineVectorToVector(int16_t* out_vector, + const int16_t* in_vector, + int16_t gain, + int32_t add_constant, + int16_t right_shifts, + size_t vector_length); + +// +// WebRtcSpl_AffineTransformVector(...) +// +// Affine transforms a vector, i.e, performs +// out_vector[k] = (in_vector[k]*gain+add_constant)>>right_shifts +// +// Input: +// - in_vector : Input vector +// - gain : Gain value, used to multiply the in vector with +// - add_constant : Constant value to add (usually 1<<(right_shifts-1), +// but others can be used as well +// - right_shifts : Number of right bit shifts (0-16) +// - vector_length : Number of samples in `in_vector` and `out_vector` +// +// Output: +// - out_vector : Vector with the output +// +void WebRtcSpl_AffineTransformVector(int16_t* out_vector, + const int16_t* in_vector, + int16_t gain, + int32_t add_constant, + int16_t right_shifts, + size_t vector_length); + +// Signal processing operations. + +// A 32-bit fix-point implementation of auto-correlation computation +// +// Input: +// - in_vector : Vector to calculate autocorrelation upon +// - in_vector_length : Length (in samples) of `vector` +// - order : The order up to which the autocorrelation should be +// calculated +// +// Output: +// - result : auto-correlation values (values should be seen +// relative to each other since the absolute values +// might have been down shifted to avoid overflow) +// +// - scale : The number of left shifts required to obtain the +// auto-correlation in Q0 +// +// Return value : Number of samples in `result`, i.e. (order+1) +size_t WebRtcSpl_AutoCorrelation(const int16_t* in_vector, + size_t in_vector_length, + size_t order, + int32_t* result, + int* scale); + +// A 32-bit fix-point implementation of the Levinson-Durbin algorithm that +// does NOT use the 64 bit class +// +// Input: +// - auto_corr : Vector with autocorrelation values of length >= `order`+1 +// - order : The LPC filter order (support up to order 20) +// +// Output: +// - lpc_coef : lpc_coef[0..order] LPC coefficients in Q12 +// - refl_coef : refl_coef[0...order-1]| Reflection coefficients in Q15 +// +// Return value : 1 for stable 0 for unstable +int16_t WebRtcSpl_LevinsonDurbin(const int32_t* auto_corr, + int16_t* lpc_coef, + int16_t* refl_coef, + size_t order); + +// Converts reflection coefficients `refl_coef` to LPC coefficients `lpc_coef`. +// This version is a 16 bit operation. +// +// NOTE: The 16 bit refl_coef -> lpc_coef conversion might result in a +// "slightly unstable" filter (i.e., a pole just outside the unit circle) in +// "rare" cases even if the reflection coefficients are stable. +// +// Input: +// - refl_coef : Reflection coefficients in Q15 that should be converted +// to LPC coefficients +// - use_order : Number of coefficients in `refl_coef` +// +// Output: +// - lpc_coef : LPC coefficients in Q12 +void WebRtcSpl_ReflCoefToLpc(const int16_t* refl_coef, + int use_order, + int16_t* lpc_coef); + +// Converts LPC coefficients `lpc_coef` to reflection coefficients `refl_coef`. +// This version is a 16 bit operation. +// The conversion is implemented by the step-down algorithm. +// +// Input: +// - lpc_coef : LPC coefficients in Q12, that should be converted to +// reflection coefficients +// - use_order : Number of coefficients in `lpc_coef` +// +// Output: +// - refl_coef : Reflection coefficients in Q15. +void WebRtcSpl_LpcToReflCoef(int16_t* lpc_coef, + int use_order, + int16_t* refl_coef); + +// Calculates reflection coefficients (16 bit) from auto-correlation values +// +// Input: +// - auto_corr : Auto-correlation values +// - use_order : Number of coefficients wanted be calculated +// +// Output: +// - refl_coef : Reflection coefficients in Q15. +void WebRtcSpl_AutoCorrToReflCoef(const int32_t* auto_corr, + int use_order, + int16_t* refl_coef); + +// The functions (with related pointer) calculate the cross-correlation between +// two sequences `seq1` and `seq2`. +// `seq1` is fixed and `seq2` slides as the pointer is increased with the +// amount `step_seq2`. Note the arguments should obey the relationship: +// `dim_seq` - 1 + `step_seq2` * (`dim_cross_correlation` - 1) < +// buffer size of `seq2` +// +// Input: +// - seq1 : First sequence (fixed throughout the correlation) +// - seq2 : Second sequence (slides `step_vector2` for each +// new correlation) +// - dim_seq : Number of samples to use in the cross-correlation +// - dim_cross_correlation : Number of cross-correlations to calculate (the +// start position for `vector2` is updated for each +// new one) +// - right_shifts : Number of right bit shifts to use. This will +// become the output Q-domain. +// - step_seq2 : How many (positive or negative) steps the +// `vector2` pointer should be updated for each new +// cross-correlation value. +// +// Output: +// - cross_correlation : The cross-correlation in Q(-right_shifts) +typedef void (*CrossCorrelation)(int32_t* cross_correlation, + const int16_t* seq1, + const int16_t* seq2, + size_t dim_seq, + size_t dim_cross_correlation, + int right_shifts, + int step_seq2); +extern const CrossCorrelation WebRtcSpl_CrossCorrelation; +void WebRtcSpl_CrossCorrelationC(int32_t* cross_correlation, + const int16_t* seq1, + const int16_t* seq2, + size_t dim_seq, + size_t dim_cross_correlation, + int right_shifts, + int step_seq2); +#if defined(WEBRTC_HAS_NEON) +void WebRtcSpl_CrossCorrelationNeon(int32_t* cross_correlation, + const int16_t* seq1, + const int16_t* seq2, + size_t dim_seq, + size_t dim_cross_correlation, + int right_shifts, + int step_seq2); +#endif +#if defined(MIPS32_LE) +void WebRtcSpl_CrossCorrelation_mips(int32_t* cross_correlation, + const int16_t* seq1, + const int16_t* seq2, + size_t dim_seq, + size_t dim_cross_correlation, + int right_shifts, + int step_seq2); +#endif + +// Creates (the first half of) a Hanning window. Size must be at least 1 and +// at most 512. +// +// Input: +// - size : Length of the requested Hanning window (1 to 512) +// +// Output: +// - window : Hanning vector in Q14. +void WebRtcSpl_GetHanningWindow(int16_t* window, size_t size); + +// Calculates y[k] = sqrt(1 - x[k]^2) for each element of the input vector +// `in_vector`. Input and output values are in Q15. +// +// Inputs: +// - in_vector : Values to calculate sqrt(1 - x^2) of +// - vector_length : Length of vector `in_vector` +// +// Output: +// - out_vector : Output values in Q15 +void WebRtcSpl_SqrtOfOneMinusXSquared(int16_t* in_vector, + size_t vector_length, + int16_t* out_vector); +// End: Signal processing operations. + +// Randomization functions. Implementations collected in +// randomization_functions.c and descriptions at bottom of this file. +int16_t WebRtcSpl_RandU(uint32_t* seed); +int16_t WebRtcSpl_RandN(uint32_t* seed); +int16_t WebRtcSpl_RandUArray(int16_t* vector, + int16_t vector_length, + uint32_t* seed); +// End: Randomization functions. + +// Math functions +int32_t WebRtcSpl_Sqrt(int32_t value); + +// Divisions. Implementations collected in division_operations.c and +// descriptions at bottom of this file. +uint32_t WebRtcSpl_DivU32U16(uint32_t num, uint16_t den); +int32_t WebRtcSpl_DivW32W16(int32_t num, int16_t den); +int16_t WebRtcSpl_DivW32W16ResW16(int32_t num, int16_t den); +int32_t WebRtcSpl_DivResultInQ31(int32_t num, int32_t den); +int32_t WebRtcSpl_DivW32HiLow(int32_t num, int16_t den_hi, int16_t den_low); +// End: Divisions. + +int32_t WebRtcSpl_Energy(int16_t* vector, + size_t vector_length, + int* scale_factor); + +// Filter operations. +size_t WebRtcSpl_FilterAR(const int16_t* ar_coef, + size_t ar_coef_length, + const int16_t* in_vector, + size_t in_vector_length, + int16_t* filter_state, + size_t filter_state_length, + int16_t* filter_state_low, + int16_t* out_vector, + int16_t* out_vector_low); + +// WebRtcSpl_FilterMAFastQ12(...) +// +// Performs a MA filtering on a vector in Q12 +// +// Input: +// - in_vector : Input samples (state in positions +// in_vector[-order] .. in_vector[-1]) +// - ma_coef : Filter coefficients (in Q12) +// - ma_coef_length : Number of B coefficients (order+1) +// - vector_length : Number of samples to be filtered +// +// Output: +// - out_vector : Filtered samples +// +void WebRtcSpl_FilterMAFastQ12(const int16_t* in_vector, + int16_t* out_vector, + const int16_t* ma_coef, + size_t ma_coef_length, + size_t vector_length); + +// Performs a AR filtering on a vector in Q12 +// Input: +// - data_in : Input samples +// - data_out : State information in positions +// data_out[-order] .. data_out[-1] +// - coefficients : Filter coefficients (in Q12) +// - coefficients_length: Number of coefficients (order+1) +// - data_length : Number of samples to be filtered +// Output: +// - data_out : Filtered samples +void WebRtcSpl_FilterARFastQ12(const int16_t* data_in, + int16_t* data_out, + const int16_t* __restrict coefficients, + size_t coefficients_length, + size_t data_length); + +// The functions (with related pointer) perform a MA down sampling filter +// on a vector. +// Input: +// - data_in : Input samples (state in positions +// data_in[-order] .. data_in[-1]) +// - data_in_length : Number of samples in `data_in` to be filtered. +// This must be at least +// `delay` + `factor`*(`out_vector_length`-1) + 1) +// - data_out_length : Number of down sampled samples desired +// - coefficients : Filter coefficients (in Q12) +// - coefficients_length: Number of coefficients (order+1) +// - factor : Decimation factor +// - delay : Delay of filter (compensated for in out_vector) +// Output: +// - data_out : Filtered samples +// Return value : 0 if OK, -1 if `in_vector` is too short +typedef int (*DownsampleFast)(const int16_t* data_in, + size_t data_in_length, + int16_t* data_out, + size_t data_out_length, + const int16_t* __restrict coefficients, + size_t coefficients_length, + int factor, + size_t delay); +extern const DownsampleFast WebRtcSpl_DownsampleFast; +int WebRtcSpl_DownsampleFastC(const int16_t* data_in, + size_t data_in_length, + int16_t* data_out, + size_t data_out_length, + const int16_t* __restrict coefficients, + size_t coefficients_length, + int factor, + size_t delay); +#if defined(WEBRTC_HAS_NEON) +int WebRtcSpl_DownsampleFastNeon(const int16_t* data_in, + size_t data_in_length, + int16_t* data_out, + size_t data_out_length, + const int16_t* __restrict coefficients, + size_t coefficients_length, + int factor, + size_t delay); +#endif +#if defined(MIPS32_LE) +int WebRtcSpl_DownsampleFast_mips(const int16_t* data_in, + size_t data_in_length, + int16_t* data_out, + size_t data_out_length, + const int16_t* __restrict coefficients, + size_t coefficients_length, + int factor, + size_t delay); +#endif + +// End: Filter operations. + +// FFT operations + +int WebRtcSpl_ComplexFFT(int16_t vector[], int stages, int mode); +int WebRtcSpl_ComplexIFFT(int16_t vector[], int stages, int mode); + +// Treat a 16-bit complex data buffer `complex_data` as an array of 32-bit +// values, and swap elements whose indexes are bit-reverses of each other. +// +// Input: +// - complex_data : Complex data buffer containing 2^`stages` real +// elements interleaved with 2^`stages` imaginary +// elements: [Re Im Re Im Re Im....] +// - stages : Number of FFT stages. Must be at least 3 and at most +// 10, since the table WebRtcSpl_kSinTable1024[] is 1024 +// elements long. +// +// Output: +// - complex_data : The complex data buffer. + +void WebRtcSpl_ComplexBitReverse(int16_t* __restrict complex_data, int stages); + +// End: FFT operations + +/************************************************************ + * + * RESAMPLING FUNCTIONS AND THEIR STRUCTS ARE DEFINED BELOW + * + ************************************************************/ + +/******************************************************************* + * resample.c + * + * Includes the following resampling combinations + * 22 kHz -> 16 kHz + * 16 kHz -> 22 kHz + * 22 kHz -> 8 kHz + * 8 kHz -> 22 kHz + * + ******************************************************************/ + +// state structure for 22 -> 16 resampler +typedef struct { + int32_t S_22_44[8]; + int32_t S_44_32[8]; + int32_t S_32_16[8]; +} WebRtcSpl_State22khzTo16khz; + +void WebRtcSpl_Resample22khzTo16khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State22khzTo16khz* state, + int32_t* tmpmem); + +void WebRtcSpl_ResetResample22khzTo16khz(WebRtcSpl_State22khzTo16khz* state); + +// state structure for 16 -> 22 resampler +typedef struct { + int32_t S_16_32[8]; + int32_t S_32_22[8]; +} WebRtcSpl_State16khzTo22khz; + +void WebRtcSpl_Resample16khzTo22khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State16khzTo22khz* state, + int32_t* tmpmem); + +void WebRtcSpl_ResetResample16khzTo22khz(WebRtcSpl_State16khzTo22khz* state); + +// state structure for 22 -> 8 resampler +typedef struct { + int32_t S_22_22[16]; + int32_t S_22_16[8]; + int32_t S_16_8[8]; +} WebRtcSpl_State22khzTo8khz; + +void WebRtcSpl_Resample22khzTo8khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State22khzTo8khz* state, + int32_t* tmpmem); + +void WebRtcSpl_ResetResample22khzTo8khz(WebRtcSpl_State22khzTo8khz* state); + +// state structure for 8 -> 22 resampler +typedef struct { + int32_t S_8_16[8]; + int32_t S_16_11[8]; + int32_t S_11_22[8]; +} WebRtcSpl_State8khzTo22khz; + +void WebRtcSpl_Resample8khzTo22khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State8khzTo22khz* state, + int32_t* tmpmem); + +void WebRtcSpl_ResetResample8khzTo22khz(WebRtcSpl_State8khzTo22khz* state); + +/******************************************************************* + * resample_fractional.c + * Functions for internal use in the other resample functions + * + * Includes the following resampling combinations + * 48 kHz -> 32 kHz + * 32 kHz -> 24 kHz + * 44 kHz -> 32 kHz + * + ******************************************************************/ + +void WebRtcSpl_Resample48khzTo32khz(const int32_t* In, int32_t* Out, size_t K); + +void WebRtcSpl_Resample32khzTo24khz(const int32_t* In, int32_t* Out, size_t K); + +void WebRtcSpl_Resample44khzTo32khz(const int32_t* In, int32_t* Out, size_t K); + +/******************************************************************* + * resample_48khz.c + * + * Includes the following resampling combinations + * 48 kHz -> 16 kHz + * 16 kHz -> 48 kHz + * 48 kHz -> 8 kHz + * 8 kHz -> 48 kHz + * + ******************************************************************/ + +typedef struct { + int32_t S_48_48[16]; + int32_t S_48_32[8]; + int32_t S_32_16[8]; +} WebRtcSpl_State48khzTo16khz; + +void WebRtcSpl_Resample48khzTo16khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State48khzTo16khz* state, + int32_t* tmpmem); + +void WebRtcSpl_ResetResample48khzTo16khz(WebRtcSpl_State48khzTo16khz* state); + +typedef struct { + int32_t S_16_32[8]; + int32_t S_32_24[8]; + int32_t S_24_48[8]; +} WebRtcSpl_State16khzTo48khz; + +void WebRtcSpl_Resample16khzTo48khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State16khzTo48khz* state, + int32_t* tmpmem); + +void WebRtcSpl_ResetResample16khzTo48khz(WebRtcSpl_State16khzTo48khz* state); + +typedef struct { + int32_t S_48_24[8]; + int32_t S_24_24[16]; + int32_t S_24_16[8]; + int32_t S_16_8[8]; +} WebRtcSpl_State48khzTo8khz; + +void WebRtcSpl_Resample48khzTo8khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State48khzTo8khz* state, + int32_t* tmpmem); + +void WebRtcSpl_ResetResample48khzTo8khz(WebRtcSpl_State48khzTo8khz* state); + +typedef struct { + int32_t S_8_16[8]; + int32_t S_16_12[8]; + int32_t S_12_24[8]; + int32_t S_24_48[8]; +} WebRtcSpl_State8khzTo48khz; + +void WebRtcSpl_Resample8khzTo48khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State8khzTo48khz* state, + int32_t* tmpmem); + +void WebRtcSpl_ResetResample8khzTo48khz(WebRtcSpl_State8khzTo48khz* state); + +/******************************************************************* + * resample_by_2.c + * + * Includes down and up sampling by a factor of two. + * + ******************************************************************/ + +void WebRtcSpl_DownsampleBy2(const int16_t* in, + size_t len, + int16_t* out, + int32_t* filtState); + +void WebRtcSpl_UpsampleBy2(const int16_t* in, + size_t len, + int16_t* out, + int32_t* filtState); + +/************************************************************ + * END OF RESAMPLING FUNCTIONS + ************************************************************/ +void WebRtcSpl_AnalysisQMF(const int16_t* in_data, + size_t in_data_length, + int16_t* low_band, + int16_t* high_band, + int32_t* filter_state1, + int32_t* filter_state2); +void WebRtcSpl_SynthesisQMF(const int16_t* low_band, + const int16_t* high_band, + size_t band_length, + int16_t* out_data, + int32_t* filter_state1, + int32_t* filter_state2); + +#ifdef __cplusplus +} +#endif // __cplusplus +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SIGNAL_PROCESSING_LIBRARY_H_ + +// +// WebRtcSpl_AddSatW16(...) +// WebRtcSpl_AddSatW32(...) +// +// Returns the result of a saturated 16-bit, respectively 32-bit, addition of +// the numbers specified by the `var1` and `var2` parameters. +// +// Input: +// - var1 : Input variable 1 +// - var2 : Input variable 2 +// +// Return value : Added and saturated value +// + +// +// WebRtcSpl_SubSatW16(...) +// WebRtcSpl_SubSatW32(...) +// +// Returns the result of a saturated 16-bit, respectively 32-bit, subtraction +// of the numbers specified by the `var1` and `var2` parameters. +// +// Input: +// - var1 : Input variable 1 +// - var2 : Input variable 2 +// +// Returned value : Subtracted and saturated value +// + +// +// WebRtcSpl_GetSizeInBits(...) +// +// Returns the # of bits that are needed at the most to represent the number +// specified by the `value` parameter. +// +// Input: +// - value : Input value +// +// Return value : Number of bits needed to represent `value` +// + +// +// WebRtcSpl_NormW32(...) +// +// Norm returns the # of left shifts required to 32-bit normalize the 32-bit +// signed number specified by the `value` parameter. +// +// Input: +// - value : Input value +// +// Return value : Number of bit shifts needed to 32-bit normalize `value` +// + +// +// WebRtcSpl_NormW16(...) +// +// Norm returns the # of left shifts required to 16-bit normalize the 16-bit +// signed number specified by the `value` parameter. +// +// Input: +// - value : Input value +// +// Return value : Number of bit shifts needed to 32-bit normalize `value` +// + +// +// WebRtcSpl_NormU32(...) +// +// Norm returns the # of left shifts required to 32-bit normalize the unsigned +// 32-bit number specified by the `value` parameter. +// +// Input: +// - value : Input value +// +// Return value : Number of bit shifts needed to 32-bit normalize `value` +// + +// +// WebRtcSpl_GetScalingSquare(...) +// +// Returns the # of bits required to scale the samples specified in the +// `in_vector` parameter so that, if the squares of the samples are added the +// # of times specified by the `times` parameter, the 32-bit addition will not +// overflow (result in int32_t). +// +// Input: +// - in_vector : Input vector to check scaling on +// - in_vector_length : Samples in `in_vector` +// - times : Number of additions to be performed +// +// Return value : Number of right bit shifts needed to avoid +// overflow in the addition calculation +// + +// +// WebRtcSpl_MemSetW16(...) +// +// Sets all the values in the int16_t vector `vector` of length +// `vector_length` to the specified value `set_value` +// +// Input: +// - vector : Pointer to the int16_t vector +// - set_value : Value specified +// - vector_length : Length of vector +// + +// +// WebRtcSpl_MemSetW32(...) +// +// Sets all the values in the int32_t vector `vector` of length +// `vector_length` to the specified value `set_value` +// +// Input: +// - vector : Pointer to the int16_t vector +// - set_value : Value specified +// - vector_length : Length of vector +// + +// +// WebRtcSpl_MemCpyReversedOrder(...) +// +// Copies all the values from the source int16_t vector `in_vector` to a +// destination int16_t vector `out_vector`. It is done in reversed order, +// meaning that the first sample of `in_vector` is copied to the last sample of +// the `out_vector`. The procedure continues until the last sample of +// `in_vector` has been copied to the first sample of `out_vector`. This +// creates a reversed vector. +// +// Input: +// - in_vector : Pointer to the first sample in a int16_t vector +// of length `length` +// - vector_length : Number of elements to copy +// +// Output: +// - out_vector : Pointer to the last sample in a int16_t vector +// of length `length` +// + +// +// WebRtcSpl_CopyFromEndW16(...) +// +// Copies the rightmost `samples` of `in_vector` (of length `in_vector_length`) +// to the vector `out_vector`. +// +// Input: +// - in_vector : Input vector +// - in_vector_length : Number of samples in `in_vector` +// - samples : Number of samples to extract (from right side) +// from `in_vector` +// +// Output: +// - out_vector : Vector with the requested samples +// + +// +// WebRtcSpl_ZerosArrayW16(...) +// WebRtcSpl_ZerosArrayW32(...) +// +// Inserts the value "zero" in all positions of a w16 and a w32 vector +// respectively. +// +// Input: +// - vector_length : Number of samples in vector +// +// Output: +// - vector : Vector containing all zeros +// + +// +// WebRtcSpl_VectorBitShiftW16(...) +// WebRtcSpl_VectorBitShiftW32(...) +// +// Bit shifts all the values in a vector up or downwards. Different calls for +// int16_t and int32_t vectors respectively. +// +// Input: +// - vector_length : Length of vector +// - in_vector : Pointer to the vector that should be bit shifted +// - right_shifts : Number of right bit shifts (negative value gives left +// shifts) +// +// Output: +// - out_vector : Pointer to the result vector (can be the same as +// `in_vector`) +// + +// +// WebRtcSpl_VectorBitShiftW32ToW16(...) +// +// Bit shifts all the values in a int32_t vector up or downwards and +// stores the result as an int16_t vector. The function will saturate the +// signal if needed, before storing in the output vector. +// +// Input: +// - vector_length : Length of vector +// - in_vector : Pointer to the vector that should be bit shifted +// - right_shifts : Number of right bit shifts (negative value gives left +// shifts) +// +// Output: +// - out_vector : Pointer to the result vector (can be the same as +// `in_vector`) +// + +// +// WebRtcSpl_ScaleVector(...) +// +// Performs the vector operation: +// out_vector[k] = (gain*in_vector[k])>>right_shifts +// +// Input: +// - in_vector : Input vector +// - gain : Scaling gain +// - vector_length : Elements in the `in_vector` +// - right_shifts : Number of right bit shifts applied +// +// Output: +// - out_vector : Output vector (can be the same as `in_vector`) +// + +// +// WebRtcSpl_ScaleVectorWithSat(...) +// +// Performs the vector operation: +// out_vector[k] = SATURATE( (gain*in_vector[k])>>right_shifts ) +// +// Input: +// - in_vector : Input vector +// - gain : Scaling gain +// - vector_length : Elements in the `in_vector` +// - right_shifts : Number of right bit shifts applied +// +// Output: +// - out_vector : Output vector (can be the same as `in_vector`) +// + +// +// WebRtcSpl_ScaleAndAddVectors(...) +// +// Performs the vector operation: +// out_vector[k] = (gain1*in_vector1[k])>>right_shifts1 +// + (gain2*in_vector2[k])>>right_shifts2 +// +// Input: +// - in_vector1 : Input vector 1 +// - gain1 : Gain to be used for vector 1 +// - right_shifts1 : Right bit shift to be used for vector 1 +// - in_vector2 : Input vector 2 +// - gain2 : Gain to be used for vector 2 +// - right_shifts2 : Right bit shift to be used for vector 2 +// - vector_length : Elements in the input vectors +// +// Output: +// - out_vector : Output vector +// + +// +// WebRtcSpl_IncreaseSeed(...) +// +// Increases the seed (and returns the new value) +// +// Input: +// - seed : Seed for random calculation +// +// Output: +// - seed : Updated seed value +// +// Return value : The new seed value +// + +// +// WebRtcSpl_RandU(...) +// +// Produces a uniformly distributed value in the int16_t range +// +// Input: +// - seed : Seed for random calculation +// +// Output: +// - seed : Updated seed value +// +// Return value : Uniformly distributed value in the range +// [Word16_MIN...Word16_MAX] +// + +// +// WebRtcSpl_RandN(...) +// +// Produces a normal distributed value in the int16_t range +// +// Input: +// - seed : Seed for random calculation +// +// Output: +// - seed : Updated seed value +// +// Return value : N(0,1) value in the Q13 domain +// + +// +// WebRtcSpl_RandUArray(...) +// +// Produces a uniformly distributed vector with elements in the int16_t +// range +// +// Input: +// - vector_length : Samples wanted in the vector +// - seed : Seed for random calculation +// +// Output: +// - vector : Vector with the uniform values +// - seed : Updated seed value +// +// Return value : Number of samples in vector, i.e., `vector_length` +// + +// +// WebRtcSpl_Sqrt(...) +// +// Returns the square root of the input value `value`. The precision of this +// function is integer precision, i.e., sqrt(8) gives 2 as answer. +// If `value` is a negative number then 0 is returned. +// +// Algorithm: +// +// A sixth order Taylor Series expansion is used here to compute the square +// root of a number y^0.5 = (1+x)^0.5 +// where +// x = y-1 +// = 1+(x/2)-0.5*((x/2)^2+0.5*((x/2)^3-0.625*((x/2)^4+0.875*((x/2)^5) +// 0.5 <= x < 1 +// +// Input: +// - value : Value to calculate sqrt of +// +// Return value : Result of the sqrt calculation +// + +// +// WebRtcSpl_DivU32U16(...) +// +// Divides a uint32_t `num` by a uint16_t `den`. +// +// If `den`==0, (uint32_t)0xFFFFFFFF is returned. +// +// Input: +// - num : Numerator +// - den : Denominator +// +// Return value : Result of the division (as a uint32_t), i.e., the +// integer part of num/den. +// + +// +// WebRtcSpl_DivW32W16(...) +// +// Divides a int32_t `num` by a int16_t `den`. +// +// If `den`==0, (int32_t)0x7FFFFFFF is returned. +// +// Input: +// - num : Numerator +// - den : Denominator +// +// Return value : Result of the division (as a int32_t), i.e., the +// integer part of num/den. +// + +// +// WebRtcSpl_DivW32W16ResW16(...) +// +// Divides a int32_t `num` by a int16_t `den`, assuming that the +// result is less than 32768, otherwise an unpredictable result will occur. +// +// If `den`==0, (int16_t)0x7FFF is returned. +// +// Input: +// - num : Numerator +// - den : Denominator +// +// Return value : Result of the division (as a int16_t), i.e., the +// integer part of num/den. +// + +// +// WebRtcSpl_DivResultInQ31(...) +// +// Divides a int32_t `num` by a int16_t `den`, assuming that the +// absolute value of the denominator is larger than the numerator, otherwise +// an unpredictable result will occur. +// +// Input: +// - num : Numerator +// - den : Denominator +// +// Return value : Result of the division in Q31. +// + +// +// WebRtcSpl_DivW32HiLow(...) +// +// Divides a int32_t `num` by a denominator in hi, low format. The +// absolute value of the denominator has to be larger (or equal to) the +// numerator. +// +// Input: +// - num : Numerator +// - den_hi : High part of denominator +// - den_low : Low part of denominator +// +// Return value : Divided value in Q31 +// + +// +// WebRtcSpl_Energy(...) +// +// Calculates the energy of a vector +// +// Input: +// - vector : Vector which the energy should be calculated on +// - vector_length : Number of samples in vector +// +// Output: +// - scale_factor : Number of left bit shifts needed to get the physical +// energy value, i.e, to get the Q0 value +// +// Return value : Energy value in Q(-`scale_factor`) +// + +// +// WebRtcSpl_FilterAR(...) +// +// Performs a 32-bit AR filtering on a vector in Q12 +// +// Input: +// - ar_coef : AR-coefficient vector (values in Q12), +// ar_coef[0] must be 4096. +// - ar_coef_length : Number of coefficients in `ar_coef`. +// - in_vector : Vector to be filtered. +// - in_vector_length : Number of samples in `in_vector`. +// - filter_state : Current state (higher part) of the filter. +// - filter_state_length : Length (in samples) of `filter_state`. +// - filter_state_low : Current state (lower part) of the filter. +// +// Output: +// - filter_state : Updated state (upper part) vector. +// - filter_state_low : Updated state (lower part) vector. +// - out_vector : Vector containing the upper part of the +// filtered values. +// - out_vector_low : Vector containing the lower part of the +// filtered values. +// +// Return value : Number of samples in the `out_vector`. +// + +// +// WebRtcSpl_ComplexIFFT(...) +// +// Complex Inverse FFT +// +// Computes an inverse complex 2^`stages`-point FFT on the input vector, which +// is in bit-reversed order. The original content of the vector is destroyed in +// the process, since the input is overwritten by the output, normal-ordered, +// FFT vector. With X as the input complex vector, y as the output complex +// vector and with M = 2^`stages`, the following is computed: +// +// M-1 +// y(k) = sum[X(i)*[cos(2*pi*i*k/M) + j*sin(2*pi*i*k/M)]] +// i=0 +// +// The implementations are optimized for speed, not for code size. It uses the +// decimation-in-time algorithm with radix-2 butterfly technique. +// +// Input: +// - vector : In pointer to complex vector containing 2^`stages` +// real elements interleaved with 2^`stages` imaginary +// elements. +// [ReImReImReIm....] +// The elements are in Q(-scale) domain, see more on Return +// Value below. +// +// - stages : Number of FFT stages. Must be at least 3 and at most 10, +// since the table WebRtcSpl_kSinTable1024[] is 1024 +// elements long. +// +// - mode : This parameter gives the user to choose how the FFT +// should work. +// mode==0: Low-complexity and Low-accuracy mode +// mode==1: High-complexity and High-accuracy mode +// +// Output: +// - vector : Out pointer to the FFT vector (the same as input). +// +// Return Value : The scale value that tells the number of left bit shifts +// that the elements in the `vector` should be shifted with +// in order to get Q0 values, i.e. the physically correct +// values. The scale parameter is always 0 or positive, +// except if N>1024 (`stages`>10), which returns a scale +// value of -1, indicating error. +// + +// +// WebRtcSpl_ComplexFFT(...) +// +// Complex FFT +// +// Computes a complex 2^`stages`-point FFT on the input vector, which is in +// bit-reversed order. The original content of the vector is destroyed in +// the process, since the input is overwritten by the output, normal-ordered, +// FFT vector. With x as the input complex vector, Y as the output complex +// vector and with M = 2^`stages`, the following is computed: +// +// M-1 +// Y(k) = 1/M * sum[x(i)*[cos(2*pi*i*k/M) + j*sin(2*pi*i*k/M)]] +// i=0 +// +// The implementations are optimized for speed, not for code size. It uses the +// decimation-in-time algorithm with radix-2 butterfly technique. +// +// This routine prevents overflow by scaling by 2 before each FFT stage. This is +// a fixed scaling, for proper normalization - there will be log2(n) passes, so +// this results in an overall factor of 1/n, distributed to maximize arithmetic +// accuracy. +// +// Input: +// - vector : In pointer to complex vector containing 2^`stages` real +// elements interleaved with 2^`stages` imaginary elements. +// [ReImReImReIm....] +// The output is in the Q0 domain. +// +// - stages : Number of FFT stages. Must be at least 3 and at most 10, +// since the table WebRtcSpl_kSinTable1024[] is 1024 +// elements long. +// +// - mode : This parameter gives the user to choose how the FFT +// should work. +// mode==0: Low-complexity and Low-accuracy mode +// mode==1: High-complexity and High-accuracy mode +// +// Output: +// - vector : The output FFT vector is in the Q0 domain. +// +// Return value : The scale parameter is always 0, except if N>1024, +// which returns a scale value of -1, indicating error. +// + +// +// WebRtcSpl_AnalysisQMF(...) +// +// Splits a 0-2*F Hz signal into two sub bands: 0-F Hz and F-2*F Hz. The +// current version has F = 8000, therefore, a super-wideband audio signal is +// split to lower-band 0-8 kHz and upper-band 8-16 kHz. +// +// Input: +// - in_data : Wide band speech signal, 320 samples (10 ms) +// +// Input & Output: +// - filter_state1 : Filter state for first All-pass filter +// - filter_state2 : Filter state for second All-pass filter +// +// Output: +// - low_band : Lower-band signal 0-8 kHz band, 160 samples (10 ms) +// - high_band : Upper-band signal 8-16 kHz band (flipped in frequency +// domain), 160 samples (10 ms) +// + +// +// WebRtcSpl_SynthesisQMF(...) +// +// Combines the two sub bands (0-F and F-2*F Hz) into a signal of 0-2*F +// Hz, (current version has F = 8000 Hz). So the filter combines lower-band +// (0-8 kHz) and upper-band (8-16 kHz) channels to obtain super-wideband 0-16 +// kHz audio. +// +// Input: +// - low_band : The signal with the 0-8 kHz band, 160 samples (10 ms) +// - high_band : The signal with the 8-16 kHz band, 160 samples (10 ms) +// +// Input & Output: +// - filter_state1 : Filter state for first All-pass filter +// - filter_state2 : Filter state for second All-pass filter +// +// Output: +// - out_data : Super-wideband speech signal, 0-16 kHz +// + +// int16_t WebRtcSpl_SatW32ToW16(...) +// +// This function saturates a 32-bit word into a 16-bit word. +// +// Input: +// - value32 : The value of a 32-bit word. +// +// Output: +// - out16 : the saturated 16-bit word. +// + +// int32_t WebRtc_MulAccumW16(...) +// +// This function multiply a 16-bit word by a 16-bit word, and accumulate this +// value to a 32-bit integer. +// +// Input: +// - a : The value of the first 16-bit word. +// - b : The value of the second 16-bit word. +// - c : The value of an 32-bit integer. +// +// Return Value: The value of a * b + c. +// diff --git a/pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl.h b/pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl.h new file mode 100644 index 00000000..2b099588 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This header file includes the inline functions in +// the fix point signal processing library. + +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_H_ + +#include + +#include "rtc_base/compile_assert_c.h" + +extern const int8_t kWebRtcSpl_CountLeadingZeros32_Table[64]; + +// Don't call this directly except in tests! +static __inline int WebRtcSpl_CountLeadingZeros32_NotBuiltin(uint32_t n) { + // Normalize n by rounding up to the nearest number that is a sequence of 0 + // bits followed by a sequence of 1 bits. This number has the same number of + // leading zeros as the original n. There are exactly 33 such values. + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + + // Multiply the modified n with a constant selected (by exhaustive search) + // such that each of the 33 possible values of n give a product whose 6 most + // significant bits are unique. Then look up the answer in the table. + return kWebRtcSpl_CountLeadingZeros32_Table[(n * 0x8c0b2891) >> 26]; +} + +// Don't call this directly except in tests! +static __inline int WebRtcSpl_CountLeadingZeros64_NotBuiltin(uint64_t n) { + const int leading_zeros = n >> 32 == 0 ? 32 : 0; + return leading_zeros + WebRtcSpl_CountLeadingZeros32_NotBuiltin( + (uint32_t)(n >> (32 - leading_zeros))); +} + +// Returns the number of leading zero bits in the argument. +static __inline int WebRtcSpl_CountLeadingZeros32(uint32_t n) { +#ifdef __GNUC__ + RTC_COMPILE_ASSERT(sizeof(unsigned int) == sizeof(uint32_t)); + return n == 0 ? 32 : __builtin_clz(n); +#else + return WebRtcSpl_CountLeadingZeros32_NotBuiltin(n); +#endif +} + +// Returns the number of leading zero bits in the argument. +static __inline int WebRtcSpl_CountLeadingZeros64(uint64_t n) { +#ifdef __GNUC__ + RTC_COMPILE_ASSERT(sizeof(unsigned long long) == sizeof(uint64_t)); // NOLINT + return n == 0 ? 64 : __builtin_clzll(n); +#else + return WebRtcSpl_CountLeadingZeros64_NotBuiltin(n); +#endif +} + +#ifdef WEBRTC_ARCH_ARM_V7 +#include "common_audio/signal_processing/include/spl_inl_armv7.h" +#else + +#if defined(MIPS32_LE) +#include "common_audio/signal_processing/include/spl_inl_mips.h" +#endif + +#if !defined(MIPS_DSP_R1_LE) +static __inline int16_t WebRtcSpl_SatW32ToW16(int32_t value32) { + int16_t out16 = (int16_t)value32; + + if (value32 > 32767) + out16 = 32767; + else if (value32 < -32768) + out16 = -32768; + + return out16; +} + +static __inline int32_t WebRtcSpl_AddSatW32(int32_t a, int32_t b) { + // Do the addition in unsigned numbers, since signed overflow is undefined + // behavior. + const int32_t sum = (int32_t)((uint32_t)a + (uint32_t)b); + + // a + b can't overflow if a and b have different signs. If they have the + // same sign, a + b also has the same sign iff it didn't overflow. + if ((a < 0) == (b < 0) && (a < 0) != (sum < 0)) { + // The direction of the overflow is obvious from the sign of a + b. + return sum < 0 ? INT32_MAX : INT32_MIN; + } + return sum; +} + +static __inline int32_t WebRtcSpl_SubSatW32(int32_t a, int32_t b) { + // Do the subtraction in unsigned numbers, since signed overflow is undefined + // behavior. + const int32_t diff = (int32_t)((uint32_t)a - (uint32_t)b); + + // a - b can't overflow if a and b have the same sign. If they have different + // signs, a - b has the same sign as a iff it didn't overflow. + if ((a < 0) != (b < 0) && (a < 0) != (diff < 0)) { + // The direction of the overflow is obvious from the sign of a - b. + return diff < 0 ? INT32_MAX : INT32_MIN; + } + return diff; +} + +static __inline int16_t WebRtcSpl_AddSatW16(int16_t a, int16_t b) { + return WebRtcSpl_SatW32ToW16((int32_t)a + (int32_t)b); +} + +static __inline int16_t WebRtcSpl_SubSatW16(int16_t var1, int16_t var2) { + return WebRtcSpl_SatW32ToW16((int32_t)var1 - (int32_t)var2); +} +#endif // #if !defined(MIPS_DSP_R1_LE) + +#if !defined(MIPS32_LE) +static __inline int16_t WebRtcSpl_GetSizeInBits(uint32_t n) { + return 32 - WebRtcSpl_CountLeadingZeros32(n); +} + +// Return the number of steps a can be left-shifted without overflow, +// or 0 if a == 0. +static __inline int16_t WebRtcSpl_NormW32(int32_t a) { + return a == 0 ? 0 : WebRtcSpl_CountLeadingZeros32(a < 0 ? ~a : a) - 1; +} + +// Return the number of steps a can be left-shifted without overflow, +// or 0 if a == 0. +static __inline int16_t WebRtcSpl_NormU32(uint32_t a) { + return a == 0 ? 0 : WebRtcSpl_CountLeadingZeros32(a); +} + +// Return the number of steps a can be left-shifted without overflow, +// or 0 if a == 0. +static __inline int16_t WebRtcSpl_NormW16(int16_t a) { + const int32_t a32 = a; + return a == 0 ? 0 : WebRtcSpl_CountLeadingZeros32(a < 0 ? ~a32 : a32) - 17; +} + +static __inline int32_t WebRtc_MulAccumW16(int16_t a, int16_t b, int32_t c) { + return (a * b + c); +} +#endif // #if !defined(MIPS32_LE) + +#endif // WEBRTC_ARCH_ARM_V7 + +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_H_ diff --git a/pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h b/pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h new file mode 100644 index 00000000..6fc3e7c1 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h @@ -0,0 +1,138 @@ +/* + * 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. + */ + +/* This header file includes the inline functions for ARM processors in + * the fix point signal processing library. + */ + +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_ARMV7_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_ARMV7_H_ + +#include + +/* TODO(kma): Replace some assembly code with GCC intrinsics + * (e.g. __builtin_clz). + */ + +/* This function produces result that is not bit exact with that by the generic + * C version in some cases, although the former is at least as accurate as the + * later. + */ +static __inline int32_t WEBRTC_SPL_MUL_16_32_RSFT16(int16_t a, int32_t b) { + int32_t tmp = 0; + __asm __volatile("smulwb %0, %1, %2" : "=r"(tmp) : "r"(b), "r"(a)); + return tmp; +} + +static __inline int32_t WEBRTC_SPL_MUL_16_16(int16_t a, int16_t b) { + int32_t tmp = 0; + __asm __volatile("smulbb %0, %1, %2" : "=r"(tmp) : "r"(a), "r"(b)); + return tmp; +} + +// TODO(kma): add unit test. +static __inline int32_t WebRtc_MulAccumW16(int16_t a, int16_t b, int32_t c) { + int32_t tmp = 0; + __asm __volatile("smlabb %0, %1, %2, %3" + : "=r"(tmp) + : "r"(a), "r"(b), "r"(c)); + return tmp; +} + +static __inline int16_t WebRtcSpl_AddSatW16(int16_t a, int16_t b) { + int32_t s_sum = 0; + + __asm __volatile("qadd16 %0, %1, %2" : "=r"(s_sum) : "r"(a), "r"(b)); + + return (int16_t)s_sum; +} + +static __inline int32_t WebRtcSpl_AddSatW32(int32_t l_var1, int32_t l_var2) { + int32_t l_sum = 0; + + __asm __volatile("qadd %0, %1, %2" : "=r"(l_sum) : "r"(l_var1), "r"(l_var2)); + + return l_sum; +} + +static __inline int32_t WebRtcSpl_SubSatW32(int32_t l_var1, int32_t l_var2) { + int32_t l_sub = 0; + + __asm __volatile("qsub %0, %1, %2" : "=r"(l_sub) : "r"(l_var1), "r"(l_var2)); + + return l_sub; +} + +static __inline int16_t WebRtcSpl_SubSatW16(int16_t var1, int16_t var2) { + int32_t s_sub = 0; + + __asm __volatile("qsub16 %0, %1, %2" : "=r"(s_sub) : "r"(var1), "r"(var2)); + + return (int16_t)s_sub; +} + +static __inline int16_t WebRtcSpl_GetSizeInBits(uint32_t n) { + int32_t tmp = 0; + + __asm __volatile("clz %0, %1" : "=r"(tmp) : "r"(n)); + + return (int16_t)(32 - tmp); +} + +static __inline int16_t WebRtcSpl_NormW32(int32_t a) { + int32_t tmp = 0; + + if (a == 0) { + return 0; + } else if (a < 0) { + a ^= 0xFFFFFFFF; + } + + __asm __volatile("clz %0, %1" : "=r"(tmp) : "r"(a)); + + return (int16_t)(tmp - 1); +} + +static __inline int16_t WebRtcSpl_NormU32(uint32_t a) { + int tmp = 0; + + if (a == 0) + return 0; + + __asm __volatile("clz %0, %1" : "=r"(tmp) : "r"(a)); + + return (int16_t)tmp; +} + +static __inline int16_t WebRtcSpl_NormW16(int16_t a) { + int32_t tmp = 0; + int32_t a_32 = a; + + if (a_32 == 0) { + return 0; + } else if (a_32 < 0) { + a_32 ^= 0xFFFFFFFF; + } + + __asm __volatile("clz %0, %1" : "=r"(tmp) : "r"(a_32)); + + return (int16_t)(tmp - 17); +} + +// TODO(kma): add unit test. +static __inline int16_t WebRtcSpl_SatW32ToW16(int32_t value32) { + int32_t out = 0; + + __asm __volatile("ssat %0, #16, %1" : "=r"(out) : "r"(value32)); + + return (int16_t)out; +} + +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_ARMV7_H_ diff --git a/pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl_mips.h b/pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl_mips.h new file mode 100644 index 00000000..1db95e82 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/include/spl_inl_mips.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This header file includes the inline functions in +// the fix point signal processing library. + +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_MIPS_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_MIPS_H_ + +static __inline int32_t WEBRTC_SPL_MUL_16_16(int32_t a, int32_t b) { + int32_t value32 = 0; + int32_t a1 = 0, b1 = 0; + + __asm __volatile( +#if defined(MIPS32_R2_LE) + "seh %[a1], %[a] \n\t" + "seh %[b1], %[b] \n\t" +#else + "sll %[a1], %[a], 16 \n\t" + "sll %[b1], %[b], 16 \n\t" + "sra %[a1], %[a1], 16 \n\t" + "sra %[b1], %[b1], 16 \n\t" +#endif + "mul %[value32], %[a1], %[b1] \n\t" + : [value32] "=r"(value32), [a1] "=&r"(a1), [b1] "=&r"(b1) + : [a] "r"(a), [b] "r"(b) + : "hi", "lo"); + return value32; +} + +static __inline int32_t WEBRTC_SPL_MUL_16_32_RSFT16(int16_t a, int32_t b) { + int32_t value32 = 0, b1 = 0, b2 = 0; + int32_t a1 = 0; + + __asm __volatile( +#if defined(MIPS32_R2_LE) + "seh %[a1], %[a] \n\t" +#else + "sll %[a1], %[a], 16 \n\t" + "sra %[a1], %[a1], 16 \n\t" +#endif + "andi %[b2], %[b], 0xFFFF \n\t" + "sra %[b1], %[b], 16 \n\t" + "sra %[b2], %[b2], 1 \n\t" + "mul %[value32], %[a1], %[b1] \n\t" + "mul %[b2], %[a1], %[b2] \n\t" + "addiu %[b2], %[b2], 0x4000 \n\t" + "sra %[b2], %[b2], 15 \n\t" + "addu %[value32], %[value32], %[b2] \n\t" + : [value32] "=&r"(value32), [b1] "=&r"(b1), [b2] "=&r"(b2), [a1] "=&r"(a1) + : [a] "r"(a), [b] "r"(b) + : "hi", "lo"); + return value32; +} + +#if defined(MIPS_DSP_R1_LE) +static __inline int16_t WebRtcSpl_SatW32ToW16(int32_t value32) { + __asm __volatile( + "shll_s.w %[value32], %[value32], 16 \n\t" + "sra %[value32], %[value32], 16 \n\t" + : [value32] "+r"(value32) + :); + int16_t out16 = (int16_t)value32; + return out16; +} + +static __inline int16_t WebRtcSpl_AddSatW16(int16_t a, int16_t b) { + int32_t value32 = 0; + + __asm __volatile("addq_s.ph %[value32], %[a], %[b] \n\t" + : [value32] "=r"(value32) + : [a] "r"(a), [b] "r"(b)); + return (int16_t)value32; +} + +static __inline int32_t WebRtcSpl_AddSatW32(int32_t l_var1, int32_t l_var2) { + int32_t l_sum; + + __asm __volatile( + "addq_s.w %[l_sum], %[l_var1], %[l_var2] \n\t" + : [l_sum] "=r"(l_sum) + : [l_var1] "r"(l_var1), [l_var2] "r"(l_var2)); + + return l_sum; +} + +static __inline int16_t WebRtcSpl_SubSatW16(int16_t var1, int16_t var2) { + int32_t value32; + + __asm __volatile("subq_s.ph %[value32], %[var1], %[var2] \n\t" + : [value32] "=r"(value32) + : [var1] "r"(var1), [var2] "r"(var2)); + + return (int16_t)value32; +} + +static __inline int32_t WebRtcSpl_SubSatW32(int32_t l_var1, int32_t l_var2) { + int32_t l_diff; + + __asm __volatile( + "subq_s.w %[l_diff], %[l_var1], %[l_var2] \n\t" + : [l_diff] "=r"(l_diff) + : [l_var1] "r"(l_var1), [l_var2] "r"(l_var2)); + + return l_diff; +} +#endif + +static __inline int16_t WebRtcSpl_GetSizeInBits(uint32_t n) { + int bits = 0; + int i32 = 32; + + __asm __volatile( + "clz %[bits], %[n] \n\t" + "subu %[bits], %[i32], %[bits] \n\t" + : [bits] "=&r"(bits) + : [n] "r"(n), [i32] "r"(i32)); + + return (int16_t)bits; +} + +static __inline int16_t WebRtcSpl_NormW32(int32_t a) { + int zeros = 0; + + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "bnez %[a], 1f \n\t" + " sra %[zeros], %[a], 31 \n\t" + "b 2f \n\t" + " move %[zeros], $zero \n\t" + "1: \n\t" + "xor %[zeros], %[a], %[zeros] \n\t" + "clz %[zeros], %[zeros] \n\t" + "addiu %[zeros], %[zeros], -1 \n\t" + "2: \n\t" + ".set pop \n\t" + : [zeros] "=&r"(zeros) + : [a] "r"(a)); + + return (int16_t)zeros; +} + +static __inline int16_t WebRtcSpl_NormU32(uint32_t a) { + int zeros = 0; + + __asm __volatile("clz %[zeros], %[a] \n\t" + : [zeros] "=r"(zeros) + : [a] "r"(a)); + + return (int16_t)(zeros & 0x1f); +} + +static __inline int16_t WebRtcSpl_NormW16(int16_t a) { + int zeros = 0; + int a0 = a << 16; + + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "bnez %[a0], 1f \n\t" + " sra %[zeros], %[a0], 31 \n\t" + "b 2f \n\t" + " move %[zeros], $zero \n\t" + "1: \n\t" + "xor %[zeros], %[a0], %[zeros] \n\t" + "clz %[zeros], %[zeros] \n\t" + "addiu %[zeros], %[zeros], -1 \n\t" + "2: \n\t" + ".set pop \n\t" + : [zeros] "=&r"(zeros) + : [a0] "r"(a0)); + + return (int16_t)zeros; +} + +static __inline int32_t WebRtc_MulAccumW16(int16_t a, int16_t b, int32_t c) { + int32_t res = 0, c1 = 0; + __asm __volatile( +#if defined(MIPS32_R2_LE) + "seh %[a], %[a] \n\t" + "seh %[b], %[b] \n\t" +#else + "sll %[a], %[a], 16 \n\t" + "sll %[b], %[b], 16 \n\t" + "sra %[a], %[a], 16 \n\t" + "sra %[b], %[b], 16 \n\t" +#endif + "mul %[res], %[a], %[b] \n\t" + "addu %[c1], %[c], %[res] \n\t" + : [c1] "=r"(c1), [res] "=&r"(res) + : [a] "r"(a), [b] "r"(b), [c] "r"(c) + : "hi", "lo"); + return (c1); +} + +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_MIPS_H_ diff --git a/pkg/apm/webrtc/common_audio/signal_processing/levinson_durbin.c b/pkg/apm/webrtc/common_audio/signal_processing/levinson_durbin.c new file mode 100644 index 00000000..7ed16858 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/levinson_durbin.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_LevinsonDurbin(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/sanitizer.h" + +#define SPL_LEVINSON_MAXORDER 20 + +int16_t RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/5486 + WebRtcSpl_LevinsonDurbin(const int32_t* R, + int16_t* A, + int16_t* K, + size_t order) { + size_t i, j; + // Auto-correlation coefficients in high precision + int16_t R_hi[SPL_LEVINSON_MAXORDER + 1], R_low[SPL_LEVINSON_MAXORDER + 1]; + // LPC coefficients in high precision + int16_t A_hi[SPL_LEVINSON_MAXORDER + 1], A_low[SPL_LEVINSON_MAXORDER + 1]; + // LPC coefficients for next iteration + int16_t A_upd_hi[SPL_LEVINSON_MAXORDER + 1], + A_upd_low[SPL_LEVINSON_MAXORDER + 1]; + // Reflection coefficient in high precision + int16_t K_hi, K_low; + // Prediction gain Alpha in high precision and with scale factor + int16_t Alpha_hi, Alpha_low, Alpha_exp; + int16_t tmp_hi, tmp_low; + int32_t temp1W32, temp2W32, temp3W32; + int16_t norm; + + // Normalize the autocorrelation R[0]...R[order+1] + + norm = WebRtcSpl_NormW32(R[0]); + + for (i = 0; i <= order; ++i) { + temp1W32 = R[i] * (1 << norm); + // UBSan: 12 * 268435456 cannot be represented in type 'int' + + // Put R in hi and low format + R_hi[i] = (int16_t)(temp1W32 >> 16); + R_low[i] = (int16_t)((temp1W32 - ((int32_t)R_hi[i] * 65536)) >> 1); + } + + // K = A[1] = -R[1] / R[0] + + temp2W32 = R[1] * (1 << norm); // R[1] in Q31 + temp3W32 = WEBRTC_SPL_ABS_W32(temp2W32); // abs R[1] + temp1W32 = WebRtcSpl_DivW32HiLow(temp3W32, R_hi[0], + R_low[0]); // abs(R[1])/R[0] in Q31 + // Put back the sign on R[1] + if (temp2W32 > 0) { + temp1W32 = -temp1W32; + } + + // Put K in hi and low format + K_hi = (int16_t)(temp1W32 >> 16); + K_low = (int16_t)((temp1W32 - ((int32_t)K_hi * 65536)) >> 1); + + // Store first reflection coefficient + K[0] = K_hi; + + temp1W32 >>= 4; // A[1] in Q27. + + // Put A[1] in hi and low format + A_hi[1] = (int16_t)(temp1W32 >> 16); + A_low[1] = (int16_t)((temp1W32 - ((int32_t)A_hi[1] * 65536)) >> 1); + + // Alpha = R[0] * (1-K^2) + + temp1W32 = ((K_hi * K_low >> 14) + K_hi * K_hi) * 2; // = k^2 in Q31 + + temp1W32 = WEBRTC_SPL_ABS_W32(temp1W32); // Guard against <0 + temp1W32 = + (int32_t)0x7fffffffL - temp1W32; // temp1W32 = (1 - K[0]*K[0]) in Q31 + + // Store temp1W32 = 1 - K[0]*K[0] on hi and low format + tmp_hi = (int16_t)(temp1W32 >> 16); + tmp_low = (int16_t)((temp1W32 - ((int32_t)tmp_hi << 16)) >> 1); + + // Calculate Alpha in Q31 + temp1W32 = + (R_hi[0] * tmp_hi + (R_hi[0] * tmp_low >> 15) + (R_low[0] * tmp_hi >> 15)) + << 1; + + // Normalize Alpha and put it in hi and low format + + Alpha_exp = WebRtcSpl_NormW32(temp1W32); + temp1W32 = WEBRTC_SPL_LSHIFT_W32(temp1W32, Alpha_exp); + Alpha_hi = (int16_t)(temp1W32 >> 16); + Alpha_low = (int16_t)((temp1W32 - ((int32_t)Alpha_hi << 16)) >> 1); + + // Perform the iterative calculations in the Levinson-Durbin algorithm + + for (i = 2; i <= order; i++) { + /* ---- + temp1W32 = R[i] + > R[j]*A[i-j] + / + ---- + j=1..i-1 + */ + + temp1W32 = 0; + + for (j = 1; j < i; j++) { + // temp1W32 is in Q31 + temp1W32 += + (R_hi[j] * A_hi[i - j] * 2) + + (((R_hi[j] * A_low[i - j] >> 15) + (R_low[j] * A_hi[i - j] >> 15)) * + 2); + } + + temp1W32 = temp1W32 * 16; + temp1W32 += ((int32_t)R_hi[i] * 65536) + + WEBRTC_SPL_LSHIFT_W32((int32_t)R_low[i], 1); + + // K = -temp1W32 / Alpha + temp2W32 = WEBRTC_SPL_ABS_W32(temp1W32); // abs(temp1W32) + temp3W32 = WebRtcSpl_DivW32HiLow(temp2W32, Alpha_hi, + Alpha_low); // abs(temp1W32)/Alpha + + // Put the sign of temp1W32 back again + if (temp1W32 > 0) { + temp3W32 = -temp3W32; + } + + // Use the Alpha shifts from earlier to de-normalize + norm = WebRtcSpl_NormW32(temp3W32); + if ((Alpha_exp <= norm) || (temp3W32 == 0)) { + temp3W32 = temp3W32 * (1 << Alpha_exp); + } else { + if (temp3W32 > 0) { + temp3W32 = (int32_t)0x7fffffffL; + } else { + temp3W32 = (int32_t)0x80000000L; + } + } + + // Put K on hi and low format + K_hi = (int16_t)(temp3W32 >> 16); + K_low = (int16_t)((temp3W32 - ((int32_t)K_hi * 65536)) >> 1); + + // Store Reflection coefficient in Q15 + K[i - 1] = K_hi; + + // Test for unstable filter. + // If unstable return 0 and let the user decide what to do in that case + + if ((int32_t)WEBRTC_SPL_ABS_W16(K_hi) > (int32_t)32750) { + return 0; // Unstable filter + } + + /* + Compute updated LPC coefficient: Anew[i] + Anew[j]= A[j] + K*A[i-j] for j=1..i-1 + Anew[i]= K + */ + + for (j = 1; j < i; j++) { + // temp1W32 = A[j] in Q27 + temp1W32 = (int32_t)A_hi[j] * 65536 + + WEBRTC_SPL_LSHIFT_W32((int32_t)A_low[j], 1); + + // temp1W32 += K*A[i-j] in Q27 + temp1W32 += (K_hi * A_hi[i - j] + (K_hi * A_low[i - j] >> 15) + + (K_low * A_hi[i - j] >> 15)) * + 2; + + // Put Anew in hi and low format + A_upd_hi[j] = (int16_t)(temp1W32 >> 16); + A_upd_low[j] = + (int16_t)((temp1W32 - ((int32_t)A_upd_hi[j] * 65536)) >> 1); + } + + // temp3W32 = K in Q27 (Convert from Q31 to Q27) + temp3W32 >>= 4; + + // Store Anew in hi and low format + A_upd_hi[i] = (int16_t)(temp3W32 >> 16); + A_upd_low[i] = (int16_t)((temp3W32 - ((int32_t)A_upd_hi[i] * 65536)) >> 1); + + // Alpha = Alpha * (1-K^2) + + temp1W32 = ((K_hi * K_low >> 14) + K_hi * K_hi) * 2; // K*K in Q31 + + temp1W32 = WEBRTC_SPL_ABS_W32(temp1W32); // Guard against <0 + temp1W32 = (int32_t)0x7fffffffL - temp1W32; // 1 - K*K in Q31 + + // Convert 1- K^2 in hi and low format + tmp_hi = (int16_t)(temp1W32 >> 16); + tmp_low = (int16_t)((temp1W32 - ((int32_t)tmp_hi << 16)) >> 1); + + // Calculate Alpha = Alpha * (1-K^2) in Q31 + temp1W32 = (Alpha_hi * tmp_hi + (Alpha_hi * tmp_low >> 15) + + (Alpha_low * tmp_hi >> 15)) + << 1; + + // Normalize Alpha and store it on hi and low format + + norm = WebRtcSpl_NormW32(temp1W32); + temp1W32 = WEBRTC_SPL_LSHIFT_W32(temp1W32, norm); + + Alpha_hi = (int16_t)(temp1W32 >> 16); + Alpha_low = (int16_t)((temp1W32 - ((int32_t)Alpha_hi << 16)) >> 1); + + // Update the total normalization of Alpha + Alpha_exp = Alpha_exp + norm; + + // Update A[] + + for (j = 1; j <= i; j++) { + A_hi[j] = A_upd_hi[j]; + A_low[j] = A_upd_low[j]; + } + } + + /* + Set A[0] to 1.0 and store the A[i] i=1...order in Q12 + (Convert from Q27 and use rounding) + */ + + A[0] = 4096; + + for (i = 1; i <= order; i++) { + // temp1W32 in Q27 + temp1W32 = + (int32_t)A_hi[i] * 65536 + WEBRTC_SPL_LSHIFT_W32((int32_t)A_low[i], 1); + // Round and store upper word + A[i] = (int16_t)(((temp1W32 * 2) + 32768) >> 16); + } + return 1; // Stable filters +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c b/pkg/apm/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c new file mode 100644 index 00000000..2a7c35ea --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_LpcToReflCoef(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +#define SPL_LPC_TO_REFL_COEF_MAX_AR_MODEL_ORDER 50 + +void WebRtcSpl_LpcToReflCoef(int16_t* a16, int use_order, int16_t* k16) { + int m, k; + int32_t tmp32[SPL_LPC_TO_REFL_COEF_MAX_AR_MODEL_ORDER]; + int32_t tmp_inv_denom32; + int16_t tmp_inv_denom16; + + k16[use_order - 1] = a16[use_order] << 3; // Q12<<3 => Q15 + for (m = use_order - 1; m > 0; m--) { + // (1 - k^2) in Q30 + tmp_inv_denom32 = 1073741823 - k16[m] * k16[m]; + // (1 - k^2) in Q15 + tmp_inv_denom16 = (int16_t)(tmp_inv_denom32 >> 15); + + for (k = 1; k <= m; k++) { + // tmp[k] = (a[k] - RC[m] * a[m-k+1]) / (1.0 - RC[m]*RC[m]); + + // [Q12<<16 - (Q15*Q12)<<1] = [Q28 - Q28] = Q28 + tmp32[k] = (a16[k] << 16) - (k16[m] * a16[m - k + 1] << 1); + + tmp32[k] = + WebRtcSpl_DivW32W16(tmp32[k], tmp_inv_denom16); // Q28/Q15 = Q13 + } + + for (k = 1; k < m; k++) { + a16[k] = (int16_t)(tmp32[k] >> 1); // Q13>>1 => Q12 + } + + tmp32[m] = WEBRTC_SPL_SAT(8191, tmp32[m], -8191); + k16[m - 1] = (int16_t)WEBRTC_SPL_LSHIFT_W32(tmp32[m], 2); // Q13<<2 => Q15 + } + return; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/min_max_operations.c b/pkg/apm/webrtc/common_audio/signal_processing/min_max_operations.c new file mode 100644 index 00000000..2a7c8264 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/min_max_operations.c @@ -0,0 +1,260 @@ +/* + * 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. + */ + +/* + * This file contains the implementation of functions + * WebRtcSpl_MaxAbsValueW16C() + * WebRtcSpl_MaxAbsValueW32C() + * WebRtcSpl_MaxValueW16C() + * WebRtcSpl_MaxValueW32C() + * WebRtcSpl_MinValueW16C() + * WebRtcSpl_MinValueW32C() + * WebRtcSpl_MaxAbsIndexW16() + * WebRtcSpl_MaxIndexW16() + * WebRtcSpl_MaxIndexW32() + * WebRtcSpl_MinIndexW16() + * WebRtcSpl_MinIndexW32() + * + */ + +#include +#include + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" + +// TODO(bjorn/kma): Consolidate function pairs (e.g. combine +// WebRtcSpl_MaxAbsValueW16C and WebRtcSpl_MaxAbsIndexW16 into a single one.) +// TODO(kma): Move the next six functions into min_max_operations_c.c. + +// Maximum absolute value of word16 vector. C version for generic platforms. +int16_t WebRtcSpl_MaxAbsValueW16C(const int16_t* vector, size_t length) { + size_t i = 0; + int absolute = 0, maximum = 0; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + absolute = abs((int)vector[i]); + + if (absolute > maximum) { + maximum = absolute; + } + } + + // Guard the case for abs(-32768). + if (maximum > WEBRTC_SPL_WORD16_MAX) { + maximum = WEBRTC_SPL_WORD16_MAX; + } + + return (int16_t)maximum; +} + +// Maximum absolute value of word32 vector. C version for generic platforms. +int32_t WebRtcSpl_MaxAbsValueW32C(const int32_t* vector, size_t length) { + // Use uint32_t for the local variables, to accommodate the return value + // of abs(0x80000000), which is 0x80000000. + + uint32_t absolute = 0, maximum = 0; + size_t i = 0; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + absolute = + (vector[i] != INT_MIN) ? abs((int)vector[i]) : INT_MAX + (uint32_t)1; + if (absolute > maximum) { + maximum = absolute; + } + } + + maximum = WEBRTC_SPL_MIN(maximum, WEBRTC_SPL_WORD32_MAX); + + return (int32_t)maximum; +} + +// Maximum value of word16 vector. C version for generic platforms. +int16_t WebRtcSpl_MaxValueW16C(const int16_t* vector, size_t length) { + int16_t maximum = WEBRTC_SPL_WORD16_MIN; + size_t i = 0; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + if (vector[i] > maximum) + maximum = vector[i]; + } + return maximum; +} + +// Maximum value of word32 vector. C version for generic platforms. +int32_t WebRtcSpl_MaxValueW32C(const int32_t* vector, size_t length) { + int32_t maximum = WEBRTC_SPL_WORD32_MIN; + size_t i = 0; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + if (vector[i] > maximum) + maximum = vector[i]; + } + return maximum; +} + +// Minimum value of word16 vector. C version for generic platforms. +int16_t WebRtcSpl_MinValueW16C(const int16_t* vector, size_t length) { + int16_t minimum = WEBRTC_SPL_WORD16_MAX; + size_t i = 0; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + if (vector[i] < minimum) + minimum = vector[i]; + } + return minimum; +} + +// Minimum value of word32 vector. C version for generic platforms. +int32_t WebRtcSpl_MinValueW32C(const int32_t* vector, size_t length) { + int32_t minimum = WEBRTC_SPL_WORD32_MAX; + size_t i = 0; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + if (vector[i] < minimum) + minimum = vector[i]; + } + return minimum; +} + +// Index of maximum absolute value in a word16 vector. +size_t WebRtcSpl_MaxAbsIndexW16(const int16_t* vector, size_t length) { + // Use type int for local variables, to accomodate the value of abs(-32768). + + size_t i = 0, index = 0; + int absolute = 0, maximum = 0; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + absolute = abs((int)vector[i]); + + if (absolute > maximum) { + maximum = absolute; + index = i; + } + } + + return index; +} + +int16_t WebRtcSpl_MaxAbsElementW16(const int16_t* vector, size_t length) { + int16_t min_val, max_val; + WebRtcSpl_MinMaxW16(vector, length, &min_val, &max_val); + if (min_val == max_val || min_val < -max_val) { + return min_val; + } + return max_val; +} + +// Index of maximum value in a word16 vector. +size_t WebRtcSpl_MaxIndexW16(const int16_t* vector, size_t length) { + size_t i = 0, index = 0; + int16_t maximum = WEBRTC_SPL_WORD16_MIN; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + if (vector[i] > maximum) { + maximum = vector[i]; + index = i; + } + } + + return index; +} + +// Index of maximum value in a word32 vector. +size_t WebRtcSpl_MaxIndexW32(const int32_t* vector, size_t length) { + size_t i = 0, index = 0; + int32_t maximum = WEBRTC_SPL_WORD32_MIN; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + if (vector[i] > maximum) { + maximum = vector[i]; + index = i; + } + } + + return index; +} + +// Index of minimum value in a word16 vector. +size_t WebRtcSpl_MinIndexW16(const int16_t* vector, size_t length) { + size_t i = 0, index = 0; + int16_t minimum = WEBRTC_SPL_WORD16_MAX; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + if (vector[i] < minimum) { + minimum = vector[i]; + index = i; + } + } + + return index; +} + +// Index of minimum value in a word32 vector. +size_t WebRtcSpl_MinIndexW32(const int32_t* vector, size_t length) { + size_t i = 0, index = 0; + int32_t minimum = WEBRTC_SPL_WORD32_MAX; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + if (vector[i] < minimum) { + minimum = vector[i]; + index = i; + } + } + + return index; +} + +// Finds both the minimum and maximum elements in an array of 16-bit integers. +void WebRtcSpl_MinMaxW16(const int16_t* vector, + size_t length, + int16_t* min_val, + int16_t* max_val) { +#if defined(WEBRTC_HAS_NEON) + return WebRtcSpl_MinMaxW16Neon(vector, length, min_val, max_val); +#else + int16_t minimum = WEBRTC_SPL_WORD16_MAX; + int16_t maximum = WEBRTC_SPL_WORD16_MIN; + size_t i = 0; + + RTC_DCHECK_GT(length, 0); + + for (i = 0; i < length; i++) { + if (vector[i] < minimum) + minimum = vector[i]; + if (vector[i] > maximum) + maximum = vector[i]; + } + *min_val = minimum; + *max_val = maximum; +#endif +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/min_max_operations_neon_arm64.c b/pkg/apm/webrtc/common_audio/signal_processing/min_max_operations_neon_arm64.c new file mode 100644 index 00000000..7cc241e2 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/min_max_operations_neon_arm64.c @@ -0,0 +1,335 @@ +/* + * 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 +#include + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" + +// Maximum absolute value of word16 vector. C version for generic platforms. +int16_t WebRtcSpl_MaxAbsValueW16Neon(const int16_t* vector, size_t length) { + int absolute = 0, maximum = 0; + + RTC_DCHECK_GT(length, 0); + + const int16_t* p_start = vector; + size_t rest = length & 7; + const int16_t* p_end = vector + length - rest; + + int16x8_t v; + uint16x8_t max_qv; + max_qv = vdupq_n_u16(0); + + while (p_start < p_end) { + v = vld1q_s16(p_start); + // Note vabs doesn't change the value of -32768. + v = vabsq_s16(v); + // Use u16 so we don't lose the value -32768. + max_qv = vmaxq_u16(max_qv, vreinterpretq_u16_s16(v)); + p_start += 8; + } + +#ifdef WEBRTC_ARCH_ARM64 + maximum = (int)vmaxvq_u16(max_qv); +#else + uint16x4_t max_dv; + max_dv = vmax_u16(vget_low_u16(max_qv), vget_high_u16(max_qv)); + max_dv = vpmax_u16(max_dv, max_dv); + max_dv = vpmax_u16(max_dv, max_dv); + + maximum = (int)vget_lane_u16(max_dv, 0); +#endif + + p_end = vector + length; + while (p_start < p_end) { + absolute = abs((int)(*p_start)); + + if (absolute > maximum) { + maximum = absolute; + } + p_start++; + } + + // Guard the case for abs(-32768). + if (maximum > WEBRTC_SPL_WORD16_MAX) { + maximum = WEBRTC_SPL_WORD16_MAX; + } + + return (int16_t)maximum; +} + +// Maximum absolute value of word32 vector. NEON intrinsics version for +// ARM 32-bit/64-bit platforms. +int32_t WebRtcSpl_MaxAbsValueW32Neon(const int32_t* vector, size_t length) { + // Use uint32_t for the local variables, to accommodate the return value + // of abs(0x80000000), which is 0x80000000. + + uint32_t absolute = 0, maximum = 0; + size_t i = 0; + size_t residual = length & 0x7; + + RTC_DCHECK_GT(length, 0); + + const int32_t* p_start = vector; + uint32x4_t max32x4_0 = vdupq_n_u32(0); + uint32x4_t max32x4_1 = vdupq_n_u32(0); + + // First part, unroll the loop 8 times. + for (i = 0; i < length - residual; i += 8) { + int32x4_t in32x4_0 = vld1q_s32(p_start); + p_start += 4; + int32x4_t in32x4_1 = vld1q_s32(p_start); + p_start += 4; + in32x4_0 = vabsq_s32(in32x4_0); + in32x4_1 = vabsq_s32(in32x4_1); + // vabs doesn't change the value of 0x80000000. + // Use u32 so we don't lose the value 0x80000000. + max32x4_0 = vmaxq_u32(max32x4_0, vreinterpretq_u32_s32(in32x4_0)); + max32x4_1 = vmaxq_u32(max32x4_1, vreinterpretq_u32_s32(in32x4_1)); + } + + uint32x4_t max32x4 = vmaxq_u32(max32x4_0, max32x4_1); +#if defined(WEBRTC_ARCH_ARM64) + maximum = vmaxvq_u32(max32x4); +#else + uint32x2_t max32x2 = vmax_u32(vget_low_u32(max32x4), vget_high_u32(max32x4)); + max32x2 = vpmax_u32(max32x2, max32x2); + + maximum = vget_lane_u32(max32x2, 0); +#endif + + // Second part, do the remaining iterations (if any). + for (i = residual; i > 0; i--) { + absolute = abs((int)(*p_start)); + if (absolute > maximum) { + maximum = absolute; + } + p_start++; + } + + // Guard against the case for 0x80000000. + maximum = WEBRTC_SPL_MIN(maximum, WEBRTC_SPL_WORD32_MAX); + + return (int32_t)maximum; +} + +// Maximum value of word16 vector. NEON intrinsics version for +// ARM 32-bit/64-bit platforms. +int16_t WebRtcSpl_MaxValueW16Neon(const int16_t* vector, size_t length) { + int16_t maximum = WEBRTC_SPL_WORD16_MIN; + size_t i = 0; + size_t residual = length & 0x7; + + RTC_DCHECK_GT(length, 0); + + const int16_t* p_start = vector; + int16x8_t max16x8 = vdupq_n_s16(WEBRTC_SPL_WORD16_MIN); + + // First part, unroll the loop 8 times. + for (i = 0; i < length - residual; i += 8) { + int16x8_t in16x8 = vld1q_s16(p_start); + max16x8 = vmaxq_s16(max16x8, in16x8); + p_start += 8; + } + +#if defined(WEBRTC_ARCH_ARM64) + maximum = vmaxvq_s16(max16x8); +#else + int16x4_t max16x4 = vmax_s16(vget_low_s16(max16x8), vget_high_s16(max16x8)); + max16x4 = vpmax_s16(max16x4, max16x4); + max16x4 = vpmax_s16(max16x4, max16x4); + + maximum = vget_lane_s16(max16x4, 0); +#endif + + // Second part, do the remaining iterations (if any). + for (i = residual; i > 0; i--) { + if (*p_start > maximum) + maximum = *p_start; + p_start++; + } + return maximum; +} + +// Maximum value of word32 vector. NEON intrinsics version for +// ARM 32-bit/64-bit platforms. +int32_t WebRtcSpl_MaxValueW32Neon(const int32_t* vector, size_t length) { + int32_t maximum = WEBRTC_SPL_WORD32_MIN; + size_t i = 0; + size_t residual = length & 0x7; + + RTC_DCHECK_GT(length, 0); + + const int32_t* p_start = vector; + int32x4_t max32x4_0 = vdupq_n_s32(WEBRTC_SPL_WORD32_MIN); + int32x4_t max32x4_1 = vdupq_n_s32(WEBRTC_SPL_WORD32_MIN); + + // First part, unroll the loop 8 times. + for (i = 0; i < length - residual; i += 8) { + int32x4_t in32x4_0 = vld1q_s32(p_start); + p_start += 4; + int32x4_t in32x4_1 = vld1q_s32(p_start); + p_start += 4; + max32x4_0 = vmaxq_s32(max32x4_0, in32x4_0); + max32x4_1 = vmaxq_s32(max32x4_1, in32x4_1); + } + + int32x4_t max32x4 = vmaxq_s32(max32x4_0, max32x4_1); +#if defined(WEBRTC_ARCH_ARM64) + maximum = vmaxvq_s32(max32x4); +#else + int32x2_t max32x2 = vmax_s32(vget_low_s32(max32x4), vget_high_s32(max32x4)); + max32x2 = vpmax_s32(max32x2, max32x2); + + maximum = vget_lane_s32(max32x2, 0); +#endif + + // Second part, do the remaining iterations (if any). + for (i = residual; i > 0; i--) { + if (*p_start > maximum) + maximum = *p_start; + p_start++; + } + return maximum; +} + +// Minimum value of word16 vector. NEON intrinsics version for +// ARM 32-bit/64-bit platforms. +int16_t WebRtcSpl_MinValueW16Neon(const int16_t* vector, size_t length) { + int16_t minimum = WEBRTC_SPL_WORD16_MAX; + size_t i = 0; + size_t residual = length & 0x7; + + RTC_DCHECK_GT(length, 0); + + const int16_t* p_start = vector; + int16x8_t min16x8 = vdupq_n_s16(WEBRTC_SPL_WORD16_MAX); + + // First part, unroll the loop 8 times. + for (i = 0; i < length - residual; i += 8) { + int16x8_t in16x8 = vld1q_s16(p_start); + min16x8 = vminq_s16(min16x8, in16x8); + p_start += 8; + } + +#if defined(WEBRTC_ARCH_ARM64) + minimum = vminvq_s16(min16x8); +#else + int16x4_t min16x4 = vmin_s16(vget_low_s16(min16x8), vget_high_s16(min16x8)); + min16x4 = vpmin_s16(min16x4, min16x4); + min16x4 = vpmin_s16(min16x4, min16x4); + + minimum = vget_lane_s16(min16x4, 0); +#endif + + // Second part, do the remaining iterations (if any). + for (i = residual; i > 0; i--) { + if (*p_start < minimum) + minimum = *p_start; + p_start++; + } + return minimum; +} + +// Minimum value of word32 vector. NEON intrinsics version for +// ARM 32-bit/64-bit platforms. +int32_t WebRtcSpl_MinValueW32Neon(const int32_t* vector, size_t length) { + int32_t minimum = WEBRTC_SPL_WORD32_MAX; + size_t i = 0; + size_t residual = length & 0x7; + + RTC_DCHECK_GT(length, 0); + + const int32_t* p_start = vector; + int32x4_t min32x4_0 = vdupq_n_s32(WEBRTC_SPL_WORD32_MAX); + int32x4_t min32x4_1 = vdupq_n_s32(WEBRTC_SPL_WORD32_MAX); + + // First part, unroll the loop 8 times. + for (i = 0; i < length - residual; i += 8) { + int32x4_t in32x4_0 = vld1q_s32(p_start); + p_start += 4; + int32x4_t in32x4_1 = vld1q_s32(p_start); + p_start += 4; + min32x4_0 = vminq_s32(min32x4_0, in32x4_0); + min32x4_1 = vminq_s32(min32x4_1, in32x4_1); + } + + int32x4_t min32x4 = vminq_s32(min32x4_0, min32x4_1); +#if defined(WEBRTC_ARCH_ARM64) + minimum = vminvq_s32(min32x4); +#else + int32x2_t min32x2 = vmin_s32(vget_low_s32(min32x4), vget_high_s32(min32x4)); + min32x2 = vpmin_s32(min32x2, min32x2); + + minimum = vget_lane_s32(min32x2, 0); +#endif + + // Second part, do the remaining iterations (if any). + for (i = residual; i > 0; i--) { + if (*p_start < minimum) + minimum = *p_start; + p_start++; + } + return minimum; +} + +// Finds both the minimum and maximum elements in an array of 16-bit integers. +void WebRtcSpl_MinMaxW16Neon(const int16_t* vector, + size_t length, + int16_t* min_val, + int16_t* max_val) { + int16_t minimum = WEBRTC_SPL_WORD16_MAX; + int16_t maximum = WEBRTC_SPL_WORD16_MIN; + size_t i = 0; + size_t residual = length & 0x7; + + RTC_DCHECK_GT(length, 0); + + const int16_t* p_start = vector; + int16x8_t min16x8 = vdupq_n_s16(WEBRTC_SPL_WORD16_MAX); + int16x8_t max16x8 = vdupq_n_s16(WEBRTC_SPL_WORD16_MIN); + + // First part, unroll the loop 8 times. + for (i = 0; i < length - residual; i += 8) { + int16x8_t in16x8 = vld1q_s16(p_start); + min16x8 = vminq_s16(min16x8, in16x8); + max16x8 = vmaxq_s16(max16x8, in16x8); + p_start += 8; + } + +#if defined(WEBRTC_ARCH_ARM64) + minimum = vminvq_s16(min16x8); + maximum = vmaxvq_s16(max16x8); +#else + int16x4_t min16x4 = vmin_s16(vget_low_s16(min16x8), vget_high_s16(min16x8)); + min16x4 = vpmin_s16(min16x4, min16x4); + min16x4 = vpmin_s16(min16x4, min16x4); + + minimum = vget_lane_s16(min16x4, 0); + + int16x4_t max16x4 = vmax_s16(vget_low_s16(max16x8), vget_high_s16(max16x8)); + max16x4 = vpmax_s16(max16x4, max16x4); + max16x4 = vpmax_s16(max16x4, max16x4); + + maximum = vget_lane_s16(max16x4, 0); +#endif + + // Second part, do the remaining iterations (if any). + for (i = residual; i > 0; i--) { + if (*p_start < minimum) + minimum = *p_start; + if (*p_start > maximum) + maximum = *p_start; + p_start++; + } + *min_val = minimum; + *max_val = maximum; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/randomization_functions.c b/pkg/apm/webrtc/common_audio/signal_processing/randomization_functions.c new file mode 100644 index 00000000..adedad07 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/randomization_functions.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains implementations of the randomization functions + * WebRtcSpl_RandU() + * WebRtcSpl_RandN() + * WebRtcSpl_RandUArray() + * + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +static const uint32_t kMaxSeedUsed = 0x80000000; + +static const int16_t kRandNTable[] = { + 9178, -7260, 40, 10189, 4894, -3531, -13779, 14764, -4008, + -8884, -8990, 1008, 7368, 5184, 3251, -5817, -9786, 5963, + 1770, 8066, -7135, 10772, -2298, 1361, 6484, 2241, -8633, + 792, 199, -3344, 6553, -10079, -15040, 95, 11608, -12469, + 14161, -4176, 2476, 6403, 13685, -16005, 6646, 2239, 10916, + -3004, -602, -3141, 2142, 14144, -5829, 5305, 8209, 4713, + 2697, -5112, 16092, -1210, -2891, -6631, -5360, -11878, -6781, + -2739, -6392, 536, 10923, 10872, 5059, -4748, -7770, 5477, + 38, -1025, -2892, 1638, 6304, 14375, -11028, 1553, -1565, + 10762, -393, 4040, 5257, 12310, 6554, -4799, 4899, -6354, + 1603, -1048, -2220, 8247, -186, -8944, -12004, 2332, 4801, + -4933, 6371, 131, 8614, -5927, -8287, -22760, 4033, -15162, + 3385, 3246, 3153, -5250, 3766, 784, 6494, -62, 3531, + -1582, 15572, 662, -3952, -330, -3196, 669, 7236, -2678, + -6569, 23319, -8645, -741, 14830, -15976, 4903, 315, -11342, + 10311, 1858, -7777, 2145, 5436, 5677, -113, -10033, 826, + -1353, 17210, 7768, 986, -1471, 8291, -4982, 8207, -14911, + -6255, -2449, -11881, -7059, -11703, -4338, 8025, 7538, -2823, + -12490, 9470, -1613, -2529, -10092, -7807, 9480, 6970, -12844, + 5123, 3532, 4816, 4803, -8455, -5045, 14032, -4378, -1643, + 5756, -11041, -2732, -16618, -6430, -18375, -3320, 6098, 5131, + -4269, -8840, 2482, -7048, 1547, -21890, -6505, -7414, -424, + -11722, 7955, 1653, -17299, 1823, 473, -9232, 3337, 1111, + 873, 4018, -8982, 9889, 3531, -11763, -3799, 7373, -4539, + 3231, 7054, -8537, 7616, 6244, 16635, 447, -2915, 13967, + 705, -2669, -1520, -1771, -16188, 5956, 5117, 6371, -9936, + -1448, 2480, 5128, 7550, -8130, 5236, 8213, -6443, 7707, + -1950, -13811, 7218, 7031, -3883, 67, 5731, -2874, 13480, + -3743, 9298, -3280, 3552, -4425, -18, -3785, -9988, -5357, + 5477, -11794, 2117, 1416, -9935, 3376, 802, -5079, -8243, + 12652, 66, 3653, -2368, 6781, -21895, -7227, 2487, 7839, + -385, 6646, -7016, -4658, 5531, -1705, 834, 129, 3694, + -1343, 2238, -22640, -6417, -11139, 11301, -2945, -3494, -5626, + 185, -3615, -2041, -7972, -3106, -60, -23497, -1566, 17064, + 3519, 2518, 304, -6805, -10269, 2105, 1936, -426, -736, + -8122, -1467, 4238, -6939, -13309, 360, 7402, -7970, 12576, + 3287, 12194, -6289, -16006, 9171, 4042, -9193, 9123, -2512, + 6388, -4734, -8739, 1028, -5406, -1696, 5889, -666, -4736, + 4971, 3565, 9362, -6292, 3876, -3652, -19666, 7523, -4061, + 391, -11773, 7502, -3763, 4929, -9478, 13278, 2805, 4496, + 7814, 16419, 12455, -14773, 2127, -2746, 3763, 4847, 3698, + 6978, 4751, -6957, -3581, -45, 6252, 1513, -4797, -7925, + 11270, 16188, -2359, -5269, 9376, -10777, 7262, 20031, -6515, + -2208, -5353, 8085, -1341, -1303, 7333, 5576, 3625, 5763, + -7931, 9833, -3371, -10305, 6534, -13539, -9971, 997, 8464, + -4064, -1495, 1857, 13624, 5458, 9490, -11086, -4524, 12022, + -550, -198, 408, -8455, -7068, 10289, 9712, -3366, 9028, + -7621, -5243, 2362, 6909, 4672, -4933, -1799, 4709, -4563, + -62, -566, 1624, -7010, 14730, -17791, -3697, -2344, -1741, + 7099, -9509, -6855, -1989, 3495, -2289, 2031, 12784, 891, + 14189, -3963, -5683, 421, -12575, 1724, -12682, -5970, -8169, + 3143, -1824, -5488, -5130, 8536, 12799, 794, 5738, 3459, + -11689, -258, -3738, -3775, -8742, 2333, 8312, -9383, 10331, + 13119, 8398, 10644, -19433, -6446, -16277, -11793, 16284, 9345, + 15222, 15834, 2009, -7349, 130, -14547, 338, -5998, 3337, + 21492, 2406, 7703, -951, 11196, -564, 3406, 2217, 4806, + 2374, -5797, 11839, 8940, -11874, 18213, 2855, 10492}; + +static uint32_t IncreaseSeed(uint32_t* seed) { + seed[0] = (seed[0] * ((int32_t)69069) + 1) & (kMaxSeedUsed - 1); + return seed[0]; +} + +int16_t WebRtcSpl_RandU(uint32_t* seed) { + return (int16_t)(IncreaseSeed(seed) >> 16); +} + +int16_t WebRtcSpl_RandN(uint32_t* seed) { + return kRandNTable[IncreaseSeed(seed) >> 23]; +} + +// Creates an array of uniformly distributed variables. +int16_t WebRtcSpl_RandUArray(int16_t* vector, + int16_t vector_length, + uint32_t* seed) { + int i; + for (i = 0; i < vector_length; i++) { + vector[i] = WebRtcSpl_RandU(seed); + } + return vector_length; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/real_fft.c b/pkg/apm/webrtc/common_audio/signal_processing/real_fft.c new file mode 100644 index 00000000..780e517a --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/real_fft.c @@ -0,0 +1,102 @@ +/* + * 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 "common_audio/signal_processing/include/real_fft.h" + +#include + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +struct RealFFT { + int order; +}; + +struct RealFFT* WebRtcSpl_CreateRealFFT(int order) { + struct RealFFT* self = NULL; + + if (order > kMaxFFTOrder || order < 0) { + return NULL; + } + + self = malloc(sizeof(struct RealFFT)); + if (self == NULL) { + return NULL; + } + self->order = order; + + return self; +} + +void WebRtcSpl_FreeRealFFT(struct RealFFT* self) { + if (self != NULL) { + free(self); + } +} + +// The C version FFT functions (i.e. WebRtcSpl_RealForwardFFT and +// WebRtcSpl_RealInverseFFT) are real-valued FFT wrappers for complex-valued +// FFT implementation in SPL. + +int WebRtcSpl_RealForwardFFT(struct RealFFT* self, + const int16_t* real_data_in, + int16_t* complex_data_out) { + int i = 0; + int j = 0; + int result = 0; + int n = 1 << self->order; + // The complex-value FFT implementation needs a buffer to hold 2^order + // 16-bit COMPLEX numbers, for both time and frequency data. + int16_t complex_buffer[2 << kMaxFFTOrder]; + + // Insert zeros to the imaginary parts for complex forward FFT input. + for (i = 0, j = 0; i < n; i += 1, j += 2) { + complex_buffer[j] = real_data_in[i]; + complex_buffer[j + 1] = 0; + }; + + WebRtcSpl_ComplexBitReverse(complex_buffer, self->order); + result = WebRtcSpl_ComplexFFT(complex_buffer, self->order, 1); + + // For real FFT output, use only the first N + 2 elements from + // complex forward FFT. + memcpy(complex_data_out, complex_buffer, sizeof(int16_t) * (n + 2)); + + return result; +} + +int WebRtcSpl_RealInverseFFT(struct RealFFT* self, + const int16_t* complex_data_in, + int16_t* real_data_out) { + int i = 0; + int j = 0; + int result = 0; + int n = 1 << self->order; + // Create the buffer specific to complex-valued FFT implementation. + int16_t complex_buffer[2 << kMaxFFTOrder]; + + // For n-point FFT, first copy the first n + 2 elements into complex + // FFT, then construct the remaining n - 2 elements by real FFT's + // conjugate-symmetric properties. + memcpy(complex_buffer, complex_data_in, sizeof(int16_t) * (n + 2)); + for (i = n + 2; i < 2 * n; i += 2) { + complex_buffer[i] = complex_data_in[2 * n - i]; + complex_buffer[i + 1] = -complex_data_in[2 * n - i + 1]; + } + + WebRtcSpl_ComplexBitReverse(complex_buffer, self->order); + result = WebRtcSpl_ComplexIFFT(complex_buffer, self->order, 1); + + // Strip out the imaginary parts of the complex inverse FFT output. + for (i = 0, j = 0; i < n; i += 1, j += 2) { + real_data_out[i] = complex_buffer[j]; + } + + return result; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c b/pkg/apm/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c new file mode 100644 index 00000000..93e87872 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_ReflCoefToLpc(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +void WebRtcSpl_ReflCoefToLpc(const int16_t* k, int use_order, int16_t* a) { + int16_t any[WEBRTC_SPL_MAX_LPC_ORDER + 1]; + int16_t *aptr, *aptr2, *anyptr; + const int16_t* kptr; + int m, i; + + kptr = k; + *a = 4096; // i.e., (Word16_MAX >> 3)+1. + *any = *a; + a[1] = *k >> 3; + + for (m = 1; m < use_order; m++) { + kptr++; + aptr = a; + aptr++; + aptr2 = &a[m]; + anyptr = any; + anyptr++; + + any[m + 1] = *kptr >> 3; + for (i = 0; i < m; i++) { + *anyptr = *aptr + (int16_t)((*aptr2 * *kptr) >> 15); + anyptr++; + aptr++; + aptr2--; + } + + aptr = a; + anyptr = any; + for (i = 0; i < (m + 2); i++) { + *aptr = *anyptr; + aptr++; + anyptr++; + } + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/resample.c b/pkg/apm/webrtc/common_audio/signal_processing/resample.c new file mode 100644 index 00000000..4a534aef --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/resample.c @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the resampling functions for 22 kHz. + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/resample_by_2_internal.h" + +// Declaration of internally used functions +static void WebRtcSpl_32khzTo22khzIntToShort(const int32_t* In, + int16_t* Out, + int32_t K); + +void WebRtcSpl_32khzTo22khzIntToInt(const int32_t* In, int32_t* Out, int32_t K); + +// interpolation coefficients +static const int16_t kCoefficients32To22[5][9] = { + {127, -712, 2359, -6333, 23456, 16775, -3695, 945, -154}, + {-39, 230, -830, 2785, 32366, -2324, 760, -218, 38}, + {117, -663, 2222, -6133, 26634, 13070, -3174, 831, -137}, + {-77, 457, -1677, 5958, 31175, -4136, 1405, -408, 71}, + {98, -560, 1900, -5406, 29240, 9423, -2480, 663, -110}}; + +////////////////////// +// 22 kHz -> 16 kHz // +////////////////////// + +// number of subblocks; options: 1, 2, 4, 5, 10 +#define SUB_BLOCKS_22_16 5 + +// 22 -> 16 resampler +void WebRtcSpl_Resample22khzTo16khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State22khzTo16khz* state, + int32_t* tmpmem) { + int k; + + // process two blocks of 10/SUB_BLOCKS_22_16 ms (to reduce temp buffer size) + for (k = 0; k < SUB_BLOCKS_22_16; k++) { + ///// 22 --> 44 ///// + // int16_t in[220/SUB_BLOCKS_22_16] + // int32_t out[440/SUB_BLOCKS_22_16] + ///// + WebRtcSpl_UpBy2ShortToInt(in, 220 / SUB_BLOCKS_22_16, tmpmem + 16, + state->S_22_44); + + ///// 44 --> 32 ///// + // int32_t in[440/SUB_BLOCKS_22_16] + // int32_t out[320/SUB_BLOCKS_22_16] + ///// + // copy state to and from input array + tmpmem[8] = state->S_44_32[0]; + tmpmem[9] = state->S_44_32[1]; + tmpmem[10] = state->S_44_32[2]; + tmpmem[11] = state->S_44_32[3]; + tmpmem[12] = state->S_44_32[4]; + tmpmem[13] = state->S_44_32[5]; + tmpmem[14] = state->S_44_32[6]; + tmpmem[15] = state->S_44_32[7]; + state->S_44_32[0] = tmpmem[440 / SUB_BLOCKS_22_16 + 8]; + state->S_44_32[1] = tmpmem[440 / SUB_BLOCKS_22_16 + 9]; + state->S_44_32[2] = tmpmem[440 / SUB_BLOCKS_22_16 + 10]; + state->S_44_32[3] = tmpmem[440 / SUB_BLOCKS_22_16 + 11]; + state->S_44_32[4] = tmpmem[440 / SUB_BLOCKS_22_16 + 12]; + state->S_44_32[5] = tmpmem[440 / SUB_BLOCKS_22_16 + 13]; + state->S_44_32[6] = tmpmem[440 / SUB_BLOCKS_22_16 + 14]; + state->S_44_32[7] = tmpmem[440 / SUB_BLOCKS_22_16 + 15]; + + WebRtcSpl_Resample44khzTo32khz(tmpmem + 8, tmpmem, 40 / SUB_BLOCKS_22_16); + + ///// 32 --> 16 ///// + // int32_t in[320/SUB_BLOCKS_22_16] + // int32_t out[160/SUB_BLOCKS_22_16] + ///// + WebRtcSpl_DownBy2IntToShort(tmpmem, 320 / SUB_BLOCKS_22_16, out, + state->S_32_16); + + // move input/output pointers 10/SUB_BLOCKS_22_16 ms seconds ahead + in += 220 / SUB_BLOCKS_22_16; + out += 160 / SUB_BLOCKS_22_16; + } +} + +// initialize state of 22 -> 16 resampler +void WebRtcSpl_ResetResample22khzTo16khz(WebRtcSpl_State22khzTo16khz* state) { + int k; + for (k = 0; k < 8; k++) { + state->S_22_44[k] = 0; + state->S_44_32[k] = 0; + state->S_32_16[k] = 0; + } +} + +////////////////////// +// 16 kHz -> 22 kHz // +////////////////////// + +// number of subblocks; options: 1, 2, 4, 5, 10 +#define SUB_BLOCKS_16_22 4 + +// 16 -> 22 resampler +void WebRtcSpl_Resample16khzTo22khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State16khzTo22khz* state, + int32_t* tmpmem) { + int k; + + // process two blocks of 10/SUB_BLOCKS_16_22 ms (to reduce temp buffer size) + for (k = 0; k < SUB_BLOCKS_16_22; k++) { + ///// 16 --> 32 ///// + // int16_t in[160/SUB_BLOCKS_16_22] + // int32_t out[320/SUB_BLOCKS_16_22] + ///// + WebRtcSpl_UpBy2ShortToInt(in, 160 / SUB_BLOCKS_16_22, tmpmem + 8, + state->S_16_32); + + ///// 32 --> 22 ///// + // int32_t in[320/SUB_BLOCKS_16_22] + // int32_t out[220/SUB_BLOCKS_16_22] + ///// + // copy state to and from input array + tmpmem[0] = state->S_32_22[0]; + tmpmem[1] = state->S_32_22[1]; + tmpmem[2] = state->S_32_22[2]; + tmpmem[3] = state->S_32_22[3]; + tmpmem[4] = state->S_32_22[4]; + tmpmem[5] = state->S_32_22[5]; + tmpmem[6] = state->S_32_22[6]; + tmpmem[7] = state->S_32_22[7]; + state->S_32_22[0] = tmpmem[320 / SUB_BLOCKS_16_22]; + state->S_32_22[1] = tmpmem[320 / SUB_BLOCKS_16_22 + 1]; + state->S_32_22[2] = tmpmem[320 / SUB_BLOCKS_16_22 + 2]; + state->S_32_22[3] = tmpmem[320 / SUB_BLOCKS_16_22 + 3]; + state->S_32_22[4] = tmpmem[320 / SUB_BLOCKS_16_22 + 4]; + state->S_32_22[5] = tmpmem[320 / SUB_BLOCKS_16_22 + 5]; + state->S_32_22[6] = tmpmem[320 / SUB_BLOCKS_16_22 + 6]; + state->S_32_22[7] = tmpmem[320 / SUB_BLOCKS_16_22 + 7]; + + WebRtcSpl_32khzTo22khzIntToShort(tmpmem, out, 20 / SUB_BLOCKS_16_22); + + // move input/output pointers 10/SUB_BLOCKS_16_22 ms seconds ahead + in += 160 / SUB_BLOCKS_16_22; + out += 220 / SUB_BLOCKS_16_22; + } +} + +// initialize state of 16 -> 22 resampler +void WebRtcSpl_ResetResample16khzTo22khz(WebRtcSpl_State16khzTo22khz* state) { + int k; + for (k = 0; k < 8; k++) { + state->S_16_32[k] = 0; + state->S_32_22[k] = 0; + } +} + +////////////////////// +// 22 kHz -> 8 kHz // +////////////////////// + +// number of subblocks; options: 1, 2, 5, 10 +#define SUB_BLOCKS_22_8 2 + +// 22 -> 8 resampler +void WebRtcSpl_Resample22khzTo8khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State22khzTo8khz* state, + int32_t* tmpmem) { + int k; + + // process two blocks of 10/SUB_BLOCKS_22_8 ms (to reduce temp buffer size) + for (k = 0; k < SUB_BLOCKS_22_8; k++) { + ///// 22 --> 22 lowpass ///// + // int16_t in[220/SUB_BLOCKS_22_8] + // int32_t out[220/SUB_BLOCKS_22_8] + ///// + WebRtcSpl_LPBy2ShortToInt(in, 220 / SUB_BLOCKS_22_8, tmpmem + 16, + state->S_22_22); + + ///// 22 --> 16 ///// + // int32_t in[220/SUB_BLOCKS_22_8] + // int32_t out[160/SUB_BLOCKS_22_8] + ///// + // copy state to and from input array + tmpmem[8] = state->S_22_16[0]; + tmpmem[9] = state->S_22_16[1]; + tmpmem[10] = state->S_22_16[2]; + tmpmem[11] = state->S_22_16[3]; + tmpmem[12] = state->S_22_16[4]; + tmpmem[13] = state->S_22_16[5]; + tmpmem[14] = state->S_22_16[6]; + tmpmem[15] = state->S_22_16[7]; + state->S_22_16[0] = tmpmem[220 / SUB_BLOCKS_22_8 + 8]; + state->S_22_16[1] = tmpmem[220 / SUB_BLOCKS_22_8 + 9]; + state->S_22_16[2] = tmpmem[220 / SUB_BLOCKS_22_8 + 10]; + state->S_22_16[3] = tmpmem[220 / SUB_BLOCKS_22_8 + 11]; + state->S_22_16[4] = tmpmem[220 / SUB_BLOCKS_22_8 + 12]; + state->S_22_16[5] = tmpmem[220 / SUB_BLOCKS_22_8 + 13]; + state->S_22_16[6] = tmpmem[220 / SUB_BLOCKS_22_8 + 14]; + state->S_22_16[7] = tmpmem[220 / SUB_BLOCKS_22_8 + 15]; + + WebRtcSpl_Resample44khzTo32khz(tmpmem + 8, tmpmem, 20 / SUB_BLOCKS_22_8); + + ///// 16 --> 8 ///// + // int32_t in[160/SUB_BLOCKS_22_8] + // int32_t out[80/SUB_BLOCKS_22_8] + ///// + WebRtcSpl_DownBy2IntToShort(tmpmem, 160 / SUB_BLOCKS_22_8, out, + state->S_16_8); + + // move input/output pointers 10/SUB_BLOCKS_22_8 ms seconds ahead + in += 220 / SUB_BLOCKS_22_8; + out += 80 / SUB_BLOCKS_22_8; + } +} + +// initialize state of 22 -> 8 resampler +void WebRtcSpl_ResetResample22khzTo8khz(WebRtcSpl_State22khzTo8khz* state) { + int k; + for (k = 0; k < 8; k++) { + state->S_22_22[k] = 0; + state->S_22_22[k + 8] = 0; + state->S_22_16[k] = 0; + state->S_16_8[k] = 0; + } +} + +////////////////////// +// 8 kHz -> 22 kHz // +////////////////////// + +// number of subblocks; options: 1, 2, 5, 10 +#define SUB_BLOCKS_8_22 2 + +// 8 -> 22 resampler +void WebRtcSpl_Resample8khzTo22khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State8khzTo22khz* state, + int32_t* tmpmem) { + int k; + + // process two blocks of 10/SUB_BLOCKS_8_22 ms (to reduce temp buffer size) + for (k = 0; k < SUB_BLOCKS_8_22; k++) { + ///// 8 --> 16 ///// + // int16_t in[80/SUB_BLOCKS_8_22] + // int32_t out[160/SUB_BLOCKS_8_22] + ///// + WebRtcSpl_UpBy2ShortToInt(in, 80 / SUB_BLOCKS_8_22, tmpmem + 18, + state->S_8_16); + + ///// 16 --> 11 ///// + // int32_t in[160/SUB_BLOCKS_8_22] + // int32_t out[110/SUB_BLOCKS_8_22] + ///// + // copy state to and from input array + tmpmem[10] = state->S_16_11[0]; + tmpmem[11] = state->S_16_11[1]; + tmpmem[12] = state->S_16_11[2]; + tmpmem[13] = state->S_16_11[3]; + tmpmem[14] = state->S_16_11[4]; + tmpmem[15] = state->S_16_11[5]; + tmpmem[16] = state->S_16_11[6]; + tmpmem[17] = state->S_16_11[7]; + state->S_16_11[0] = tmpmem[160 / SUB_BLOCKS_8_22 + 10]; + state->S_16_11[1] = tmpmem[160 / SUB_BLOCKS_8_22 + 11]; + state->S_16_11[2] = tmpmem[160 / SUB_BLOCKS_8_22 + 12]; + state->S_16_11[3] = tmpmem[160 / SUB_BLOCKS_8_22 + 13]; + state->S_16_11[4] = tmpmem[160 / SUB_BLOCKS_8_22 + 14]; + state->S_16_11[5] = tmpmem[160 / SUB_BLOCKS_8_22 + 15]; + state->S_16_11[6] = tmpmem[160 / SUB_BLOCKS_8_22 + 16]; + state->S_16_11[7] = tmpmem[160 / SUB_BLOCKS_8_22 + 17]; + + WebRtcSpl_32khzTo22khzIntToInt(tmpmem + 10, tmpmem, 10 / SUB_BLOCKS_8_22); + + ///// 11 --> 22 ///// + // int32_t in[110/SUB_BLOCKS_8_22] + // int16_t out[220/SUB_BLOCKS_8_22] + ///// + WebRtcSpl_UpBy2IntToShort(tmpmem, 110 / SUB_BLOCKS_8_22, out, + state->S_11_22); + + // move input/output pointers 10/SUB_BLOCKS_8_22 ms seconds ahead + in += 80 / SUB_BLOCKS_8_22; + out += 220 / SUB_BLOCKS_8_22; + } +} + +// initialize state of 8 -> 22 resampler +void WebRtcSpl_ResetResample8khzTo22khz(WebRtcSpl_State8khzTo22khz* state) { + int k; + for (k = 0; k < 8; k++) { + state->S_8_16[k] = 0; + state->S_16_11[k] = 0; + state->S_11_22[k] = 0; + } +} + +// compute two inner-products and store them to output array +static void WebRtcSpl_DotProdIntToInt(const int32_t* in1, + const int32_t* in2, + const int16_t* coef_ptr, + int32_t* out1, + int32_t* out2) { + int32_t tmp1 = 16384; + int32_t tmp2 = 16384; + int16_t coef; + + coef = coef_ptr[0]; + tmp1 += coef * in1[0]; + tmp2 += coef * in2[-0]; + + coef = coef_ptr[1]; + tmp1 += coef * in1[1]; + tmp2 += coef * in2[-1]; + + coef = coef_ptr[2]; + tmp1 += coef * in1[2]; + tmp2 += coef * in2[-2]; + + coef = coef_ptr[3]; + tmp1 += coef * in1[3]; + tmp2 += coef * in2[-3]; + + coef = coef_ptr[4]; + tmp1 += coef * in1[4]; + tmp2 += coef * in2[-4]; + + coef = coef_ptr[5]; + tmp1 += coef * in1[5]; + tmp2 += coef * in2[-5]; + + coef = coef_ptr[6]; + tmp1 += coef * in1[6]; + tmp2 += coef * in2[-6]; + + coef = coef_ptr[7]; + tmp1 += coef * in1[7]; + tmp2 += coef * in2[-7]; + + coef = coef_ptr[8]; + *out1 = tmp1 + coef * in1[8]; + *out2 = tmp2 + coef * in2[-8]; +} + +// compute two inner-products and store them to output array +static void WebRtcSpl_DotProdIntToShort(const int32_t* in1, + const int32_t* in2, + const int16_t* coef_ptr, + int16_t* out1, + int16_t* out2) { + int32_t tmp1 = 16384; + int32_t tmp2 = 16384; + int16_t coef; + + coef = coef_ptr[0]; + tmp1 += coef * in1[0]; + tmp2 += coef * in2[-0]; + + coef = coef_ptr[1]; + tmp1 += coef * in1[1]; + tmp2 += coef * in2[-1]; + + coef = coef_ptr[2]; + tmp1 += coef * in1[2]; + tmp2 += coef * in2[-2]; + + coef = coef_ptr[3]; + tmp1 += coef * in1[3]; + tmp2 += coef * in2[-3]; + + coef = coef_ptr[4]; + tmp1 += coef * in1[4]; + tmp2 += coef * in2[-4]; + + coef = coef_ptr[5]; + tmp1 += coef * in1[5]; + tmp2 += coef * in2[-5]; + + coef = coef_ptr[6]; + tmp1 += coef * in1[6]; + tmp2 += coef * in2[-6]; + + coef = coef_ptr[7]; + tmp1 += coef * in1[7]; + tmp2 += coef * in2[-7]; + + coef = coef_ptr[8]; + tmp1 += coef * in1[8]; + tmp2 += coef * in2[-8]; + + // scale down, round and saturate + tmp1 >>= 15; + if (tmp1 > (int32_t)0x00007FFF) + tmp1 = 0x00007FFF; + if (tmp1 < (int32_t)0xFFFF8000) + tmp1 = 0xFFFF8000; + tmp2 >>= 15; + if (tmp2 > (int32_t)0x00007FFF) + tmp2 = 0x00007FFF; + if (tmp2 < (int32_t)0xFFFF8000) + tmp2 = 0xFFFF8000; + *out1 = (int16_t)tmp1; + *out2 = (int16_t)tmp2; +} + +// Resampling ratio: 11/16 +// input: int32_t (normalized, not saturated) :: size 16 * K +// output: int32_t (shifted 15 positions to the left, + offset 16384) :: size 11 +// * K +// K: Number of blocks + +void WebRtcSpl_32khzTo22khzIntToInt(const int32_t* In, + int32_t* Out, + int32_t K) { + ///////////////////////////////////////////////////////////// + // Filter operation: + // + // Perform resampling (16 input samples -> 11 output samples); + // process in sub blocks of size 16 samples. + int32_t m; + + for (m = 0; m < K; m++) { + // first output sample + Out[0] = ((int32_t)In[3] << 15) + (1 << 14); + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_DotProdIntToInt(&In[0], &In[22], kCoefficients32To22[0], &Out[1], + &Out[10]); + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_DotProdIntToInt(&In[2], &In[20], kCoefficients32To22[1], &Out[2], + &Out[9]); + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_DotProdIntToInt(&In[3], &In[19], kCoefficients32To22[2], &Out[3], + &Out[8]); + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_DotProdIntToInt(&In[5], &In[17], kCoefficients32To22[3], &Out[4], + &Out[7]); + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_DotProdIntToInt(&In[6], &In[16], kCoefficients32To22[4], &Out[5], + &Out[6]); + + // update pointers + In += 16; + Out += 11; + } +} + +// Resampling ratio: 11/16 +// input: int32_t (normalized, not saturated) :: size 16 * K +// output: int16_t (saturated) :: size 11 * K +// K: Number of blocks + +void WebRtcSpl_32khzTo22khzIntToShort(const int32_t* In, + int16_t* Out, + int32_t K) { + ///////////////////////////////////////////////////////////// + // Filter operation: + // + // Perform resampling (16 input samples -> 11 output samples); + // process in sub blocks of size 16 samples. + int32_t tmp; + int32_t m; + + for (m = 0; m < K; m++) { + // first output sample + tmp = In[3]; + if (tmp > (int32_t)0x00007FFF) + tmp = 0x00007FFF; + if (tmp < (int32_t)0xFFFF8000) + tmp = 0xFFFF8000; + Out[0] = (int16_t)tmp; + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_DotProdIntToShort(&In[0], &In[22], kCoefficients32To22[0], + &Out[1], &Out[10]); + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_DotProdIntToShort(&In[2], &In[20], kCoefficients32To22[1], + &Out[2], &Out[9]); + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_DotProdIntToShort(&In[3], &In[19], kCoefficients32To22[2], + &Out[3], &Out[8]); + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_DotProdIntToShort(&In[5], &In[17], kCoefficients32To22[3], + &Out[4], &Out[7]); + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_DotProdIntToShort(&In[6], &In[16], kCoefficients32To22[4], + &Out[5], &Out[6]); + + // update pointers + In += 16; + Out += 11; + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/resample_48khz.c b/pkg/apm/webrtc/common_audio/signal_processing/resample_48khz.c new file mode 100644 index 00000000..d083379f --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/resample_48khz.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains resampling functions between 48 kHz and nb/wb. + * The description header can be found in signal_processing_library.h + * + */ + +#include + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/resample_by_2_internal.h" + +//////////////////////////// +///// 48 kHz -> 16 kHz ///// +//////////////////////////// + +// 48 -> 16 resampler +void WebRtcSpl_Resample48khzTo16khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State48khzTo16khz* state, + int32_t* tmpmem) { + ///// 48 --> 48(LP) ///// + // int16_t in[480] + // int32_t out[480] + ///// + WebRtcSpl_LPBy2ShortToInt(in, 480, tmpmem + 16, state->S_48_48); + + ///// 48 --> 32 ///// + // int32_t in[480] + // int32_t out[320] + ///// + // copy state to and from input array + memcpy(tmpmem + 8, state->S_48_32, 8 * sizeof(int32_t)); + memcpy(state->S_48_32, tmpmem + 488, 8 * sizeof(int32_t)); + WebRtcSpl_Resample48khzTo32khz(tmpmem + 8, tmpmem, 160); + + ///// 32 --> 16 ///// + // int32_t in[320] + // int16_t out[160] + ///// + WebRtcSpl_DownBy2IntToShort(tmpmem, 320, out, state->S_32_16); +} + +// initialize state of 48 -> 16 resampler +void WebRtcSpl_ResetResample48khzTo16khz(WebRtcSpl_State48khzTo16khz* state) { + memset(state->S_48_48, 0, 16 * sizeof(int32_t)); + memset(state->S_48_32, 0, 8 * sizeof(int32_t)); + memset(state->S_32_16, 0, 8 * sizeof(int32_t)); +} + +//////////////////////////// +///// 16 kHz -> 48 kHz ///// +//////////////////////////// + +// 16 -> 48 resampler +void WebRtcSpl_Resample16khzTo48khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State16khzTo48khz* state, + int32_t* tmpmem) { + ///// 16 --> 32 ///// + // int16_t in[160] + // int32_t out[320] + ///// + WebRtcSpl_UpBy2ShortToInt(in, 160, tmpmem + 16, state->S_16_32); + + ///// 32 --> 24 ///// + // int32_t in[320] + // int32_t out[240] + // copy state to and from input array + ///// + memcpy(tmpmem + 8, state->S_32_24, 8 * sizeof(int32_t)); + memcpy(state->S_32_24, tmpmem + 328, 8 * sizeof(int32_t)); + WebRtcSpl_Resample32khzTo24khz(tmpmem + 8, tmpmem, 80); + + ///// 24 --> 48 ///// + // int32_t in[240] + // int16_t out[480] + ///// + WebRtcSpl_UpBy2IntToShort(tmpmem, 240, out, state->S_24_48); +} + +// initialize state of 16 -> 48 resampler +void WebRtcSpl_ResetResample16khzTo48khz(WebRtcSpl_State16khzTo48khz* state) { + memset(state->S_16_32, 0, 8 * sizeof(int32_t)); + memset(state->S_32_24, 0, 8 * sizeof(int32_t)); + memset(state->S_24_48, 0, 8 * sizeof(int32_t)); +} + +//////////////////////////// +///// 48 kHz -> 8 kHz ///// +//////////////////////////// + +// 48 -> 8 resampler +void WebRtcSpl_Resample48khzTo8khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State48khzTo8khz* state, + int32_t* tmpmem) { + ///// 48 --> 24 ///// + // int16_t in[480] + // int32_t out[240] + ///// + WebRtcSpl_DownBy2ShortToInt(in, 480, tmpmem + 256, state->S_48_24); + + ///// 24 --> 24(LP) ///// + // int32_t in[240] + // int32_t out[240] + ///// + WebRtcSpl_LPBy2IntToInt(tmpmem + 256, 240, tmpmem + 16, state->S_24_24); + + ///// 24 --> 16 ///// + // int32_t in[240] + // int32_t out[160] + ///// + // copy state to and from input array + memcpy(tmpmem + 8, state->S_24_16, 8 * sizeof(int32_t)); + memcpy(state->S_24_16, tmpmem + 248, 8 * sizeof(int32_t)); + WebRtcSpl_Resample48khzTo32khz(tmpmem + 8, tmpmem, 80); + + ///// 16 --> 8 ///// + // int32_t in[160] + // int16_t out[80] + ///// + WebRtcSpl_DownBy2IntToShort(tmpmem, 160, out, state->S_16_8); +} + +// initialize state of 48 -> 8 resampler +void WebRtcSpl_ResetResample48khzTo8khz(WebRtcSpl_State48khzTo8khz* state) { + memset(state->S_48_24, 0, 8 * sizeof(int32_t)); + memset(state->S_24_24, 0, 16 * sizeof(int32_t)); + memset(state->S_24_16, 0, 8 * sizeof(int32_t)); + memset(state->S_16_8, 0, 8 * sizeof(int32_t)); +} + +//////////////////////////// +///// 8 kHz -> 48 kHz ///// +//////////////////////////// + +// 8 -> 48 resampler +void WebRtcSpl_Resample8khzTo48khz(const int16_t* in, + int16_t* out, + WebRtcSpl_State8khzTo48khz* state, + int32_t* tmpmem) { + ///// 8 --> 16 ///// + // int16_t in[80] + // int32_t out[160] + ///// + WebRtcSpl_UpBy2ShortToInt(in, 80, tmpmem + 264, state->S_8_16); + + ///// 16 --> 12 ///// + // int32_t in[160] + // int32_t out[120] + ///// + // copy state to and from input array + memcpy(tmpmem + 256, state->S_16_12, 8 * sizeof(int32_t)); + memcpy(state->S_16_12, tmpmem + 416, 8 * sizeof(int32_t)); + WebRtcSpl_Resample32khzTo24khz(tmpmem + 256, tmpmem + 240, 40); + + ///// 12 --> 24 ///// + // int32_t in[120] + // int16_t out[240] + ///// + WebRtcSpl_UpBy2IntToInt(tmpmem + 240, 120, tmpmem, state->S_12_24); + + ///// 24 --> 48 ///// + // int32_t in[240] + // int16_t out[480] + ///// + WebRtcSpl_UpBy2IntToShort(tmpmem, 240, out, state->S_24_48); +} + +// initialize state of 8 -> 48 resampler +void WebRtcSpl_ResetResample8khzTo48khz(WebRtcSpl_State8khzTo48khz* state) { + memset(state->S_8_16, 0, 8 * sizeof(int32_t)); + memset(state->S_16_12, 0, 8 * sizeof(int32_t)); + memset(state->S_12_24, 0, 8 * sizeof(int32_t)); + memset(state->S_24_48, 0, 8 * sizeof(int32_t)); +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/resample_by_2.c b/pkg/apm/webrtc/common_audio/signal_processing/resample_by_2.c new file mode 100644 index 00000000..3172154d --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/resample_by_2.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the resampling by two functions. + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +#ifdef WEBRTC_ARCH_ARM_V7 + +// allpass filter coefficients. +static const uint32_t kResampleAllpass1[3] = {3284, 24441, 49528 << 15}; +static const uint32_t kResampleAllpass2[3] = {12199, 37471 << 15, 60255 << 15}; + +// Multiply two 32-bit values and accumulate to another input value. +// Return: state + ((diff * tbl_value) >> 16) + +static __inline int32_t MUL_ACCUM_1(int32_t tbl_value, + int32_t diff, + int32_t state) { + int32_t result; + __asm __volatile("smlawb %0, %1, %2, %3" + : "=r"(result) + : "r"(diff), "r"(tbl_value), "r"(state)); + return result; +} + +// Multiply two 32-bit values and accumulate to another input value. +// Return: Return: state + (((diff << 1) * tbl_value) >> 32) +// +// The reason to introduce this function is that, in case we can't use smlawb +// instruction (in MUL_ACCUM_1) due to input value range, we can still use +// smmla to save some cycles. + +static __inline int32_t MUL_ACCUM_2(int32_t tbl_value, + int32_t diff, + int32_t state) { + int32_t result; + __asm __volatile("smmla %0, %1, %2, %3" + : "=r"(result) + : "r"(diff << 1), "r"(tbl_value), "r"(state)); + return result; +} + +#else + +// allpass filter coefficients. +static const uint16_t kResampleAllpass1[3] = {3284, 24441, 49528}; +static const uint16_t kResampleAllpass2[3] = {12199, 37471, 60255}; + +// Multiply a 32-bit value with a 16-bit value and accumulate to another input: +#define MUL_ACCUM_1(a, b, c) WEBRTC_SPL_SCALEDIFF32(a, b, c) +#define MUL_ACCUM_2(a, b, c) WEBRTC_SPL_SCALEDIFF32(a, b, c) + +#endif // WEBRTC_ARCH_ARM_V7 + +// decimator +#if !defined(MIPS32_LE) +void WebRtcSpl_DownsampleBy2(const int16_t* in, + size_t len, + int16_t* out, + int32_t* filtState) { + int32_t tmp1, tmp2, diff, in32, out32; + size_t i; + + register int32_t state0 = filtState[0]; + register int32_t state1 = filtState[1]; + register int32_t state2 = filtState[2]; + register int32_t state3 = filtState[3]; + register int32_t state4 = filtState[4]; + register int32_t state5 = filtState[5]; + register int32_t state6 = filtState[6]; + register int32_t state7 = filtState[7]; + + for (i = (len >> 1); i > 0; i--) { + // lower allpass filter + in32 = (int32_t)(*in++) * (1 << 10); + diff = in32 - state1; + tmp1 = MUL_ACCUM_1(kResampleAllpass2[0], diff, state0); + state0 = in32; + diff = tmp1 - state2; + tmp2 = MUL_ACCUM_2(kResampleAllpass2[1], diff, state1); + state1 = tmp1; + diff = tmp2 - state3; + state3 = MUL_ACCUM_2(kResampleAllpass2[2], diff, state2); + state2 = tmp2; + + // upper allpass filter + in32 = (int32_t)(*in++) * (1 << 10); + diff = in32 - state5; + tmp1 = MUL_ACCUM_1(kResampleAllpass1[0], diff, state4); + state4 = in32; + diff = tmp1 - state6; + tmp2 = MUL_ACCUM_1(kResampleAllpass1[1], diff, state5); + state5 = tmp1; + diff = tmp2 - state7; + state7 = MUL_ACCUM_2(kResampleAllpass1[2], diff, state6); + state6 = tmp2; + + // add two allpass outputs, divide by two and round + out32 = (state3 + state7 + 1024) >> 11; + + // limit amplitude to prevent wrap-around, and write to output array + *out++ = WebRtcSpl_SatW32ToW16(out32); + } + + filtState[0] = state0; + filtState[1] = state1; + filtState[2] = state2; + filtState[3] = state3; + filtState[4] = state4; + filtState[5] = state5; + filtState[6] = state6; + filtState[7] = state7; +} +#endif // #if defined(MIPS32_LE) + +void WebRtcSpl_UpsampleBy2(const int16_t* in, + size_t len, + int16_t* out, + int32_t* filtState) { + int32_t tmp1, tmp2, diff, in32, out32; + size_t i; + + register int32_t state0 = filtState[0]; + register int32_t state1 = filtState[1]; + register int32_t state2 = filtState[2]; + register int32_t state3 = filtState[3]; + register int32_t state4 = filtState[4]; + register int32_t state5 = filtState[5]; + register int32_t state6 = filtState[6]; + register int32_t state7 = filtState[7]; + + for (i = len; i > 0; i--) { + // lower allpass filter + in32 = (int32_t)(*in++) * (1 << 10); + diff = in32 - state1; + tmp1 = MUL_ACCUM_1(kResampleAllpass1[0], diff, state0); + state0 = in32; + diff = tmp1 - state2; + tmp2 = MUL_ACCUM_1(kResampleAllpass1[1], diff, state1); + state1 = tmp1; + diff = tmp2 - state3; + state3 = MUL_ACCUM_2(kResampleAllpass1[2], diff, state2); + state2 = tmp2; + + // round; limit amplitude to prevent wrap-around; write to output array + out32 = (state3 + 512) >> 10; + *out++ = WebRtcSpl_SatW32ToW16(out32); + + // upper allpass filter + diff = in32 - state5; + tmp1 = MUL_ACCUM_1(kResampleAllpass2[0], diff, state4); + state4 = in32; + diff = tmp1 - state6; + tmp2 = MUL_ACCUM_2(kResampleAllpass2[1], diff, state5); + state5 = tmp1; + diff = tmp2 - state7; + state7 = MUL_ACCUM_2(kResampleAllpass2[2], diff, state6); + state6 = tmp2; + + // round; limit amplitude to prevent wrap-around; write to output array + out32 = (state7 + 512) >> 10; + *out++ = WebRtcSpl_SatW32ToW16(out32); + } + + filtState[0] = state0; + filtState[1] = state1; + filtState[2] = state2; + filtState[3] = state3; + filtState[4] = state4; + filtState[5] = state5; + filtState[6] = state6; + filtState[7] = state7; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/resample_by_2_internal.c b/pkg/apm/webrtc/common_audio/signal_processing/resample_by_2_internal.c new file mode 100644 index 00000000..a68eced7 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/resample_by_2_internal.c @@ -0,0 +1,673 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This header file contains some internal resampling functions. + * + */ + +#include "common_audio/signal_processing/resample_by_2_internal.h" + +#include "rtc_base/sanitizer.h" + +// allpass filter coefficients. +static const int16_t kResampleAllpass[2][3] = {{821, 6110, 12382}, + {3050, 9368, 15063}}; + +// +// decimator +// input: int32_t (shifted 15 positions to the left, + offset 16384) +// OVERWRITTEN! output: int16_t (saturated) (of length len/2) state: filter +// state array; length = 8 + +void RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/5486 + WebRtcSpl_DownBy2IntToShort(int32_t* in, + int32_t len, + int16_t* out, + int32_t* state) { + int32_t tmp0, tmp1, diff; + int32_t i; + + len >>= 1; + + // lower allpass filter (operates on even input samples) + for (i = 0; i < len; i++) { + tmp0 = in[i << 1]; + diff = tmp0 - state[1]; + // UBSan: -1771017321 - 999586185 cannot be represented in type 'int' + + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[0] + diff * kResampleAllpass[1][0]; + state[0] = tmp0; + diff = tmp1 - state[2]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[1] + diff * kResampleAllpass[1][1]; + state[1] = tmp1; + diff = tmp0 - state[3]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[3] = state[2] + diff * kResampleAllpass[1][2]; + state[2] = tmp0; + + // divide by two and store temporarily + in[i << 1] = (state[3] >> 1); + } + + in++; + + // upper allpass filter (operates on odd input samples) + for (i = 0; i < len; i++) { + tmp0 = in[i << 1]; + diff = tmp0 - state[5]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[4] + diff * kResampleAllpass[0][0]; + state[4] = tmp0; + diff = tmp1 - state[6]; + // scale down and round + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[5] + diff * kResampleAllpass[0][1]; + state[5] = tmp1; + diff = tmp0 - state[7]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[7] = state[6] + diff * kResampleAllpass[0][2]; + state[6] = tmp0; + + // divide by two and store temporarily + in[i << 1] = (state[7] >> 1); + } + + in--; + + // combine allpass outputs + for (i = 0; i < len; i += 2) { + // divide by two, add both allpass outputs and round + tmp0 = (in[i << 1] + in[(i << 1) + 1]) >> 15; + tmp1 = (in[(i << 1) + 2] + in[(i << 1) + 3]) >> 15; + if (tmp0 > (int32_t)0x00007FFF) + tmp0 = 0x00007FFF; + if (tmp0 < (int32_t)0xFFFF8000) + tmp0 = 0xFFFF8000; + out[i] = (int16_t)tmp0; + if (tmp1 > (int32_t)0x00007FFF) + tmp1 = 0x00007FFF; + if (tmp1 < (int32_t)0xFFFF8000) + tmp1 = 0xFFFF8000; + out[i + 1] = (int16_t)tmp1; + } +} + +// +// decimator +// input: int16_t +// output: int32_t (shifted 15 positions to the left, + offset 16384) (of length +// len/2) state: filter state array; length = 8 + +void RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/5486 + WebRtcSpl_DownBy2ShortToInt(const int16_t* in, + int32_t len, + int32_t* out, + int32_t* state) { + int32_t tmp0, tmp1, diff; + int32_t i; + + len >>= 1; + + // lower allpass filter (operates on even input samples) + for (i = 0; i < len; i++) { + tmp0 = ((int32_t)in[i << 1] << 15) + (1 << 14); + diff = tmp0 - state[1]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[0] + diff * kResampleAllpass[1][0]; + state[0] = tmp0; + diff = tmp1 - state[2]; + // UBSan: -1379909682 - 834099714 cannot be represented in type 'int' + + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[1] + diff * kResampleAllpass[1][1]; + state[1] = tmp1; + diff = tmp0 - state[3]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[3] = state[2] + diff * kResampleAllpass[1][2]; + state[2] = tmp0; + + // divide by two and store temporarily + out[i] = (state[3] >> 1); + } + + in++; + + // upper allpass filter (operates on odd input samples) + for (i = 0; i < len; i++) { + tmp0 = ((int32_t)in[i << 1] << 15) + (1 << 14); + diff = tmp0 - state[5]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[4] + diff * kResampleAllpass[0][0]; + state[4] = tmp0; + diff = tmp1 - state[6]; + // scale down and round + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[5] + diff * kResampleAllpass[0][1]; + state[5] = tmp1; + diff = tmp0 - state[7]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[7] = state[6] + diff * kResampleAllpass[0][2]; + state[6] = tmp0; + + // divide by two and store temporarily + out[i] += (state[7] >> 1); + } + + in--; +} + +// +// interpolator +// input: int16_t +// output: int32_t (normalized, not saturated) (of length len*2) +// state: filter state array; length = 8 +void WebRtcSpl_UpBy2ShortToInt(const int16_t* in, + int32_t len, + int32_t* out, + int32_t* state) { + int32_t tmp0, tmp1, diff; + int32_t i; + + // upper allpass filter (generates odd output samples) + for (i = 0; i < len; i++) { + tmp0 = ((int32_t)in[i] << 15) + (1 << 14); + diff = tmp0 - state[5]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[4] + diff * kResampleAllpass[0][0]; + state[4] = tmp0; + diff = tmp1 - state[6]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[5] + diff * kResampleAllpass[0][1]; + state[5] = tmp1; + diff = tmp0 - state[7]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[7] = state[6] + diff * kResampleAllpass[0][2]; + state[6] = tmp0; + + // scale down, round and store + out[i << 1] = state[7] >> 15; + } + + out++; + + // lower allpass filter (generates even output samples) + for (i = 0; i < len; i++) { + tmp0 = ((int32_t)in[i] << 15) + (1 << 14); + diff = tmp0 - state[1]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[0] + diff * kResampleAllpass[1][0]; + state[0] = tmp0; + diff = tmp1 - state[2]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[1] + diff * kResampleAllpass[1][1]; + state[1] = tmp1; + diff = tmp0 - state[3]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[3] = state[2] + diff * kResampleAllpass[1][2]; + state[2] = tmp0; + + // scale down, round and store + out[i << 1] = state[3] >> 15; + } +} + +// +// interpolator +// input: int32_t (shifted 15 positions to the left, + offset 16384) +// output: int32_t (shifted 15 positions to the left, + offset 16384) (of length +// len*2) state: filter state array; length = 8 +void WebRtcSpl_UpBy2IntToInt(const int32_t* in, + int32_t len, + int32_t* out, + int32_t* state) { + int32_t tmp0, tmp1, diff; + int32_t i; + + // upper allpass filter (generates odd output samples) + for (i = 0; i < len; i++) { + tmp0 = in[i]; + diff = tmp0 - state[5]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[4] + diff * kResampleAllpass[0][0]; + state[4] = tmp0; + diff = tmp1 - state[6]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[5] + diff * kResampleAllpass[0][1]; + state[5] = tmp1; + diff = tmp0 - state[7]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[7] = state[6] + diff * kResampleAllpass[0][2]; + state[6] = tmp0; + + // scale down, round and store + out[i << 1] = state[7]; + } + + out++; + + // lower allpass filter (generates even output samples) + for (i = 0; i < len; i++) { + tmp0 = in[i]; + diff = tmp0 - state[1]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[0] + diff * kResampleAllpass[1][0]; + state[0] = tmp0; + diff = tmp1 - state[2]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[1] + diff * kResampleAllpass[1][1]; + state[1] = tmp1; + diff = tmp0 - state[3]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[3] = state[2] + diff * kResampleAllpass[1][2]; + state[2] = tmp0; + + // scale down, round and store + out[i << 1] = state[3]; + } +} + +// +// interpolator +// input: int32_t (shifted 15 positions to the left, + offset 16384) +// output: int16_t (saturated) (of length len*2) +// state: filter state array; length = 8 +void WebRtcSpl_UpBy2IntToShort(const int32_t* in, + int32_t len, + int16_t* out, + int32_t* state) { + int32_t tmp0, tmp1, diff; + int32_t i; + + // upper allpass filter (generates odd output samples) + for (i = 0; i < len; i++) { + tmp0 = in[i]; + diff = tmp0 - state[5]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[4] + diff * kResampleAllpass[0][0]; + state[4] = tmp0; + diff = tmp1 - state[6]; + // scale down and round + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[5] + diff * kResampleAllpass[0][1]; + state[5] = tmp1; + diff = tmp0 - state[7]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[7] = state[6] + diff * kResampleAllpass[0][2]; + state[6] = tmp0; + + // scale down, saturate and store + tmp1 = state[7] >> 15; + if (tmp1 > (int32_t)0x00007FFF) + tmp1 = 0x00007FFF; + if (tmp1 < (int32_t)0xFFFF8000) + tmp1 = 0xFFFF8000; + out[i << 1] = (int16_t)tmp1; + } + + out++; + + // lower allpass filter (generates even output samples) + for (i = 0; i < len; i++) { + tmp0 = in[i]; + diff = tmp0 - state[1]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[0] + diff * kResampleAllpass[1][0]; + state[0] = tmp0; + diff = tmp1 - state[2]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[1] + diff * kResampleAllpass[1][1]; + state[1] = tmp1; + diff = tmp0 - state[3]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[3] = state[2] + diff * kResampleAllpass[1][2]; + state[2] = tmp0; + + // scale down, saturate and store + tmp1 = state[3] >> 15; + if (tmp1 > (int32_t)0x00007FFF) + tmp1 = 0x00007FFF; + if (tmp1 < (int32_t)0xFFFF8000) + tmp1 = 0xFFFF8000; + out[i << 1] = (int16_t)tmp1; + } +} + +// lowpass filter +// input: int16_t +// output: int32_t (normalized, not saturated) +// state: filter state array; length = 8 +void WebRtcSpl_LPBy2ShortToInt(const int16_t* in, + int32_t len, + int32_t* out, + int32_t* state) { + int32_t tmp0, tmp1, diff; + int32_t i; + + len >>= 1; + + // lower allpass filter: odd input -> even output samples + in++; + // initial state of polyphase delay element + tmp0 = state[12]; + for (i = 0; i < len; i++) { + diff = tmp0 - state[1]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[0] + diff * kResampleAllpass[1][0]; + state[0] = tmp0; + diff = tmp1 - state[2]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[1] + diff * kResampleAllpass[1][1]; + state[1] = tmp1; + diff = tmp0 - state[3]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[3] = state[2] + diff * kResampleAllpass[1][2]; + state[2] = tmp0; + + // scale down, round and store + out[i << 1] = state[3] >> 1; + tmp0 = ((int32_t)in[i << 1] << 15) + (1 << 14); + } + in--; + + // upper allpass filter: even input -> even output samples + for (i = 0; i < len; i++) { + tmp0 = ((int32_t)in[i << 1] << 15) + (1 << 14); + diff = tmp0 - state[5]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[4] + diff * kResampleAllpass[0][0]; + state[4] = tmp0; + diff = tmp1 - state[6]; + // scale down and round + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[5] + diff * kResampleAllpass[0][1]; + state[5] = tmp1; + diff = tmp0 - state[7]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[7] = state[6] + diff * kResampleAllpass[0][2]; + state[6] = tmp0; + + // average the two allpass outputs, scale down and store + out[i << 1] = (out[i << 1] + (state[7] >> 1)) >> 15; + } + + // switch to odd output samples + out++; + + // lower allpass filter: even input -> odd output samples + for (i = 0; i < len; i++) { + tmp0 = ((int32_t)in[i << 1] << 15) + (1 << 14); + diff = tmp0 - state[9]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[8] + diff * kResampleAllpass[1][0]; + state[8] = tmp0; + diff = tmp1 - state[10]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[9] + diff * kResampleAllpass[1][1]; + state[9] = tmp1; + diff = tmp0 - state[11]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[11] = state[10] + diff * kResampleAllpass[1][2]; + state[10] = tmp0; + + // scale down, round and store + out[i << 1] = state[11] >> 1; + } + + // upper allpass filter: odd input -> odd output samples + in++; + for (i = 0; i < len; i++) { + tmp0 = ((int32_t)in[i << 1] << 15) + (1 << 14); + diff = tmp0 - state[13]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[12] + diff * kResampleAllpass[0][0]; + state[12] = tmp0; + diff = tmp1 - state[14]; + // scale down and round + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[13] + diff * kResampleAllpass[0][1]; + state[13] = tmp1; + diff = tmp0 - state[15]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[15] = state[14] + diff * kResampleAllpass[0][2]; + state[14] = tmp0; + + // average the two allpass outputs, scale down and store + out[i << 1] = (out[i << 1] + (state[15] >> 1)) >> 15; + } +} + +// lowpass filter +// input: int32_t (shifted 15 positions to the left, + offset 16384) +// output: int32_t (normalized, not saturated) +// state: filter state array; length = 8 +void RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/5486 + WebRtcSpl_LPBy2IntToInt(const int32_t* in, + int32_t len, + int32_t* out, + int32_t* state) { + int32_t tmp0, tmp1, diff; + int32_t i; + + len >>= 1; + + // lower allpass filter: odd input -> even output samples + in++; + // initial state of polyphase delay element + tmp0 = state[12]; + for (i = 0; i < len; i++) { + diff = tmp0 - state[1]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[0] + diff * kResampleAllpass[1][0]; + state[0] = tmp0; + diff = tmp1 - state[2]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[1] + diff * kResampleAllpass[1][1]; + state[1] = tmp1; + diff = tmp0 - state[3]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[3] = state[2] + diff * kResampleAllpass[1][2]; + state[2] = tmp0; + + // scale down, round and store + out[i << 1] = state[3] >> 1; + tmp0 = in[i << 1]; + } + in--; + + // upper allpass filter: even input -> even output samples + for (i = 0; i < len; i++) { + tmp0 = in[i << 1]; + diff = tmp0 - state[5]; + // UBSan: -794814117 - 1566149201 cannot be represented in type 'int' + + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[4] + diff * kResampleAllpass[0][0]; + state[4] = tmp0; + diff = tmp1 - state[6]; + // scale down and round + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[5] + diff * kResampleAllpass[0][1]; + state[5] = tmp1; + diff = tmp0 - state[7]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[7] = state[6] + diff * kResampleAllpass[0][2]; + state[6] = tmp0; + + // average the two allpass outputs, scale down and store + out[i << 1] = (out[i << 1] + (state[7] >> 1)) >> 15; + } + + // switch to odd output samples + out++; + + // lower allpass filter: even input -> odd output samples + for (i = 0; i < len; i++) { + tmp0 = in[i << 1]; + diff = tmp0 - state[9]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[8] + diff * kResampleAllpass[1][0]; + state[8] = tmp0; + diff = tmp1 - state[10]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[9] + diff * kResampleAllpass[1][1]; + state[9] = tmp1; + diff = tmp0 - state[11]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[11] = state[10] + diff * kResampleAllpass[1][2]; + state[10] = tmp0; + + // scale down, round and store + out[i << 1] = state[11] >> 1; + } + + // upper allpass filter: odd input -> odd output samples + in++; + for (i = 0; i < len; i++) { + tmp0 = in[i << 1]; + diff = tmp0 - state[13]; + // scale down and round + diff = (diff + (1 << 13)) >> 14; + tmp1 = state[12] + diff * kResampleAllpass[0][0]; + state[12] = tmp0; + diff = tmp1 - state[14]; + // scale down and round + diff = diff >> 14; + if (diff < 0) + diff += 1; + tmp0 = state[13] + diff * kResampleAllpass[0][1]; + state[13] = tmp1; + diff = tmp0 - state[15]; + // scale down and truncate + diff = diff >> 14; + if (diff < 0) + diff += 1; + state[15] = state[14] + diff * kResampleAllpass[0][2]; + state[14] = tmp0; + + // average the two allpass outputs, scale down and store + out[i << 1] = (out[i << 1] + (state[15] >> 1)) >> 15; + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/resample_by_2_internal.h b/pkg/apm/webrtc/common_audio/signal_processing/resample_by_2_internal.h new file mode 100644 index 00000000..145395a4 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/resample_by_2_internal.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This header file contains some internal resampling functions. + * + */ + +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_RESAMPLE_BY_2_INTERNAL_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_RESAMPLE_BY_2_INTERNAL_H_ + +#include + +/******************************************************************* + * resample_by_2_fast.c + * Functions for internal use in the other resample functions + ******************************************************************/ +void WebRtcSpl_DownBy2IntToShort(int32_t* in, + int32_t len, + int16_t* out, + int32_t* state); + +void WebRtcSpl_DownBy2ShortToInt(const int16_t* in, + int32_t len, + int32_t* out, + int32_t* state); + +void WebRtcSpl_UpBy2ShortToInt(const int16_t* in, + int32_t len, + int32_t* out, + int32_t* state); + +void WebRtcSpl_UpBy2IntToInt(const int32_t* in, + int32_t len, + int32_t* out, + int32_t* state); + +void WebRtcSpl_UpBy2IntToShort(const int32_t* in, + int32_t len, + int16_t* out, + int32_t* state); + +void WebRtcSpl_LPBy2ShortToInt(const int16_t* in, + int32_t len, + int32_t* out, + int32_t* state); + +void WebRtcSpl_LPBy2IntToInt(const int32_t* in, + int32_t len, + int32_t* out, + int32_t* state); + +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_RESAMPLE_BY_2_INTERNAL_H_ diff --git a/pkg/apm/webrtc/common_audio/signal_processing/resample_fractional.c b/pkg/apm/webrtc/common_audio/signal_processing/resample_fractional.c new file mode 100644 index 00000000..ea7af815 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/resample_fractional.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the resampling functions between 48, 44, 32 and 24 kHz. + * The description headers can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +// interpolation coefficients +static const int16_t kCoefficients48To32[2][8] = { + {778, -2050, 1087, 23285, 12903, -3783, 441, 222}, + {222, 441, -3783, 12903, 23285, 1087, -2050, 778}}; + +static const int16_t kCoefficients32To24[3][8] = { + {767, -2362, 2434, 24406, 10620, -3838, 721, 90}, + {386, -381, -2646, 19062, 19062, -2646, -381, 386}, + {90, 721, -3838, 10620, 24406, 2434, -2362, 767}}; + +static const int16_t kCoefficients44To32[4][9] = { + {117, -669, 2245, -6183, 26267, 13529, -3245, 845, -138}, + {-101, 612, -2283, 8532, 29790, -5138, 1789, -524, 91}, + {50, -292, 1016, -3064, 32010, 3933, -1147, 315, -53}, + {-156, 974, -3863, 18603, 21691, -6246, 2353, -712, 126}}; + +// Resampling ratio: 2/3 +// input: int32_t (normalized, not saturated) :: size 3 * K +// output: int32_t (shifted 15 positions to the left, + offset 16384) :: size 2 +// * K +// K: number of blocks + +void WebRtcSpl_Resample48khzTo32khz(const int32_t* In, int32_t* Out, size_t K) { + ///////////////////////////////////////////////////////////// + // Filter operation: + // + // Perform resampling (3 input samples -> 2 output samples); + // process in sub blocks of size 3 samples. + int32_t tmp; + size_t m; + + for (m = 0; m < K; m++) { + tmp = 1 << 14; + tmp += kCoefficients48To32[0][0] * In[0]; + tmp += kCoefficients48To32[0][1] * In[1]; + tmp += kCoefficients48To32[0][2] * In[2]; + tmp += kCoefficients48To32[0][3] * In[3]; + tmp += kCoefficients48To32[0][4] * In[4]; + tmp += kCoefficients48To32[0][5] * In[5]; + tmp += kCoefficients48To32[0][6] * In[6]; + tmp += kCoefficients48To32[0][7] * In[7]; + Out[0] = tmp; + + tmp = 1 << 14; + tmp += kCoefficients48To32[1][0] * In[1]; + tmp += kCoefficients48To32[1][1] * In[2]; + tmp += kCoefficients48To32[1][2] * In[3]; + tmp += kCoefficients48To32[1][3] * In[4]; + tmp += kCoefficients48To32[1][4] * In[5]; + tmp += kCoefficients48To32[1][5] * In[6]; + tmp += kCoefficients48To32[1][6] * In[7]; + tmp += kCoefficients48To32[1][7] * In[8]; + Out[1] = tmp; + + // update pointers + In += 3; + Out += 2; + } +} + +// Resampling ratio: 3/4 +// input: int32_t (normalized, not saturated) :: size 4 * K +// output: int32_t (shifted 15 positions to the left, + offset 16384) :: size 3 +// * K +// K: number of blocks + +void WebRtcSpl_Resample32khzTo24khz(const int32_t* In, int32_t* Out, size_t K) { + ///////////////////////////////////////////////////////////// + // Filter operation: + // + // Perform resampling (4 input samples -> 3 output samples); + // process in sub blocks of size 4 samples. + size_t m; + int32_t tmp; + + for (m = 0; m < K; m++) { + tmp = 1 << 14; + tmp += kCoefficients32To24[0][0] * In[0]; + tmp += kCoefficients32To24[0][1] * In[1]; + tmp += kCoefficients32To24[0][2] * In[2]; + tmp += kCoefficients32To24[0][3] * In[3]; + tmp += kCoefficients32To24[0][4] * In[4]; + tmp += kCoefficients32To24[0][5] * In[5]; + tmp += kCoefficients32To24[0][6] * In[6]; + tmp += kCoefficients32To24[0][7] * In[7]; + Out[0] = tmp; + + tmp = 1 << 14; + tmp += kCoefficients32To24[1][0] * In[1]; + tmp += kCoefficients32To24[1][1] * In[2]; + tmp += kCoefficients32To24[1][2] * In[3]; + tmp += kCoefficients32To24[1][3] * In[4]; + tmp += kCoefficients32To24[1][4] * In[5]; + tmp += kCoefficients32To24[1][5] * In[6]; + tmp += kCoefficients32To24[1][6] * In[7]; + tmp += kCoefficients32To24[1][7] * In[8]; + Out[1] = tmp; + + tmp = 1 << 14; + tmp += kCoefficients32To24[2][0] * In[2]; + tmp += kCoefficients32To24[2][1] * In[3]; + tmp += kCoefficients32To24[2][2] * In[4]; + tmp += kCoefficients32To24[2][3] * In[5]; + tmp += kCoefficients32To24[2][4] * In[6]; + tmp += kCoefficients32To24[2][5] * In[7]; + tmp += kCoefficients32To24[2][6] * In[8]; + tmp += kCoefficients32To24[2][7] * In[9]; + Out[2] = tmp; + + // update pointers + In += 4; + Out += 3; + } +} + +// +// fractional resampling filters +// Fout = 11/16 * Fin +// Fout = 8/11 * Fin +// + +// compute two inner-products and store them to output array +static void WebRtcSpl_ResampDotProduct(const int32_t* in1, + const int32_t* in2, + const int16_t* coef_ptr, + int32_t* out1, + int32_t* out2) { + int32_t tmp1 = 16384; + int32_t tmp2 = 16384; + int16_t coef; + + coef = coef_ptr[0]; + tmp1 += coef * in1[0]; + tmp2 += coef * in2[-0]; + + coef = coef_ptr[1]; + tmp1 += coef * in1[1]; + tmp2 += coef * in2[-1]; + + coef = coef_ptr[2]; + tmp1 += coef * in1[2]; + tmp2 += coef * in2[-2]; + + coef = coef_ptr[3]; + tmp1 += coef * in1[3]; + tmp2 += coef * in2[-3]; + + coef = coef_ptr[4]; + tmp1 += coef * in1[4]; + tmp2 += coef * in2[-4]; + + coef = coef_ptr[5]; + tmp1 += coef * in1[5]; + tmp2 += coef * in2[-5]; + + coef = coef_ptr[6]; + tmp1 += coef * in1[6]; + tmp2 += coef * in2[-6]; + + coef = coef_ptr[7]; + tmp1 += coef * in1[7]; + tmp2 += coef * in2[-7]; + + coef = coef_ptr[8]; + *out1 = tmp1 + coef * in1[8]; + *out2 = tmp2 + coef * in2[-8]; +} + +// Resampling ratio: 8/11 +// input: int32_t (normalized, not saturated) :: size 11 * K +// output: int32_t (shifted 15 positions to the left, + offset 16384) :: size 8 +// * K +// K: number of blocks + +void WebRtcSpl_Resample44khzTo32khz(const int32_t* In, int32_t* Out, size_t K) { + ///////////////////////////////////////////////////////////// + // Filter operation: + // + // Perform resampling (11 input samples -> 8 output samples); + // process in sub blocks of size 11 samples. + int32_t tmp; + size_t m; + + for (m = 0; m < K; m++) { + tmp = 1 << 14; + + // first output sample + Out[0] = ((int32_t)In[3] << 15) + tmp; + + // sum and accumulate filter coefficients and input samples + tmp += kCoefficients44To32[3][0] * In[5]; + tmp += kCoefficients44To32[3][1] * In[6]; + tmp += kCoefficients44To32[3][2] * In[7]; + tmp += kCoefficients44To32[3][3] * In[8]; + tmp += kCoefficients44To32[3][4] * In[9]; + tmp += kCoefficients44To32[3][5] * In[10]; + tmp += kCoefficients44To32[3][6] * In[11]; + tmp += kCoefficients44To32[3][7] * In[12]; + tmp += kCoefficients44To32[3][8] * In[13]; + Out[4] = tmp; + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_ResampDotProduct(&In[0], &In[17], kCoefficients44To32[0], &Out[1], + &Out[7]); + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_ResampDotProduct(&In[2], &In[15], kCoefficients44To32[1], &Out[2], + &Out[6]); + + // sum and accumulate filter coefficients and input samples + WebRtcSpl_ResampDotProduct(&In[3], &In[14], kCoefficients44To32[2], &Out[3], + &Out[5]); + + // update pointers + In += 11; + Out += 8; + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/sp.go b/pkg/apm/webrtc/common_audio/signal_processing/sp.go new file mode 100644 index 00000000..9bb1f9ad --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/sp.go @@ -0,0 +1,15 @@ +//go:build console + +package signal_processing + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +// #cgo CFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CFLAGS: -DWEBRTC_WIN +// #cgo arm64 CFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/common_audio/signal_processing/spl_init.c b/pkg/apm/webrtc/common_audio/signal_processing/spl_init.c new file mode 100644 index 00000000..cf37d47b --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/spl_init.c @@ -0,0 +1,69 @@ +/* + * 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. + */ + +// Some code came from common/rtcd.c in the WebM project. + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +// TODO(bugs.webrtc.org/9553): These function pointers are useless. Refactor +// things so that we simply have a bunch of regular functions with different +// implementations for different platforms. + +#if defined(WEBRTC_HAS_NEON) + +const MaxAbsValueW16 WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16Neon; +const MaxAbsValueW32 WebRtcSpl_MaxAbsValueW32 = WebRtcSpl_MaxAbsValueW32Neon; +const MaxValueW16 WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16Neon; +const MaxValueW32 WebRtcSpl_MaxValueW32 = WebRtcSpl_MaxValueW32Neon; +const MinValueW16 WebRtcSpl_MinValueW16 = WebRtcSpl_MinValueW16Neon; +const MinValueW32 WebRtcSpl_MinValueW32 = WebRtcSpl_MinValueW32Neon; +const CrossCorrelation WebRtcSpl_CrossCorrelation = + WebRtcSpl_CrossCorrelationNeon; +const DownsampleFast WebRtcSpl_DownsampleFast = WebRtcSpl_DownsampleFastNeon; +const ScaleAndAddVectorsWithRound WebRtcSpl_ScaleAndAddVectorsWithRound = + WebRtcSpl_ScaleAndAddVectorsWithRoundC; + +#elif defined(MIPS32_LE) + +const MaxAbsValueW16 WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16_mips; +const MaxAbsValueW32 WebRtcSpl_MaxAbsValueW32 = +#ifdef MIPS_DSP_R1_LE + WebRtcSpl_MaxAbsValueW32_mips; +#else + WebRtcSpl_MaxAbsValueW32C; +#endif +const MaxValueW16 WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16_mips; +const MaxValueW32 WebRtcSpl_MaxValueW32 = WebRtcSpl_MaxValueW32_mips; +const MinValueW16 WebRtcSpl_MinValueW16 = WebRtcSpl_MinValueW16_mips; +const MinValueW32 WebRtcSpl_MinValueW32 = WebRtcSpl_MinValueW32_mips; +const CrossCorrelation WebRtcSpl_CrossCorrelation = + WebRtcSpl_CrossCorrelation_mips; +const DownsampleFast WebRtcSpl_DownsampleFast = WebRtcSpl_DownsampleFast_mips; +const ScaleAndAddVectorsWithRound WebRtcSpl_ScaleAndAddVectorsWithRound = +#ifdef MIPS_DSP_R1_LE + WebRtcSpl_ScaleAndAddVectorsWithRound_mips; +#else + WebRtcSpl_ScaleAndAddVectorsWithRoundC; +#endif + +#else + +const MaxAbsValueW16 WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16C; +const MaxAbsValueW32 WebRtcSpl_MaxAbsValueW32 = WebRtcSpl_MaxAbsValueW32C; +const MaxValueW16 WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16C; +const MaxValueW32 WebRtcSpl_MaxValueW32 = WebRtcSpl_MaxValueW32C; +const MinValueW16 WebRtcSpl_MinValueW16 = WebRtcSpl_MinValueW16C; +const MinValueW32 WebRtcSpl_MinValueW32 = WebRtcSpl_MinValueW32C; +const CrossCorrelation WebRtcSpl_CrossCorrelation = WebRtcSpl_CrossCorrelationC; +const DownsampleFast WebRtcSpl_DownsampleFast = WebRtcSpl_DownsampleFastC; +const ScaleAndAddVectorsWithRound WebRtcSpl_ScaleAndAddVectorsWithRound = + WebRtcSpl_ScaleAndAddVectorsWithRoundC; + +#endif diff --git a/pkg/apm/webrtc/common_audio/signal_processing/spl_inl.c b/pkg/apm/webrtc/common_audio/signal_processing/spl_inl.c new file mode 100644 index 00000000..01897f2f --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/spl_inl.c @@ -0,0 +1,24 @@ +/* + * 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 "common_audio/signal_processing/include/spl_inl.h" + +#include + +// Table used by WebRtcSpl_CountLeadingZeros32_NotBuiltin. For each uint32_t n +// that's a sequence of 0 bits followed by a sequence of 1 bits, the entry at +// index (n * 0x8c0b2891) >> 26 in this table gives the number of zero bits in +// n. +const int8_t kWebRtcSpl_CountLeadingZeros32_Table[64] = { + 32, 8, 17, -1, -1, 14, -1, -1, -1, 20, -1, -1, -1, 28, -1, 18, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 26, 25, 24, + 4, 11, 23, 31, 3, 7, 10, 16, 22, 30, -1, -1, 2, 6, 13, 9, + -1, 15, -1, 21, -1, 29, 19, -1, -1, -1, -1, -1, 1, 27, 5, 12, +}; diff --git a/pkg/apm/webrtc/common_audio/signal_processing/spl_sqrt.c b/pkg/apm/webrtc/common_audio/signal_processing/spl_sqrt.c new file mode 100644 index 00000000..2ef119d8 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/spl_sqrt.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_Sqrt(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" + +int32_t WebRtcSpl_SqrtLocal(int32_t in); + +int32_t WebRtcSpl_SqrtLocal(int32_t in) { + int16_t x_half, t16; + int32_t A, B, x2; + + /* The following block performs: + y=in/2 + x=y-2^30 + x_half=x/2^31 + t = 1 + (x_half) - 0.5*((x_half)^2) + 0.5*((x_half)^3) - 0.625*((x_half)^4) + + 0.875*((x_half)^5) + */ + + B = in / 2; + + B = B - ((int32_t)0x40000000); // B = in/2 - 1/2 + x_half = (int16_t)(B >> 16); // x_half = x/2 = (in-1)/2 + B = B + ((int32_t)0x40000000); // B = 1 + x/2 + B = B + + ((int32_t)0x40000000); // Add 0.5 twice (since 1.0 does not exist in Q31) + + x2 = ((int32_t)x_half) * ((int32_t)x_half) * 2; // A = (x/2)^2 + A = -x2; // A = -(x/2)^2 + B = B + (A >> 1); // B = 1 + x/2 - 0.5*(x/2)^2 + + A >>= 16; + A = A * A * 2; // A = (x/2)^4 + t16 = (int16_t)(A >> 16); + B += -20480 * t16 * 2; // B = B - 0.625*A + // After this, B = 1 + x/2 - 0.5*(x/2)^2 - 0.625*(x/2)^4 + + A = x_half * t16 * 2; // A = (x/2)^5 + t16 = (int16_t)(A >> 16); + B += 28672 * t16 * 2; // B = B + 0.875*A + // After this, B = 1 + x/2 - 0.5*(x/2)^2 - 0.625*(x/2)^4 + 0.875*(x/2)^5 + + t16 = (int16_t)(x2 >> 16); + A = x_half * t16 * 2; // A = x/2^3 + + B = B + (A >> 1); // B = B + 0.5*A + // After this, B = 1 + x/2 - 0.5*(x/2)^2 + 0.5*(x/2)^3 - 0.625*(x/2)^4 + + // 0.875*(x/2)^5 + + B = B + ((int32_t)32768); // Round off bit + + return B; +} + +int32_t WebRtcSpl_Sqrt(int32_t value) { + /* + Algorithm: + + Six term Taylor Series is used here to compute the square root of a number + y^0.5 = (1+x)^0.5 where x = y-1 + = 1+(x/2)-0.5*((x/2)^2+0.5*((x/2)^3-0.625*((x/2)^4+0.875*((x/2)^5) + 0.5 <= x < 1 + + Example of how the algorithm works, with ut=sqrt(in), and + with in=73632 and ut=271 (even shift value case): + + in=73632 + y= in/131072 + x=y-1 + t = 1 + (x/2) - 0.5*((x/2)^2) + 0.5*((x/2)^3) - 0.625*((x/2)^4) + + 0.875*((x/2)^5) ut=t*(1/sqrt(2))*512 + + or: + + in=73632 + in2=73632*2^14 + y= in2/2^31 + x=y-1 + t = 1 + (x/2) - 0.5*((x/2)^2) + 0.5*((x/2)^3) - 0.625*((x/2)^4) + + 0.875*((x/2)^5) ut=t*(1/sqrt(2)) ut2=ut*2^9 + + which gives: + + in = 73632 + in2 = 1206386688 + y = 0.56176757812500 + x = -0.43823242187500 + t = 0.74973506527313 + ut = 0.53014274874797 + ut2 = 2.714330873589594e+002 + + or: + + in=73632 + in2=73632*2^14 + y=in2/2 + x=y-2^30 + x_half=x/2^31 + t = 1 + (x_half) - 0.5*((x_half)^2) + 0.5*((x_half)^3) - 0.625*((x_half)^4) + + 0.875*((x_half)^5) + ut=t*(1/sqrt(2)) + ut2=ut*2^9 + + which gives: + + in = 73632 + in2 = 1206386688 + y = 603193344 + x = -470548480 + x_half = -0.21911621093750 + t = 0.74973506527313 + ut = 0.53014274874797 + ut2 = 2.714330873589594e+002 + + */ + + int16_t x_norm, nshift, t16, sh; + int32_t A; + + int16_t k_sqrt_2 = 23170; // 1/sqrt2 (==5a82) + + A = value; + + // The convention in this function is to calculate sqrt(abs(A)). Negate the + // input if it is negative. + if (A < 0) { + if (A == WEBRTC_SPL_WORD32_MIN) { + // This number cannot be held in an int32_t after negating. + // Map it to the maximum positive value. + A = WEBRTC_SPL_WORD32_MAX; + } else { + A = -A; + } + } else if (A == 0) { + return 0; // sqrt(0) = 0 + } + + sh = WebRtcSpl_NormW32(A); // # shifts to normalize A + A = WEBRTC_SPL_LSHIFT_W32(A, sh); // Normalize A + if (A < (WEBRTC_SPL_WORD32_MAX - 32767)) { + A = A + ((int32_t)32768); // Round off bit + } else { + A = WEBRTC_SPL_WORD32_MAX; + } + + x_norm = (int16_t)(A >> 16); // x_norm = AH + + nshift = (sh / 2); + RTC_DCHECK_GE(nshift, 0); + + A = (int32_t)WEBRTC_SPL_LSHIFT_W32((int32_t)x_norm, 16); + A = WEBRTC_SPL_ABS_W32(A); // A = abs(x_norm<<16) + A = WebRtcSpl_SqrtLocal(A); // A = sqrt(A) + + if (2 * nshift == sh) { + // Even shift value case + + t16 = (int16_t)(A >> 16); // t16 = AH + + A = k_sqrt_2 * t16 * 2; // A = 1/sqrt(2)*t16 + A = A + ((int32_t)32768); // Round off + A = A & ((int32_t)0x7fff0000); // Round off + + A >>= 15; // A = A>>16 + + } else { + A >>= 16; // A = A>>16 + } + + A = A & ((int32_t)0x0000ffff); + A >>= nshift; // De-normalize the result. + + return A; +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/splitting_filter.c b/pkg/apm/webrtc/common_audio/signal_processing/splitting_filter.c new file mode 100644 index 00000000..60ce5471 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/splitting_filter.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the splitting filter functions. + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" + +// Maximum number of samples in a low/high-band frame. +enum { + kMaxBandFrameLength = 320 // 10 ms at 64 kHz. +}; + +// QMF filter coefficients in Q16. +static const uint16_t WebRtcSpl_kAllPassFilter1[3] = {6418, 36982, 57261}; +static const uint16_t WebRtcSpl_kAllPassFilter2[3] = {21333, 49062, 63010}; + +/////////////////////////////////////////////////////////////////////////////////////////////// +// WebRtcSpl_AllPassQMF(...) +// +// Allpass filter used by the analysis and synthesis parts of the QMF filter. +// +// Input: +// - in_data : Input data sequence (Q10) +// - data_length : Length of data sequence (>2) +// - filter_coefficients : Filter coefficients (length 3, Q16) +// +// Input & Output: +// - filter_state : Filter state (length 6, Q10). +// +// Output: +// - out_data : Output data sequence (Q10), length equal to +// `data_length` +// + +static void WebRtcSpl_AllPassQMF(int32_t* in_data, + size_t data_length, + int32_t* out_data, + const uint16_t* filter_coefficients, + int32_t* filter_state) { + // The procedure is to filter the input with three first order all pass + // filters (cascade operations). + // + // a_3 + q^-1 a_2 + q^-1 a_1 + q^-1 + // y[n] = ----------- ----------- ----------- x[n] + // 1 + a_3q^-1 1 + a_2q^-1 1 + a_1q^-1 + // + // The input vector `filter_coefficients` includes these three filter + // coefficients. The filter state contains the in_data state, in_data[-1], + // followed by the out_data state, out_data[-1]. This is repeated for each + // cascade. The first cascade filter will filter the `in_data` and store + // the output in `out_data`. The second will the take the `out_data` as + // input and make an intermediate storage in `in_data`, to save memory. The + // third, and final, cascade filter operation takes the `in_data` (which is + // the output from the previous cascade filter) and store the output in + // `out_data`. Note that the input vector values are changed during the + // process. + size_t k; + int32_t diff; + // First all-pass cascade; filter from in_data to out_data. + + // Let y_i[n] indicate the output of cascade filter i (with filter + // coefficient a_i) at vector position n. Then the final output will be + // y[n] = y_3[n] + + // First loop, use the states stored in memory. + // "diff" should be safe from wrap around since max values are 2^25 + // diff = (x[0] - y_1[-1]) + diff = WebRtcSpl_SubSatW32(in_data[0], filter_state[1]); + // y_1[0] = x[-1] + a_1 * (x[0] - y_1[-1]) + out_data[0] = + WEBRTC_SPL_SCALEDIFF32(filter_coefficients[0], diff, filter_state[0]); + + // For the remaining loops, use previous values. + for (k = 1; k < data_length; k++) { + // diff = (x[n] - y_1[n-1]) + diff = WebRtcSpl_SubSatW32(in_data[k], out_data[k - 1]); + // y_1[n] = x[n-1] + a_1 * (x[n] - y_1[n-1]) + out_data[k] = + WEBRTC_SPL_SCALEDIFF32(filter_coefficients[0], diff, in_data[k - 1]); + } + + // Update states. + filter_state[0] = + in_data[data_length - 1]; // x[N-1], becomes x[-1] next time + filter_state[1] = + out_data[data_length - 1]; // y_1[N-1], becomes y_1[-1] next time + + // Second all-pass cascade; filter from out_data to in_data. + // diff = (y_1[0] - y_2[-1]) + diff = WebRtcSpl_SubSatW32(out_data[0], filter_state[3]); + // y_2[0] = y_1[-1] + a_2 * (y_1[0] - y_2[-1]) + in_data[0] = + WEBRTC_SPL_SCALEDIFF32(filter_coefficients[1], diff, filter_state[2]); + for (k = 1; k < data_length; k++) { + // diff = (y_1[n] - y_2[n-1]) + diff = WebRtcSpl_SubSatW32(out_data[k], in_data[k - 1]); + // y_2[0] = y_1[-1] + a_2 * (y_1[0] - y_2[-1]) + in_data[k] = + WEBRTC_SPL_SCALEDIFF32(filter_coefficients[1], diff, out_data[k - 1]); + } + + filter_state[2] = + out_data[data_length - 1]; // y_1[N-1], becomes y_1[-1] next time + filter_state[3] = + in_data[data_length - 1]; // y_2[N-1], becomes y_2[-1] next time + + // Third all-pass cascade; filter from in_data to out_data. + // diff = (y_2[0] - y[-1]) + diff = WebRtcSpl_SubSatW32(in_data[0], filter_state[5]); + // y[0] = y_2[-1] + a_3 * (y_2[0] - y[-1]) + out_data[0] = + WEBRTC_SPL_SCALEDIFF32(filter_coefficients[2], diff, filter_state[4]); + for (k = 1; k < data_length; k++) { + // diff = (y_2[n] - y[n-1]) + diff = WebRtcSpl_SubSatW32(in_data[k], out_data[k - 1]); + // y[n] = y_2[n-1] + a_3 * (y_2[n] - y[n-1]) + out_data[k] = + WEBRTC_SPL_SCALEDIFF32(filter_coefficients[2], diff, in_data[k - 1]); + } + filter_state[4] = + in_data[data_length - 1]; // y_2[N-1], becomes y_2[-1] next time + filter_state[5] = + out_data[data_length - 1]; // y[N-1], becomes y[-1] next time +} + +void WebRtcSpl_AnalysisQMF(const int16_t* in_data, + size_t in_data_length, + int16_t* low_band, + int16_t* high_band, + int32_t* filter_state1, + int32_t* filter_state2) { + size_t i; + int16_t k; + int32_t tmp; + int32_t half_in1[kMaxBandFrameLength]; + int32_t half_in2[kMaxBandFrameLength]; + int32_t filter1[kMaxBandFrameLength]; + int32_t filter2[kMaxBandFrameLength]; + const size_t band_length = in_data_length / 2; + RTC_DCHECK_EQ(0, in_data_length % 2); + RTC_DCHECK_LE(band_length, kMaxBandFrameLength); + + // Split even and odd samples. Also shift them to Q10. + for (i = 0, k = 0; i < band_length; i++, k += 2) { + half_in2[i] = ((int32_t)in_data[k]) * (1 << 10); + half_in1[i] = ((int32_t)in_data[k + 1]) * (1 << 10); + } + + // All pass filter even and odd samples, independently. + WebRtcSpl_AllPassQMF(half_in1, band_length, filter1, + WebRtcSpl_kAllPassFilter1, filter_state1); + WebRtcSpl_AllPassQMF(half_in2, band_length, filter2, + WebRtcSpl_kAllPassFilter2, filter_state2); + + // Take the sum and difference of filtered version of odd and even + // branches to get upper & lower band. + for (i = 0; i < band_length; i++) { + tmp = (filter1[i] + filter2[i] + 1024) >> 11; + low_band[i] = WebRtcSpl_SatW32ToW16(tmp); + + tmp = (filter1[i] - filter2[i] + 1024) >> 11; + high_band[i] = WebRtcSpl_SatW32ToW16(tmp); + } +} + +void WebRtcSpl_SynthesisQMF(const int16_t* low_band, + const int16_t* high_band, + size_t band_length, + int16_t* out_data, + int32_t* filter_state1, + int32_t* filter_state2) { + int32_t tmp; + int32_t half_in1[kMaxBandFrameLength]; + int32_t half_in2[kMaxBandFrameLength]; + int32_t filter1[kMaxBandFrameLength]; + int32_t filter2[kMaxBandFrameLength]; + size_t i; + int16_t k; + RTC_DCHECK_LE(band_length, kMaxBandFrameLength); + + // Obtain the sum and difference channels out of upper and lower-band + // channels. Also shift to Q10 domain. + for (i = 0; i < band_length; i++) { + tmp = (int32_t)low_band[i] + (int32_t)high_band[i]; + half_in1[i] = tmp * (1 << 10); + tmp = (int32_t)low_band[i] - (int32_t)high_band[i]; + half_in2[i] = tmp * (1 << 10); + } + + // all-pass filter the sum and difference channels + WebRtcSpl_AllPassQMF(half_in1, band_length, filter1, + WebRtcSpl_kAllPassFilter2, filter_state1); + WebRtcSpl_AllPassQMF(half_in2, band_length, filter2, + WebRtcSpl_kAllPassFilter1, filter_state2); + + // The filtered signals are even and odd samples of the output. Combine + // them. The signals are Q10 should shift them back to Q0 and take care of + // saturation. + for (i = 0, k = 0; i < band_length; i++) { + tmp = (filter2[i] + 512) >> 10; + out_data[k++] = WebRtcSpl_SatW32ToW16(tmp); + + tmp = (filter1[i] + 512) >> 10; + out_data[k++] = WebRtcSpl_SatW32ToW16(tmp); + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c b/pkg/apm/webrtc/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c new file mode 100644 index 00000000..07e845a5 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * This file contains the function WebRtcSpl_SqrtOfOneMinusXSquared(). + * The description header can be found in signal_processing_library.h + * + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +void WebRtcSpl_SqrtOfOneMinusXSquared(int16_t* xQ15, + size_t vector_length, + int16_t* yQ15) { + int32_t sq; + size_t m; + int16_t tmp; + + for (m = 0; m < vector_length; m++) { + tmp = xQ15[m]; + sq = tmp * tmp; // x^2 in Q30 + sq = 1073741823 - + sq; // 1-x^2, where 1 ~= 0.99999999906 is 1073741823 in Q30 + sq = WebRtcSpl_Sqrt(sq); // sqrt(1-x^2) in Q15 + yQ15[m] = (int16_t)sq; + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/vector_operations.c b/pkg/apm/webrtc/common_audio/signal_processing/vector_operations.c new file mode 100644 index 00000000..880605c3 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/vector_operations.c @@ -0,0 +1,79 @@ +/* + * 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 "common_audio/signal_processing/include/signal_processing_library.h" + +void WebRtcSpl_ReverseOrderMultArrayElements(int16_t* out, + const int16_t* in, + const int16_t* win, + size_t vector_length, + int16_t right_shifts) { + size_t i; + int16_t* outptr = out; + const int16_t* inptr = in; + const int16_t* winptr = win; + for (i = 0; i < vector_length; i++) { + *outptr++ = (int16_t)((*inptr++ * *winptr--) >> right_shifts); + } +} + +void WebRtcSpl_ElementwiseVectorMult(int16_t* out, + const int16_t* in, + const int16_t* win, + size_t vector_length, + int16_t right_shifts) { + size_t i; + int16_t* outptr = out; + const int16_t* inptr = in; + const int16_t* winptr = win; + for (i = 0; i < vector_length; i++) { + *outptr++ = (int16_t)((*inptr++ * *winptr++) >> right_shifts); + } +} + +void WebRtcSpl_AddVectorsAndShift(int16_t* out, + const int16_t* in1, + const int16_t* in2, + size_t vector_length, + int16_t right_shifts) { + size_t i; + int16_t* outptr = out; + const int16_t* in1ptr = in1; + const int16_t* in2ptr = in2; + for (i = vector_length; i > 0; i--) { + (*outptr++) = (int16_t)(((*in1ptr++) + (*in2ptr++)) >> right_shifts); + } +} + +void WebRtcSpl_AddAffineVectorToVector(int16_t* out, + const int16_t* in, + int16_t gain, + int32_t add_constant, + int16_t right_shifts, + size_t vector_length) { + size_t i; + + for (i = 0; i < vector_length; i++) { + out[i] += (int16_t)((in[i] * gain + add_constant) >> right_shifts); + } +} + +void WebRtcSpl_AffineTransformVector(int16_t* out, + const int16_t* in, + int16_t gain, + int32_t add_constant, + int16_t right_shifts, + size_t vector_length) { + size_t i; + + for (i = 0; i < vector_length; i++) { + out[i] = (int16_t)((in[i] * gain + add_constant) >> right_shifts); + } +} diff --git a/pkg/apm/webrtc/common_audio/signal_processing/vector_scaling_operations.c b/pkg/apm/webrtc/common_audio/signal_processing/vector_scaling_operations.c new file mode 100644 index 00000000..a280ebd3 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/signal_processing/vector_scaling_operations.c @@ -0,0 +1,163 @@ +/* + * 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. + */ + +/* + * This file contains implementations of the functions + * WebRtcSpl_VectorBitShiftW16() + * WebRtcSpl_VectorBitShiftW32() + * WebRtcSpl_VectorBitShiftW32ToW16() + * WebRtcSpl_ScaleVector() + * WebRtcSpl_ScaleVectorWithSat() + * WebRtcSpl_ScaleAndAddVectors() + * WebRtcSpl_ScaleAndAddVectorsWithRoundC() + */ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +void WebRtcSpl_VectorBitShiftW16(int16_t* res, + size_t length, + const int16_t* in, + int16_t right_shifts) { + size_t i; + + if (right_shifts > 0) { + for (i = length; i > 0; i--) { + (*res++) = ((*in++) >> right_shifts); + } + } else { + for (i = length; i > 0; i--) { + (*res++) = ((*in++) * (1 << (-right_shifts))); + } + } +} + +void WebRtcSpl_VectorBitShiftW32(int32_t* out_vector, + size_t vector_length, + const int32_t* in_vector, + int16_t right_shifts) { + size_t i; + + if (right_shifts > 0) { + for (i = vector_length; i > 0; i--) { + (*out_vector++) = ((*in_vector++) >> right_shifts); + } + } else { + for (i = vector_length; i > 0; i--) { + (*out_vector++) = ((*in_vector++) << (-right_shifts)); + } + } +} + +void WebRtcSpl_VectorBitShiftW32ToW16(int16_t* out, + size_t length, + const int32_t* in, + int right_shifts) { + size_t i; + int32_t tmp_w32; + + if (right_shifts >= 0) { + for (i = length; i > 0; i--) { + tmp_w32 = (*in++) >> right_shifts; + (*out++) = WebRtcSpl_SatW32ToW16(tmp_w32); + } + } else { + int left_shifts = -right_shifts; + for (i = length; i > 0; i--) { + tmp_w32 = (*in++) << left_shifts; + (*out++) = WebRtcSpl_SatW32ToW16(tmp_w32); + } + } +} + +void WebRtcSpl_ScaleVector(const int16_t* in_vector, + int16_t* out_vector, + int16_t gain, + size_t in_vector_length, + int16_t right_shifts) { + // Performs vector operation: out_vector = (gain*in_vector)>>right_shifts + size_t i; + const int16_t* inptr; + int16_t* outptr; + + inptr = in_vector; + outptr = out_vector; + + for (i = 0; i < in_vector_length; i++) { + *outptr++ = (int16_t)((*inptr++ * gain) >> right_shifts); + } +} + +void WebRtcSpl_ScaleVectorWithSat(const int16_t* in_vector, + int16_t* out_vector, + int16_t gain, + size_t in_vector_length, + int16_t right_shifts) { + // Performs vector operation: out_vector = (gain*in_vector)>>right_shifts + size_t i; + const int16_t* inptr; + int16_t* outptr; + + inptr = in_vector; + outptr = out_vector; + + for (i = 0; i < in_vector_length; i++) { + *outptr++ = WebRtcSpl_SatW32ToW16((*inptr++ * gain) >> right_shifts); + } +} + +void WebRtcSpl_ScaleAndAddVectors(const int16_t* in1, + int16_t gain1, + int shift1, + const int16_t* in2, + int16_t gain2, + int shift2, + int16_t* out, + size_t vector_length) { + // Performs vector operation: out = (gain1*in1)>>shift1 + (gain2*in2)>>shift2 + size_t i; + const int16_t* in1ptr; + const int16_t* in2ptr; + int16_t* outptr; + + in1ptr = in1; + in2ptr = in2; + outptr = out; + + for (i = 0; i < vector_length; i++) { + *outptr++ = (int16_t)((gain1 * *in1ptr++) >> shift1) + + (int16_t)((gain2 * *in2ptr++) >> shift2); + } +} + +// C version of WebRtcSpl_ScaleAndAddVectorsWithRound() for generic platforms. +int WebRtcSpl_ScaleAndAddVectorsWithRoundC(const int16_t* in_vector1, + int16_t in_vector1_scale, + const int16_t* in_vector2, + int16_t in_vector2_scale, + int right_shifts, + int16_t* out_vector, + size_t length) { + size_t i = 0; + int round_value = (1 << right_shifts) >> 1; + + if (in_vector1 == NULL || in_vector2 == NULL || out_vector == NULL || + length == 0 || right_shifts < 0) { + return -1; + } + + for (i = 0; i < length; i++) { + out_vector[i] = + (int16_t)((in_vector1[i] * in_vector1_scale + + in_vector2[i] * in_vector2_scale + round_value) >> + right_shifts); + } + + return 0; +} diff --git a/pkg/apm/webrtc/common_audio/smoothing_filter.cc b/pkg/apm/webrtc/common_audio/smoothing_filter.cc new file mode 100644 index 00000000..624182f3 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/smoothing_filter.cc @@ -0,0 +1,147 @@ +/* + * 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 "common_audio/smoothing_filter.h" + +#include + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +SmoothingFilterImpl::SmoothingFilterImpl(int init_time_ms) + : init_time_ms_(init_time_ms), + // Duing the initalization time, we use an increasing alpha. Specifically, + // alpha(n) = exp(-powf(init_factor_, n)), + // where `init_factor_` is chosen such that + // alpha(init_time_ms_) = exp(-1.0f / init_time_ms_), + init_factor_(init_time_ms_ == 0 + ? 0.0f + : powf(init_time_ms_, -1.0f / init_time_ms_)), + // `init_const_` is to a factor to help the calculation during + // initialization phase. + init_const_(init_time_ms_ == 0 + ? 0.0f + : init_time_ms_ - + powf(init_time_ms_, 1.0f - 1.0f / init_time_ms_)) { + UpdateAlpha(init_time_ms_); +} + +SmoothingFilterImpl::~SmoothingFilterImpl() = default; + +void SmoothingFilterImpl::AddSample(float sample) { + const int64_t now_ms = TimeMillis(); + + if (!init_end_time_ms_) { + // This is equivalent to assuming the filter has been receiving the same + // value as the first sample since time -infinity. + state_ = last_sample_ = sample; + init_end_time_ms_ = now_ms + init_time_ms_; + last_state_time_ms_ = now_ms; + return; + } + + ExtrapolateLastSample(now_ms); + last_sample_ = sample; +} + +std::optional SmoothingFilterImpl::GetAverage() { + if (!init_end_time_ms_) { + // `init_end_time_ms_` undefined since we have not received any sample. + return std::nullopt; + } + ExtrapolateLastSample(TimeMillis()); + return state_; +} + +bool SmoothingFilterImpl::SetTimeConstantMs(int time_constant_ms) { + if (!init_end_time_ms_ || last_state_time_ms_ < *init_end_time_ms_) { + return false; + } + UpdateAlpha(time_constant_ms); + return true; +} + +void SmoothingFilterImpl::UpdateAlpha(int time_constant_ms) { + alpha_ = time_constant_ms == 0 ? 0.0f : std::exp(-1.0f / time_constant_ms); +} + +void SmoothingFilterImpl::ExtrapolateLastSample(int64_t time_ms) { + RTC_DCHECK_GE(time_ms, last_state_time_ms_); + RTC_DCHECK(init_end_time_ms_); + + float multiplier = 0.0f; + + if (time_ms <= *init_end_time_ms_) { + // Current update is to be made during initialization phase. + // We update the state as if the `alpha` has been increased according + // alpha(n) = exp(-powf(init_factor_, n)), + // where n is the time (in millisecond) since the first sample received. + // With algebraic derivation as shown in the Appendix, we can find that the + // state can be updated in a similar manner as if alpha is a constant, + // except for a different multiplier. + if (init_time_ms_ == 0) { + // This means `init_factor_` = 0. + multiplier = 0.0f; + } else if (init_time_ms_ == 1) { + // This means `init_factor_` = 1. + multiplier = std::exp(last_state_time_ms_ - time_ms); + } else { + multiplier = std::exp( + -(powf(init_factor_, last_state_time_ms_ - *init_end_time_ms_) - + powf(init_factor_, time_ms - *init_end_time_ms_)) / + init_const_); + } + } else { + if (last_state_time_ms_ < *init_end_time_ms_) { + // The latest state update was made during initialization phase. + // We first extrapolate to the initialization time. + ExtrapolateLastSample(*init_end_time_ms_); + // Then extrapolate the rest by the following. + } + multiplier = powf(alpha_, time_ms - last_state_time_ms_); + } + + state_ = multiplier * state_ + (1.0f - multiplier) * last_sample_; + last_state_time_ms_ = time_ms; +} + +} // namespace webrtc + +// Appendix: derivation of extrapolation during initialization phase. +// (LaTeX syntax) +// Assuming +// \begin{align} +// y(n) &= \alpha_{n-1} y(n-1) + \left(1 - \alpha_{n-1}\right) x(m) \\* +// &= \left(\prod_{i=m}^{n-1} \alpha_i\right) y(m) + +// \left(1 - \prod_{i=m}^{n-1} \alpha_i \right) x(m) +// \end{align} +// Taking $\alpha_{n} = \exp(-\gamma^n)$, $\gamma$ denotes init\_factor\_, the +// multiplier becomes +// \begin{align} +// \prod_{i=m}^{n-1} \alpha_i +// &= \exp\left(-\sum_{i=m}^{n-1} \gamma^i \right) \\* +// &= \begin{cases} +// \exp\left(-\frac{\gamma^m - \gamma^n}{1 - \gamma} \right) +// & \gamma \neq 1 \\* +// m-n & \gamma = 1 +// \end{cases} +// \end{align} +// We know $\gamma = T^{-\frac{1}{T}}$, where $T$ denotes init\_time\_ms\_. Then +// $1 - \gamma$ approaches zero when $T$ increases. This can cause numerical +// difficulties. We multiply $T$ (if $T > 0$) to both numerator and denominator +// in the fraction. See. +// \begin{align} +// \frac{\gamma^m - \gamma^n}{1 - \gamma} +// &= \frac{T^\frac{T-m}{T} - T^\frac{T-n}{T}}{T - T^{1-\frac{1}{T}}} +// \end{align} diff --git a/pkg/apm/webrtc/common_audio/smoothing_filter.h b/pkg/apm/webrtc/common_audio/smoothing_filter.h new file mode 100644 index 00000000..488cc97a --- /dev/null +++ b/pkg/apm/webrtc/common_audio/smoothing_filter.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef COMMON_AUDIO_SMOOTHING_FILTER_H_ +#define COMMON_AUDIO_SMOOTHING_FILTER_H_ + +#include + +#include + +namespace webrtc { + +class SmoothingFilter { + public: + virtual ~SmoothingFilter() = default; + virtual void AddSample(float sample) = 0; + virtual std::optional GetAverage() = 0; + virtual bool SetTimeConstantMs(int time_constant_ms) = 0; +}; + +// SmoothingFilterImpl applies an exponential filter +// alpha = exp(-1.0 / time_constant_ms); +// y[t] = alpha * y[t-1] + (1 - alpha) * sample; +// This implies a sample rate of 1000 Hz, i.e., 1 sample / ms. +// But SmoothingFilterImpl allows sparse samples. All missing samples will be +// assumed to equal the last received sample. +class SmoothingFilterImpl final : public SmoothingFilter { + public: + // `init_time_ms` is initialization time. It defines a period starting from + // the arriving time of the first sample. During this period, the exponential + // filter uses a varying time constant so that a smaller time constant will be + // applied to the earlier samples. This is to allow the the filter to adapt to + // earlier samples quickly. After the initialization period, the time constant + // will be set to `init_time_ms` first and can be changed through + // `SetTimeConstantMs`. + explicit SmoothingFilterImpl(int init_time_ms); + + SmoothingFilterImpl() = delete; + SmoothingFilterImpl(const SmoothingFilterImpl&) = delete; + SmoothingFilterImpl& operator=(const SmoothingFilterImpl&) = delete; + + ~SmoothingFilterImpl() override; + + void AddSample(float sample) override; + std::optional GetAverage() override; + bool SetTimeConstantMs(int time_constant_ms) override; + + // Methods used for unittests. + float alpha() const { return alpha_; } + + private: + void UpdateAlpha(int time_constant_ms); + void ExtrapolateLastSample(int64_t time_ms); + + const int init_time_ms_; + const float init_factor_; + const float init_const_; + + std::optional init_end_time_ms_; + float last_sample_; + float alpha_; + float state_; + int64_t last_state_time_ms_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_SMOOTHING_FILTER_H_ diff --git a/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/fft_size_128.go b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/fft_size_128.go new file mode 100644 index 00000000..b243e9fd --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/fft_size_128.go @@ -0,0 +1,10 @@ +//go:build console + +package fft_size_128 + +// #cgo CXXFLAGS: -I${SRCDIR}/../../../.. -I${SRCDIR}/../../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.cc b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.cc new file mode 100644 index 00000000..b7a529d1 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.cc @@ -0,0 +1,548 @@ +/* + * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html + * Copyright Takuya OOURA, 1996-2001 + * + * You may use, copy, modify and distribute this code for any purpose (include + * commercial use) and without fee. Please refer to this package when you modify + * this code. + * + * Changes by the WebRTC authors: + * - Trivial type modifications. + * - Minimal code subset to do rdft of length 128. + * - Optimizations because of known length. + * - Removed the global variables by moving the code in to a class in order + * to make it thread safe. + * + * All changes are covered by the WebRTC license and IP grant: + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can 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/third_party/ooura/fft_size_128/ooura_fft.h" + +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h" +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" + +namespace webrtc { + +namespace { + +#if !(defined(MIPS_FPU_LE) || defined(WEBRTC_HAS_NEON)) +static void cft1st_128_C(float* a) { + const int n = 128; + int j, k1, k2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + // The processing of the first set of elements was simplified in C to avoid + // some operations (multiplication by zero or one, addition of two elements + // multiplied by the same weight, ...). + x0r = a[0] + a[2]; + x0i = a[1] + a[3]; + x1r = a[0] - a[2]; + x1i = a[1] - a[3]; + x2r = a[4] + a[6]; + x2i = a[5] + a[7]; + x3r = a[4] - a[6]; + x3i = a[5] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[2] = x1r - x3i; + a[3] = x1i + x3r; + a[6] = x1r + x3i; + a[7] = x1i - x3r; + wk1r = rdft_w[2]; + x0r = a[8] + a[10]; + x0i = a[9] + a[11]; + x1r = a[8] - a[10]; + x1i = a[9] - a[11]; + x2r = a[12] + a[14]; + x2i = a[13] + a[15]; + x3r = a[12] - a[14]; + x3i = a[13] - a[15]; + a[8] = x0r + x2r; + a[9] = x0i + x2i; + a[12] = x2i - x0i; + a[13] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[10] = wk1r * (x0r - x0i); + a[11] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[14] = wk1r * (x0i - x0r); + a[15] = wk1r * (x0i + x0r); + k1 = 0; + for (j = 16; j < n; j += 16) { + k1 += 2; + k2 = 2 * k1; + wk2r = rdft_w[k1 + 0]; + wk2i = rdft_w[k1 + 1]; + wk1r = rdft_w[k2 + 0]; + wk1i = rdft_w[k2 + 1]; + wk3r = rdft_wk3ri_first[k1 + 0]; + wk3i = rdft_wk3ri_first[k1 + 1]; + x0r = a[j + 0] + a[j + 2]; + x0i = a[j + 1] + a[j + 3]; + x1r = a[j + 0] - a[j + 2]; + x1i = a[j + 1] - a[j + 3]; + x2r = a[j + 4] + a[j + 6]; + x2i = a[j + 5] + a[j + 7]; + x3r = a[j + 4] - a[j + 6]; + x3i = a[j + 5] - a[j + 7]; + a[j + 0] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 4] = wk2r * x0r - wk2i * x0i; + a[j + 5] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 2] = wk1r * x0r - wk1i * x0i; + a[j + 3] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 6] = wk3r * x0r - wk3i * x0i; + a[j + 7] = wk3r * x0i + wk3i * x0r; + wk1r = rdft_w[k2 + 2]; + wk1i = rdft_w[k2 + 3]; + wk3r = rdft_wk3ri_second[k1 + 0]; + wk3i = rdft_wk3ri_second[k1 + 1]; + x0r = a[j + 8] + a[j + 10]; + x0i = a[j + 9] + a[j + 11]; + x1r = a[j + 8] - a[j + 10]; + x1i = a[j + 9] - a[j + 11]; + x2r = a[j + 12] + a[j + 14]; + x2i = a[j + 13] + a[j + 15]; + x3r = a[j + 12] - a[j + 14]; + x3i = a[j + 13] - a[j + 15]; + a[j + 8] = x0r + x2r; + a[j + 9] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 12] = -wk2i * x0r - wk2r * x0i; + a[j + 13] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 10] = wk1r * x0r - wk1i * x0i; + a[j + 11] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 14] = wk3r * x0r - wk3i * x0i; + a[j + 15] = wk3r * x0i + wk3i * x0r; + } +} + +static void cftmdl_128_C(float* a) { + const int l = 8; + const int n = 128; + const int m = 32; + int j0, j1, j2, j3, k, k1, k2, m2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + for (j0 = 0; j0 < l; j0 += 2) { + j1 = j0 + 8; + j2 = j0 + 16; + j3 = j0 + 24; + x0r = a[j0 + 0] + a[j1 + 0]; + x0i = a[j0 + 1] + a[j1 + 1]; + x1r = a[j0 + 0] - a[j1 + 0]; + x1i = a[j0 + 1] - a[j1 + 1]; + x2r = a[j2 + 0] + a[j3 + 0]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2 + 0] - a[j3 + 0]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j0 + 0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j2 + 0] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1 + 0] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3 + 0] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + wk1r = rdft_w[2]; + for (j0 = m; j0 < l + m; j0 += 2) { + j1 = j0 + 8; + j2 = j0 + 16; + j3 = j0 + 24; + x0r = a[j0 + 0] + a[j1 + 0]; + x0i = a[j0 + 1] + a[j1 + 1]; + x1r = a[j0 + 0] - a[j1 + 0]; + x1i = a[j0 + 1] - a[j1 + 1]; + x2r = a[j2 + 0] + a[j3 + 0]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2 + 0] - a[j3 + 0]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j0 + 0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j2 + 0] = x2i - x0i; + a[j2 + 1] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1 + 0] = wk1r * (x0r - x0i); + a[j1 + 1] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[j3 + 0] = wk1r * (x0i - x0r); + a[j3 + 1] = wk1r * (x0i + x0r); + } + k1 = 0; + m2 = 2 * m; + for (k = m2; k < n; k += m2) { + k1 += 2; + k2 = 2 * k1; + wk2r = rdft_w[k1 + 0]; + wk2i = rdft_w[k1 + 1]; + wk1r = rdft_w[k2 + 0]; + wk1i = rdft_w[k2 + 1]; + wk3r = rdft_wk3ri_first[k1 + 0]; + wk3i = rdft_wk3ri_first[k1 + 1]; + for (j0 = k; j0 < l + k; j0 += 2) { + j1 = j0 + 8; + j2 = j0 + 16; + j3 = j0 + 24; + x0r = a[j0 + 0] + a[j1 + 0]; + x0i = a[j0 + 1] + a[j1 + 1]; + x1r = a[j0 + 0] - a[j1 + 0]; + x1i = a[j0 + 1] - a[j1 + 1]; + x2r = a[j2 + 0] + a[j3 + 0]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2 + 0] - a[j3 + 0]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j0 + 0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2 + 0] = wk2r * x0r - wk2i * x0i; + a[j2 + 1] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1 + 0] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3 + 0] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + wk1r = rdft_w[k2 + 2]; + wk1i = rdft_w[k2 + 3]; + wk3r = rdft_wk3ri_second[k1 + 0]; + wk3i = rdft_wk3ri_second[k1 + 1]; + for (j0 = k + m; j0 < l + (k + m); j0 += 2) { + j1 = j0 + 8; + j2 = j0 + 16; + j3 = j0 + 24; + x0r = a[j0 + 0] + a[j1 + 0]; + x0i = a[j0 + 1] + a[j1 + 1]; + x1r = a[j0 + 0] - a[j1 + 0]; + x1i = a[j0 + 1] - a[j1 + 1]; + x2r = a[j2 + 0] + a[j3 + 0]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2 + 0] - a[j3 + 0]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j0 + 0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2 + 0] = -wk2i * x0r - wk2r * x0i; + a[j2 + 1] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1 + 0] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3 + 0] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + } +} + +static void rftfsub_128_C(float* a) { + const float* c = rdft_w + 32; + int j1, j2, k1, k2; + float wkr, wki, xr, xi, yr, yi; + + for (j1 = 1, j2 = 2; j2 < 64; j1 += 1, j2 += 2) { + k2 = 128 - j2; + k1 = 32 - j1; + wkr = 0.5f - c[k1]; + wki = c[j1]; + xr = a[j2 + 0] - a[k2 + 0]; + xi = a[j2 + 1] + a[k2 + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j2 + 0] -= yr; + a[j2 + 1] -= yi; + a[k2 + 0] += yr; + a[k2 + 1] -= yi; + } +} + +static void rftbsub_128_C(float* a) { + const float* c = rdft_w + 32; + int j1, j2, k1, k2; + float wkr, wki, xr, xi, yr, yi; + + a[1] = -a[1]; + for (j1 = 1, j2 = 2; j2 < 64; j1 += 1, j2 += 2) { + k2 = 128 - j2; + k1 = 32 - j1; + wkr = 0.5f - c[k1]; + wki = c[j1]; + xr = a[j2 + 0] - a[k2 + 0]; + xi = a[j2 + 1] + a[k2 + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j2 + 0] = a[j2 + 0] - yr; + a[j2 + 1] = yi - a[j2 + 1]; + a[k2 + 0] = yr + a[k2 + 0]; + a[k2 + 1] = yi - a[k2 + 1]; + } + a[65] = -a[65]; +} +#endif + +} // namespace + +OouraFft::OouraFft([[maybe_unused]] bool sse2_available) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + use_sse2_ = sse2_available; +#else + use_sse2_ = false; +#endif +} + +OouraFft::OouraFft() { +#if defined(WEBRTC_ARCH_X86_FAMILY) + use_sse2_ = (GetCPUInfo(kSSE2) != 0); +#else + use_sse2_ = false; +#endif +} + +OouraFft::~OouraFft() = default; + +void OouraFft::Fft(float* a) const { + float xi; + bitrv2_128(a); + cftfsub_128(a); + rftfsub_128(a); + xi = a[0] - a[1]; + a[0] += a[1]; + a[1] = xi; +} +void OouraFft::InverseFft(float* a) const { + a[1] = 0.5f * (a[0] - a[1]); + a[0] -= a[1]; + rftbsub_128(a); + bitrv2_128(a); + cftbsub_128(a); +} + +void OouraFft::cft1st_128(float* a) const { +#if defined(MIPS_FPU_LE) + cft1st_128_mips(a); +#elif defined(WEBRTC_HAS_NEON) + cft1st_128_neon(a); +#elif defined(WEBRTC_ARCH_X86_FAMILY) + if (use_sse2_) { + cft1st_128_SSE2(a); + } else { + cft1st_128_C(a); + } +#else + cft1st_128_C(a); +#endif +} +void OouraFft::cftmdl_128(float* a) const { +#if defined(MIPS_FPU_LE) + cftmdl_128_mips(a); +#elif defined(WEBRTC_HAS_NEON) + cftmdl_128_neon(a); +#elif defined(WEBRTC_ARCH_X86_FAMILY) + if (use_sse2_) { + cftmdl_128_SSE2(a); + } else { + cftmdl_128_C(a); + } +#else + cftmdl_128_C(a); +#endif +} +void OouraFft::rftfsub_128(float* a) const { +#if defined(MIPS_FPU_LE) + rftfsub_128_mips(a); +#elif defined(WEBRTC_HAS_NEON) + rftfsub_128_neon(a); +#elif defined(WEBRTC_ARCH_X86_FAMILY) + if (use_sse2_) { + rftfsub_128_SSE2(a); + } else { + rftfsub_128_C(a); + } +#else + rftfsub_128_C(a); +#endif +} + +void OouraFft::rftbsub_128(float* a) const { +#if defined(MIPS_FPU_LE) + rftbsub_128_mips(a); +#elif defined(WEBRTC_HAS_NEON) + rftbsub_128_neon(a); +#elif defined(WEBRTC_ARCH_X86_FAMILY) + if (use_sse2_) { + rftbsub_128_SSE2(a); + } else { + rftbsub_128_C(a); + } +#else + rftbsub_128_C(a); +#endif +} + +void OouraFft::cftbsub_128(float* a) const { + int j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + cft1st_128(a); + cftmdl_128(a); + l = 32; + + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = -a[j + 1] - a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = -a[j + 1] + a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i + x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i - x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i + x3r; + } +} + +void OouraFft::cftfsub_128(float* a) const { + int j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + cft1st_128(a); + cftmdl_128(a); + l = 32; + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } +} + +void OouraFft::bitrv2_128(float* a) const { + /* + Following things have been attempted but are no faster: + (a) Storing the swap indexes in a LUT (index calculations are done + for 'free' while waiting on memory/L1). + (b) Consolidate the load/store of two consecutive floats by a 64 bit + integer (execution is memory/L1 bound). + (c) Do a mix of floats and 64 bit integer to maximize register + utilization (execution is memory/L1 bound). + (d) Replacing ip[i] by ((k<<31)>>25) + ((k >> 1)<<5). + (e) Hard-coding of the offsets to completely eliminates index + calculations. + */ + + unsigned int j, j1, k, k1; + float xr, xi, yr, yi; + + const int ip[4] = {0, 64, 32, 96}; + for (k = 0; k < 4; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1 + 0]; + xi = a[j1 + 1]; + yr = a[k1 + 0]; + yi = a[k1 + 1]; + a[j1 + 0] = yr; + a[j1 + 1] = yi; + a[k1 + 0] = xr; + a[k1 + 1] = xi; + j1 += 8; + k1 += 16; + xr = a[j1 + 0]; + xi = a[j1 + 1]; + yr = a[k1 + 0]; + yi = a[k1 + 1]; + a[j1 + 0] = yr; + a[j1 + 1] = yi; + a[k1 + 0] = xr; + a[k1 + 1] = xi; + j1 += 8; + k1 -= 8; + xr = a[j1 + 0]; + xi = a[j1 + 1]; + yr = a[k1 + 0]; + yi = a[k1 + 1]; + a[j1 + 0] = yr; + a[j1 + 1] = yi; + a[k1 + 0] = xr; + a[k1 + 1] = xi; + j1 += 8; + k1 += 16; + xr = a[j1 + 0]; + xi = a[j1 + 1]; + yr = a[k1 + 0]; + yi = a[k1 + 1]; + a[j1 + 0] = yr; + a[j1 + 1] = yi; + a[k1 + 0] = xr; + a[k1 + 1] = xi; + } + j1 = 2 * k + 8 + ip[k]; + k1 = j1 + 8; + xr = a[j1 + 0]; + xi = a[j1 + 1]; + yr = a[k1 + 0]; + yi = a[k1 + 1]; + a[j1 + 0] = yr; + a[j1 + 1] = yi; + a[k1 + 0] = xr; + a[k1 + 1] = xi; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.h b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.h new file mode 100644 index 00000000..8273dfe5 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_H_ + +#include "rtc_base/system/arch.h" + +namespace webrtc { + +#if defined(WEBRTC_ARCH_X86_FAMILY) +void cft1st_128_SSE2(float* a); +void cftmdl_128_SSE2(float* a); +void rftfsub_128_SSE2(float* a); +void rftbsub_128_SSE2(float* a); +#endif + +#if defined(MIPS_FPU_LE) +void cft1st_128_mips(float* a); +void cftmdl_128_mips(float* a); +void rftfsub_128_mips(float* a); +void rftbsub_128_mips(float* a); +#endif + +#if defined(WEBRTC_HAS_NEON) +void cft1st_128_neon(float* a); +void cftmdl_128_neon(float* a); +void rftfsub_128_neon(float* a); +void rftbsub_128_neon(float* a); +#endif + +class OouraFft { + public: + // Ctor allowing the availability of SSE2 support to be specified. + explicit OouraFft(bool sse2_available); + + // Deprecated: This Ctor will soon be removed. + OouraFft(); + ~OouraFft(); + void Fft(float* a) const; + void InverseFft(float* a) const; + + private: + void cft1st_128(float* a) const; + void cftmdl_128(float* a) const; + void rftfsub_128(float* a) const; + void rftbsub_128(float* a) const; + + void cftfsub_128(float* a) const; + void cftbsub_128(float* a) const; + void bitrv2_128(float* a) const; + bool use_sse2_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_H_ diff --git a/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_neon_arm64.cc b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_neon_arm64.cc new file mode 100644 index 00000000..acab9722 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_neon_arm64.cc @@ -0,0 +1,351 @@ +/* + * 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. + */ + +/* + * The rdft AEC algorithm, neon version of speed-critical functions. + * + * Based on the sse2 version. + */ + +#include + +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h" + +namespace webrtc { + +#if defined(WEBRTC_HAS_NEON) +void cft1st_128_neon(float* a) { + const float32x4_t vec_swap_sign = vld1q_f32((float32_t*)k_swap_sign); + int j, k2; + + for (k2 = 0, j = 0; j < 128; j += 16, k2 += 4) { + float32x4_t a00v = vld1q_f32(&a[j + 0]); + float32x4_t a04v = vld1q_f32(&a[j + 4]); + float32x4_t a08v = vld1q_f32(&a[j + 8]); + float32x4_t a12v = vld1q_f32(&a[j + 12]); + float32x4_t a01v = vcombine_f32(vget_low_f32(a00v), vget_low_f32(a08v)); + float32x4_t a23v = vcombine_f32(vget_high_f32(a00v), vget_high_f32(a08v)); + float32x4_t a45v = vcombine_f32(vget_low_f32(a04v), vget_low_f32(a12v)); + float32x4_t a67v = vcombine_f32(vget_high_f32(a04v), vget_high_f32(a12v)); + const float32x4_t wk1rv = vld1q_f32(&rdft_wk1r[k2]); + const float32x4_t wk1iv = vld1q_f32(&rdft_wk1i[k2]); + const float32x4_t wk2rv = vld1q_f32(&rdft_wk2r[k2]); + const float32x4_t wk2iv = vld1q_f32(&rdft_wk2i[k2]); + const float32x4_t wk3rv = vld1q_f32(&rdft_wk3r[k2]); + const float32x4_t wk3iv = vld1q_f32(&rdft_wk3i[k2]); + float32x4_t x0v = vaddq_f32(a01v, a23v); + const float32x4_t x1v = vsubq_f32(a01v, a23v); + const float32x4_t x2v = vaddq_f32(a45v, a67v); + const float32x4_t x3v = vsubq_f32(a45v, a67v); + const float32x4_t x3w = vrev64q_f32(x3v); + float32x4_t x0w; + a01v = vaddq_f32(x0v, x2v); + x0v = vsubq_f32(x0v, x2v); + x0w = vrev64q_f32(x0v); + a45v = vmulq_f32(wk2rv, x0v); + a45v = vmlaq_f32(a45v, wk2iv, x0w); + x0v = vmlaq_f32(x1v, x3w, vec_swap_sign); + x0w = vrev64q_f32(x0v); + a23v = vmulq_f32(wk1rv, x0v); + a23v = vmlaq_f32(a23v, wk1iv, x0w); + x0v = vmlsq_f32(x1v, x3w, vec_swap_sign); + x0w = vrev64q_f32(x0v); + a67v = vmulq_f32(wk3rv, x0v); + a67v = vmlaq_f32(a67v, wk3iv, x0w); + a00v = vcombine_f32(vget_low_f32(a01v), vget_low_f32(a23v)); + a04v = vcombine_f32(vget_low_f32(a45v), vget_low_f32(a67v)); + a08v = vcombine_f32(vget_high_f32(a01v), vget_high_f32(a23v)); + a12v = vcombine_f32(vget_high_f32(a45v), vget_high_f32(a67v)); + vst1q_f32(&a[j + 0], a00v); + vst1q_f32(&a[j + 4], a04v); + vst1q_f32(&a[j + 8], a08v); + vst1q_f32(&a[j + 12], a12v); + } +} + +void cftmdl_128_neon(float* a) { + int j; + const int l = 8; + const float32x4_t vec_swap_sign = vld1q_f32((float32_t*)k_swap_sign); + float32x4_t wk1rv = vld1q_f32(cftmdl_wk1r); + + for (j = 0; j < l; j += 2) { + const float32x2_t a_00 = vld1_f32(&a[j + 0]); + const float32x2_t a_08 = vld1_f32(&a[j + 8]); + const float32x2_t a_32 = vld1_f32(&a[j + 32]); + const float32x2_t a_40 = vld1_f32(&a[j + 40]); + const float32x4_t a_00_32 = vcombine_f32(a_00, a_32); + const float32x4_t a_08_40 = vcombine_f32(a_08, a_40); + const float32x4_t x0r0_0i0_0r1_x0i1 = vaddq_f32(a_00_32, a_08_40); + const float32x4_t x1r0_1i0_1r1_x1i1 = vsubq_f32(a_00_32, a_08_40); + const float32x2_t a_16 = vld1_f32(&a[j + 16]); + const float32x2_t a_24 = vld1_f32(&a[j + 24]); + const float32x2_t a_48 = vld1_f32(&a[j + 48]); + const float32x2_t a_56 = vld1_f32(&a[j + 56]); + const float32x4_t a_16_48 = vcombine_f32(a_16, a_48); + const float32x4_t a_24_56 = vcombine_f32(a_24, a_56); + const float32x4_t x2r0_2i0_2r1_x2i1 = vaddq_f32(a_16_48, a_24_56); + const float32x4_t x3r0_3i0_3r1_x3i1 = vsubq_f32(a_16_48, a_24_56); + const float32x4_t xx0 = vaddq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + const float32x4_t xx1 = vsubq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + const float32x4_t x3i0_3r0_3i1_x3r1 = vrev64q_f32(x3r0_3i0_3r1_x3i1); + const float32x4_t x1_x3_add = + vmlaq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); + const float32x4_t x1_x3_sub = + vmlsq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); + const float32x2_t yy0_a = vdup_lane_f32(vget_high_f32(x1_x3_add), 0); + const float32x2_t yy0_s = vdup_lane_f32(vget_high_f32(x1_x3_sub), 0); + const float32x4_t yy0_as = vcombine_f32(yy0_a, yy0_s); + const float32x2_t yy1_a = vdup_lane_f32(vget_high_f32(x1_x3_add), 1); + const float32x2_t yy1_s = vdup_lane_f32(vget_high_f32(x1_x3_sub), 1); + const float32x4_t yy1_as = vcombine_f32(yy1_a, yy1_s); + const float32x4_t yy0 = vmlaq_f32(yy0_as, vec_swap_sign, yy1_as); + const float32x4_t yy4 = vmulq_f32(wk1rv, yy0); + const float32x4_t xx1_rev = vrev64q_f32(xx1); + const float32x4_t yy4_rev = vrev64q_f32(yy4); + + vst1_f32(&a[j + 0], vget_low_f32(xx0)); + vst1_f32(&a[j + 32], vget_high_f32(xx0)); + vst1_f32(&a[j + 16], vget_low_f32(xx1)); + vst1_f32(&a[j + 48], vget_high_f32(xx1_rev)); + + a[j + 48] = -a[j + 48]; + + vst1_f32(&a[j + 8], vget_low_f32(x1_x3_add)); + vst1_f32(&a[j + 24], vget_low_f32(x1_x3_sub)); + vst1_f32(&a[j + 40], vget_low_f32(yy4)); + vst1_f32(&a[j + 56], vget_high_f32(yy4_rev)); + } + + { + const int k = 64; + const int k1 = 2; + const int k2 = 2 * k1; + const float32x4_t wk2rv = vld1q_f32(&rdft_wk2r[k2 + 0]); + const float32x4_t wk2iv = vld1q_f32(&rdft_wk2i[k2 + 0]); + const float32x4_t wk1iv = vld1q_f32(&rdft_wk1i[k2 + 0]); + const float32x4_t wk3rv = vld1q_f32(&rdft_wk3r[k2 + 0]); + const float32x4_t wk3iv = vld1q_f32(&rdft_wk3i[k2 + 0]); + wk1rv = vld1q_f32(&rdft_wk1r[k2 + 0]); + for (j = k; j < l + k; j += 2) { + const float32x2_t a_00 = vld1_f32(&a[j + 0]); + const float32x2_t a_08 = vld1_f32(&a[j + 8]); + const float32x2_t a_32 = vld1_f32(&a[j + 32]); + const float32x2_t a_40 = vld1_f32(&a[j + 40]); + const float32x4_t a_00_32 = vcombine_f32(a_00, a_32); + const float32x4_t a_08_40 = vcombine_f32(a_08, a_40); + const float32x4_t x0r0_0i0_0r1_x0i1 = vaddq_f32(a_00_32, a_08_40); + const float32x4_t x1r0_1i0_1r1_x1i1 = vsubq_f32(a_00_32, a_08_40); + const float32x2_t a_16 = vld1_f32(&a[j + 16]); + const float32x2_t a_24 = vld1_f32(&a[j + 24]); + const float32x2_t a_48 = vld1_f32(&a[j + 48]); + const float32x2_t a_56 = vld1_f32(&a[j + 56]); + const float32x4_t a_16_48 = vcombine_f32(a_16, a_48); + const float32x4_t a_24_56 = vcombine_f32(a_24, a_56); + const float32x4_t x2r0_2i0_2r1_x2i1 = vaddq_f32(a_16_48, a_24_56); + const float32x4_t x3r0_3i0_3r1_x3i1 = vsubq_f32(a_16_48, a_24_56); + const float32x4_t xx = vaddq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + const float32x4_t xx1 = vsubq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + const float32x4_t x3i0_3r0_3i1_x3r1 = vrev64q_f32(x3r0_3i0_3r1_x3i1); + const float32x4_t x1_x3_add = + vmlaq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); + const float32x4_t x1_x3_sub = + vmlsq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); + float32x4_t xx4 = vmulq_f32(wk2rv, xx1); + float32x4_t xx12 = vmulq_f32(wk1rv, x1_x3_add); + float32x4_t xx22 = vmulq_f32(wk3rv, x1_x3_sub); + xx4 = vmlaq_f32(xx4, wk2iv, vrev64q_f32(xx1)); + xx12 = vmlaq_f32(xx12, wk1iv, vrev64q_f32(x1_x3_add)); + xx22 = vmlaq_f32(xx22, wk3iv, vrev64q_f32(x1_x3_sub)); + + vst1_f32(&a[j + 0], vget_low_f32(xx)); + vst1_f32(&a[j + 32], vget_high_f32(xx)); + vst1_f32(&a[j + 16], vget_low_f32(xx4)); + vst1_f32(&a[j + 48], vget_high_f32(xx4)); + vst1_f32(&a[j + 8], vget_low_f32(xx12)); + vst1_f32(&a[j + 40], vget_high_f32(xx12)); + vst1_f32(&a[j + 24], vget_low_f32(xx22)); + vst1_f32(&a[j + 56], vget_high_f32(xx22)); + } + } +} + +__inline static float32x4_t reverse_order_f32x4(float32x4_t in) { + // A B C D -> C D A B + const float32x4_t rev = vcombine_f32(vget_high_f32(in), vget_low_f32(in)); + // C D A B -> D C B A + return vrev64q_f32(rev); +} + +void rftfsub_128_neon(float* a) { + const float* c = rdft_w + 32; + int j1, j2; + const float32x4_t mm_half = vdupq_n_f32(0.5f); + + // Vectorized code (four at once). + // Note: commented number are indexes for the first iteration of the loop. + for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) { + // Load 'wk'. + const float32x4_t c_j1 = vld1q_f32(&c[j1]); // 1, 2, 3, 4, + const float32x4_t c_k1 = vld1q_f32(&c[29 - j1]); // 28, 29, 30, 31, + const float32x4_t wkrt = vsubq_f32(mm_half, c_k1); // 28, 29, 30, 31, + const float32x4_t wkr_ = reverse_order_f32x4(wkrt); // 31, 30, 29, 28, + const float32x4_t wki_ = c_j1; // 1, 2, 3, 4, + // Load and shuffle 'a'. + // 2, 4, 6, 8, 3, 5, 7, 9 + float32x4x2_t a_j2_p = vld2q_f32(&a[0 + j2]); + // 120, 122, 124, 126, 121, 123, 125, 127, + const float32x4x2_t k2_0_4 = vld2q_f32(&a[122 - j2]); + // 126, 124, 122, 120 + const float32x4_t a_k2_p0 = reverse_order_f32x4(k2_0_4.val[0]); + // 127, 125, 123, 121 + const float32x4_t a_k2_p1 = reverse_order_f32x4(k2_0_4.val[1]); + // Calculate 'x'. + const float32x4_t xr_ = vsubq_f32(a_j2_p.val[0], a_k2_p0); + // 2-126, 4-124, 6-122, 8-120, + const float32x4_t xi_ = vaddq_f32(a_j2_p.val[1], a_k2_p1); + // 3-127, 5-125, 7-123, 9-121, + // Calculate product into 'y'. + // yr = wkr * xr - wki * xi; + // yi = wkr * xi + wki * xr; + const float32x4_t a_ = vmulq_f32(wkr_, xr_); + const float32x4_t b_ = vmulq_f32(wki_, xi_); + const float32x4_t c_ = vmulq_f32(wkr_, xi_); + const float32x4_t d_ = vmulq_f32(wki_, xr_); + const float32x4_t yr_ = vsubq_f32(a_, b_); // 2-126, 4-124, 6-122, 8-120, + const float32x4_t yi_ = vaddq_f32(c_, d_); // 3-127, 5-125, 7-123, 9-121, + // Update 'a'. + // a[j2 + 0] -= yr; + // a[j2 + 1] -= yi; + // a[k2 + 0] += yr; + // a[k2 + 1] -= yi; + // 126, 124, 122, 120, + const float32x4_t a_k2_p0n = vaddq_f32(a_k2_p0, yr_); + // 127, 125, 123, 121, + const float32x4_t a_k2_p1n = vsubq_f32(a_k2_p1, yi_); + // Shuffle in right order and store. + const float32x4_t a_k2_p0nr = vrev64q_f32(a_k2_p0n); + const float32x4_t a_k2_p1nr = vrev64q_f32(a_k2_p1n); + // 124, 125, 126, 127, 120, 121, 122, 123 + const float32x4x2_t a_k2_n = vzipq_f32(a_k2_p0nr, a_k2_p1nr); + // 2, 4, 6, 8, + a_j2_p.val[0] = vsubq_f32(a_j2_p.val[0], yr_); + // 3, 5, 7, 9, + a_j2_p.val[1] = vsubq_f32(a_j2_p.val[1], yi_); + // 2, 3, 4, 5, 6, 7, 8, 9, + vst2q_f32(&a[0 + j2], a_j2_p); + + vst1q_f32(&a[122 - j2], a_k2_n.val[1]); + vst1q_f32(&a[126 - j2], a_k2_n.val[0]); + } + + // Scalar code for the remaining items. + for (; j2 < 64; j1 += 1, j2 += 2) { + const int k2 = 128 - j2; + const int k1 = 32 - j1; + const float wkr = 0.5f - c[k1]; + const float wki = c[j1]; + const float xr = a[j2 + 0] - a[k2 + 0]; + const float xi = a[j2 + 1] + a[k2 + 1]; + const float yr = wkr * xr - wki * xi; + const float yi = wkr * xi + wki * xr; + a[j2 + 0] -= yr; + a[j2 + 1] -= yi; + a[k2 + 0] += yr; + a[k2 + 1] -= yi; + } +} + +void rftbsub_128_neon(float* a) { + const float* c = rdft_w + 32; + int j1, j2; + const float32x4_t mm_half = vdupq_n_f32(0.5f); + + a[1] = -a[1]; + // Vectorized code (four at once). + // Note: commented number are indexes for the first iteration of the loop. + for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) { + // Load 'wk'. + const float32x4_t c_j1 = vld1q_f32(&c[j1]); // 1, 2, 3, 4, + const float32x4_t c_k1 = vld1q_f32(&c[29 - j1]); // 28, 29, 30, 31, + const float32x4_t wkrt = vsubq_f32(mm_half, c_k1); // 28, 29, 30, 31, + const float32x4_t wkr_ = reverse_order_f32x4(wkrt); // 31, 30, 29, 28, + const float32x4_t wki_ = c_j1; // 1, 2, 3, 4, + // Load and shuffle 'a'. + // 2, 4, 6, 8, 3, 5, 7, 9 + float32x4x2_t a_j2_p = vld2q_f32(&a[0 + j2]); + // 120, 122, 124, 126, 121, 123, 125, 127, + const float32x4x2_t k2_0_4 = vld2q_f32(&a[122 - j2]); + // 126, 124, 122, 120 + const float32x4_t a_k2_p0 = reverse_order_f32x4(k2_0_4.val[0]); + // 127, 125, 123, 121 + const float32x4_t a_k2_p1 = reverse_order_f32x4(k2_0_4.val[1]); + // Calculate 'x'. + const float32x4_t xr_ = vsubq_f32(a_j2_p.val[0], a_k2_p0); + // 2-126, 4-124, 6-122, 8-120, + const float32x4_t xi_ = vaddq_f32(a_j2_p.val[1], a_k2_p1); + // 3-127, 5-125, 7-123, 9-121, + // Calculate product into 'y'. + // yr = wkr * xr - wki * xi; + // yi = wkr * xi + wki * xr; + const float32x4_t a_ = vmulq_f32(wkr_, xr_); + const float32x4_t b_ = vmulq_f32(wki_, xi_); + const float32x4_t c_ = vmulq_f32(wkr_, xi_); + const float32x4_t d_ = vmulq_f32(wki_, xr_); + const float32x4_t yr_ = vaddq_f32(a_, b_); // 2-126, 4-124, 6-122, 8-120, + const float32x4_t yi_ = vsubq_f32(c_, d_); // 3-127, 5-125, 7-123, 9-121, + // Update 'a'. + // a[j2 + 0] -= yr; + // a[j2 + 1] -= yi; + // a[k2 + 0] += yr; + // a[k2 + 1] -= yi; + // 126, 124, 122, 120, + const float32x4_t a_k2_p0n = vaddq_f32(a_k2_p0, yr_); + // 127, 125, 123, 121, + const float32x4_t a_k2_p1n = vsubq_f32(yi_, a_k2_p1); + // Shuffle in right order and store. + // 2, 3, 4, 5, 6, 7, 8, 9, + const float32x4_t a_k2_p0nr = vrev64q_f32(a_k2_p0n); + const float32x4_t a_k2_p1nr = vrev64q_f32(a_k2_p1n); + // 124, 125, 126, 127, 120, 121, 122, 123 + const float32x4x2_t a_k2_n = vzipq_f32(a_k2_p0nr, a_k2_p1nr); + // 2, 4, 6, 8, + a_j2_p.val[0] = vsubq_f32(a_j2_p.val[0], yr_); + // 3, 5, 7, 9, + a_j2_p.val[1] = vsubq_f32(yi_, a_j2_p.val[1]); + // 2, 3, 4, 5, 6, 7, 8, 9, + vst2q_f32(&a[0 + j2], a_j2_p); + + vst1q_f32(&a[122 - j2], a_k2_n.val[1]); + vst1q_f32(&a[126 - j2], a_k2_n.val[0]); + } + + // Scalar code for the remaining items. + for (; j2 < 64; j1 += 1, j2 += 2) { + const int k2 = 128 - j2; + const int k1 = 32 - j1; + const float wkr = 0.5f - c[k1]; + const float wki = c[j1]; + const float xr = a[j2 + 0] - a[k2 + 0]; + const float xi = a[j2 + 1] + a[k2 + 1]; + const float yr = wkr * xr + wki * xi; + const float yi = wkr * xi - wki * xr; + a[j2 + 0] = a[j2 + 0] - yr; + a[j2 + 1] = yi - a[j2 + 1]; + a[k2 + 0] = yr + a[k2 + 0]; + a[k2 + 1] = yi - a[k2 + 1]; + } + a[65] = -a[65]; +} +#endif + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_sse2_amd64.cc b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_sse2_amd64.cc new file mode 100644 index 00000000..7f0802dd --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_sse2_amd64.cc @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { + +#if defined(WEBRTC_ARCH_X86_FAMILY) + +namespace { +// These intrinsics were unavailable before VS 2008. +// TODO(andrew): move to a common file. +#if defined(_MSC_VER) && _MSC_VER < 1500 +static __inline __m128 _mm_castsi128_ps(__m128i a) { + return *(__m128*)&a; +} +static __inline __m128i _mm_castps_si128(__m128 a) { + return *(__m128i*)&a; +} +#endif + +} // namespace + +void cft1st_128_SSE2(float* a) { + const __m128 mm_swap_sign = _mm_load_ps(k_swap_sign); + int j, k2; + + for (k2 = 0, j = 0; j < 128; j += 16, k2 += 4) { + __m128 a00v = _mm_loadu_ps(&a[j + 0]); + __m128 a04v = _mm_loadu_ps(&a[j + 4]); + __m128 a08v = _mm_loadu_ps(&a[j + 8]); + __m128 a12v = _mm_loadu_ps(&a[j + 12]); + __m128 a01v = _mm_shuffle_ps(a00v, a08v, _MM_SHUFFLE(1, 0, 1, 0)); + __m128 a23v = _mm_shuffle_ps(a00v, a08v, _MM_SHUFFLE(3, 2, 3, 2)); + __m128 a45v = _mm_shuffle_ps(a04v, a12v, _MM_SHUFFLE(1, 0, 1, 0)); + __m128 a67v = _mm_shuffle_ps(a04v, a12v, _MM_SHUFFLE(3, 2, 3, 2)); + + const __m128 wk1rv = _mm_load_ps(&rdft_wk1r[k2]); + const __m128 wk1iv = _mm_load_ps(&rdft_wk1i[k2]); + const __m128 wk2rv = _mm_load_ps(&rdft_wk2r[k2]); + const __m128 wk2iv = _mm_load_ps(&rdft_wk2i[k2]); + const __m128 wk3rv = _mm_load_ps(&rdft_wk3r[k2]); + const __m128 wk3iv = _mm_load_ps(&rdft_wk3i[k2]); + __m128 x0v = _mm_add_ps(a01v, a23v); + const __m128 x1v = _mm_sub_ps(a01v, a23v); + const __m128 x2v = _mm_add_ps(a45v, a67v); + const __m128 x3v = _mm_sub_ps(a45v, a67v); + __m128 x0w; + a01v = _mm_add_ps(x0v, x2v); + x0v = _mm_sub_ps(x0v, x2v); + x0w = _mm_shuffle_ps(x0v, x0v, _MM_SHUFFLE(2, 3, 0, 1)); + { + const __m128 a45_0v = _mm_mul_ps(wk2rv, x0v); + const __m128 a45_1v = _mm_mul_ps(wk2iv, x0w); + a45v = _mm_add_ps(a45_0v, a45_1v); + } + { + __m128 a23_0v, a23_1v; + const __m128 x3w = _mm_shuffle_ps(x3v, x3v, _MM_SHUFFLE(2, 3, 0, 1)); + const __m128 x3s = _mm_mul_ps(mm_swap_sign, x3w); + x0v = _mm_add_ps(x1v, x3s); + x0w = _mm_shuffle_ps(x0v, x0v, _MM_SHUFFLE(2, 3, 0, 1)); + a23_0v = _mm_mul_ps(wk1rv, x0v); + a23_1v = _mm_mul_ps(wk1iv, x0w); + a23v = _mm_add_ps(a23_0v, a23_1v); + + x0v = _mm_sub_ps(x1v, x3s); + x0w = _mm_shuffle_ps(x0v, x0v, _MM_SHUFFLE(2, 3, 0, 1)); + } + { + const __m128 a67_0v = _mm_mul_ps(wk3rv, x0v); + const __m128 a67_1v = _mm_mul_ps(wk3iv, x0w); + a67v = _mm_add_ps(a67_0v, a67_1v); + } + + a00v = _mm_shuffle_ps(a01v, a23v, _MM_SHUFFLE(1, 0, 1, 0)); + a04v = _mm_shuffle_ps(a45v, a67v, _MM_SHUFFLE(1, 0, 1, 0)); + a08v = _mm_shuffle_ps(a01v, a23v, _MM_SHUFFLE(3, 2, 3, 2)); + a12v = _mm_shuffle_ps(a45v, a67v, _MM_SHUFFLE(3, 2, 3, 2)); + _mm_storeu_ps(&a[j + 0], a00v); + _mm_storeu_ps(&a[j + 4], a04v); + _mm_storeu_ps(&a[j + 8], a08v); + _mm_storeu_ps(&a[j + 12], a12v); + } +} + +void cftmdl_128_SSE2(float* a) { + const int l = 8; + const __m128 mm_swap_sign = _mm_load_ps(k_swap_sign); + int j0; + + __m128 wk1rv = _mm_load_ps(cftmdl_wk1r); + for (j0 = 0; j0 < l; j0 += 2) { + const __m128i a_00 = _mm_loadl_epi64((__m128i*)&a[j0 + 0]); + const __m128i a_08 = _mm_loadl_epi64((__m128i*)&a[j0 + 8]); + const __m128i a_32 = _mm_loadl_epi64((__m128i*)&a[j0 + 32]); + const __m128i a_40 = _mm_loadl_epi64((__m128i*)&a[j0 + 40]); + const __m128 a_00_32 = + _mm_shuffle_ps(_mm_castsi128_ps(a_00), _mm_castsi128_ps(a_32), + _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_08_40 = + _mm_shuffle_ps(_mm_castsi128_ps(a_08), _mm_castsi128_ps(a_40), + _MM_SHUFFLE(1, 0, 1, 0)); + __m128 x0r0_0i0_0r1_x0i1 = _mm_add_ps(a_00_32, a_08_40); + const __m128 x1r0_1i0_1r1_x1i1 = _mm_sub_ps(a_00_32, a_08_40); + + const __m128i a_16 = _mm_loadl_epi64((__m128i*)&a[j0 + 16]); + const __m128i a_24 = _mm_loadl_epi64((__m128i*)&a[j0 + 24]); + const __m128i a_48 = _mm_loadl_epi64((__m128i*)&a[j0 + 48]); + const __m128i a_56 = _mm_loadl_epi64((__m128i*)&a[j0 + 56]); + const __m128 a_16_48 = + _mm_shuffle_ps(_mm_castsi128_ps(a_16), _mm_castsi128_ps(a_48), + _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_24_56 = + _mm_shuffle_ps(_mm_castsi128_ps(a_24), _mm_castsi128_ps(a_56), + _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 x2r0_2i0_2r1_x2i1 = _mm_add_ps(a_16_48, a_24_56); + const __m128 x3r0_3i0_3r1_x3i1 = _mm_sub_ps(a_16_48, a_24_56); + + const __m128 xx0 = _mm_add_ps(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + const __m128 xx1 = _mm_sub_ps(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + + const __m128 x3i0_3r0_3i1_x3r1 = _mm_castsi128_ps(_mm_shuffle_epi32( + _mm_castps_si128(x3r0_3i0_3r1_x3i1), _MM_SHUFFLE(2, 3, 0, 1))); + const __m128 x3_swapped = _mm_mul_ps(mm_swap_sign, x3i0_3r0_3i1_x3r1); + const __m128 x1_x3_add = _mm_add_ps(x1r0_1i0_1r1_x1i1, x3_swapped); + const __m128 x1_x3_sub = _mm_sub_ps(x1r0_1i0_1r1_x1i1, x3_swapped); + + const __m128 yy0 = + _mm_shuffle_ps(x1_x3_add, x1_x3_sub, _MM_SHUFFLE(2, 2, 2, 2)); + const __m128 yy1 = + _mm_shuffle_ps(x1_x3_add, x1_x3_sub, _MM_SHUFFLE(3, 3, 3, 3)); + const __m128 yy2 = _mm_mul_ps(mm_swap_sign, yy1); + const __m128 yy3 = _mm_add_ps(yy0, yy2); + const __m128 yy4 = _mm_mul_ps(wk1rv, yy3); + + _mm_storel_epi64((__m128i*)&a[j0 + 0], _mm_castps_si128(xx0)); + _mm_storel_epi64( + (__m128i*)&a[j0 + 32], + _mm_shuffle_epi32(_mm_castps_si128(xx0), _MM_SHUFFLE(3, 2, 3, 2))); + + _mm_storel_epi64((__m128i*)&a[j0 + 16], _mm_castps_si128(xx1)); + _mm_storel_epi64( + (__m128i*)&a[j0 + 48], + _mm_shuffle_epi32(_mm_castps_si128(xx1), _MM_SHUFFLE(2, 3, 2, 3))); + a[j0 + 48] = -a[j0 + 48]; + + _mm_storel_epi64((__m128i*)&a[j0 + 8], _mm_castps_si128(x1_x3_add)); + _mm_storel_epi64((__m128i*)&a[j0 + 24], _mm_castps_si128(x1_x3_sub)); + + _mm_storel_epi64((__m128i*)&a[j0 + 40], _mm_castps_si128(yy4)); + _mm_storel_epi64( + (__m128i*)&a[j0 + 56], + _mm_shuffle_epi32(_mm_castps_si128(yy4), _MM_SHUFFLE(2, 3, 2, 3))); + } + + { + int k = 64; + int k1 = 2; + int k2 = 2 * k1; + const __m128 wk2rv = _mm_load_ps(&rdft_wk2r[k2 + 0]); + const __m128 wk2iv = _mm_load_ps(&rdft_wk2i[k2 + 0]); + const __m128 wk1iv = _mm_load_ps(&rdft_wk1i[k2 + 0]); + const __m128 wk3rv = _mm_load_ps(&rdft_wk3r[k2 + 0]); + const __m128 wk3iv = _mm_load_ps(&rdft_wk3i[k2 + 0]); + wk1rv = _mm_load_ps(&rdft_wk1r[k2 + 0]); + for (j0 = k; j0 < l + k; j0 += 2) { + const __m128i a_00 = _mm_loadl_epi64((__m128i*)&a[j0 + 0]); + const __m128i a_08 = _mm_loadl_epi64((__m128i*)&a[j0 + 8]); + const __m128i a_32 = _mm_loadl_epi64((__m128i*)&a[j0 + 32]); + const __m128i a_40 = _mm_loadl_epi64((__m128i*)&a[j0 + 40]); + const __m128 a_00_32 = + _mm_shuffle_ps(_mm_castsi128_ps(a_00), _mm_castsi128_ps(a_32), + _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_08_40 = + _mm_shuffle_ps(_mm_castsi128_ps(a_08), _mm_castsi128_ps(a_40), + _MM_SHUFFLE(1, 0, 1, 0)); + __m128 x0r0_0i0_0r1_x0i1 = _mm_add_ps(a_00_32, a_08_40); + const __m128 x1r0_1i0_1r1_x1i1 = _mm_sub_ps(a_00_32, a_08_40); + + const __m128i a_16 = _mm_loadl_epi64((__m128i*)&a[j0 + 16]); + const __m128i a_24 = _mm_loadl_epi64((__m128i*)&a[j0 + 24]); + const __m128i a_48 = _mm_loadl_epi64((__m128i*)&a[j0 + 48]); + const __m128i a_56 = _mm_loadl_epi64((__m128i*)&a[j0 + 56]); + const __m128 a_16_48 = + _mm_shuffle_ps(_mm_castsi128_ps(a_16), _mm_castsi128_ps(a_48), + _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_24_56 = + _mm_shuffle_ps(_mm_castsi128_ps(a_24), _mm_castsi128_ps(a_56), + _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 x2r0_2i0_2r1_x2i1 = _mm_add_ps(a_16_48, a_24_56); + const __m128 x3r0_3i0_3r1_x3i1 = _mm_sub_ps(a_16_48, a_24_56); + + const __m128 xx = _mm_add_ps(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + const __m128 xx1 = _mm_sub_ps(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); + const __m128 xx2 = _mm_mul_ps(xx1, wk2rv); + const __m128 xx3 = _mm_mul_ps( + wk2iv, _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(xx1), + _MM_SHUFFLE(2, 3, 0, 1)))); + const __m128 xx4 = _mm_add_ps(xx2, xx3); + + const __m128 x3i0_3r0_3i1_x3r1 = _mm_castsi128_ps(_mm_shuffle_epi32( + _mm_castps_si128(x3r0_3i0_3r1_x3i1), _MM_SHUFFLE(2, 3, 0, 1))); + const __m128 x3_swapped = _mm_mul_ps(mm_swap_sign, x3i0_3r0_3i1_x3r1); + const __m128 x1_x3_add = _mm_add_ps(x1r0_1i0_1r1_x1i1, x3_swapped); + const __m128 x1_x3_sub = _mm_sub_ps(x1r0_1i0_1r1_x1i1, x3_swapped); + + const __m128 xx10 = _mm_mul_ps(x1_x3_add, wk1rv); + const __m128 xx11 = _mm_mul_ps( + wk1iv, _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(x1_x3_add), + _MM_SHUFFLE(2, 3, 0, 1)))); + const __m128 xx12 = _mm_add_ps(xx10, xx11); + + const __m128 xx20 = _mm_mul_ps(x1_x3_sub, wk3rv); + const __m128 xx21 = _mm_mul_ps( + wk3iv, _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(x1_x3_sub), + _MM_SHUFFLE(2, 3, 0, 1)))); + const __m128 xx22 = _mm_add_ps(xx20, xx21); + + _mm_storel_epi64((__m128i*)&a[j0 + 0], _mm_castps_si128(xx)); + _mm_storel_epi64( + (__m128i*)&a[j0 + 32], + _mm_shuffle_epi32(_mm_castps_si128(xx), _MM_SHUFFLE(3, 2, 3, 2))); + + _mm_storel_epi64((__m128i*)&a[j0 + 16], _mm_castps_si128(xx4)); + _mm_storel_epi64( + (__m128i*)&a[j0 + 48], + _mm_shuffle_epi32(_mm_castps_si128(xx4), _MM_SHUFFLE(3, 2, 3, 2))); + + _mm_storel_epi64((__m128i*)&a[j0 + 8], _mm_castps_si128(xx12)); + _mm_storel_epi64( + (__m128i*)&a[j0 + 40], + _mm_shuffle_epi32(_mm_castps_si128(xx12), _MM_SHUFFLE(3, 2, 3, 2))); + + _mm_storel_epi64((__m128i*)&a[j0 + 24], _mm_castps_si128(xx22)); + _mm_storel_epi64( + (__m128i*)&a[j0 + 56], + _mm_shuffle_epi32(_mm_castps_si128(xx22), _MM_SHUFFLE(3, 2, 3, 2))); + } + } +} + +void rftfsub_128_SSE2(float* a) { + const float* c = rdft_w + 32; + int j1, j2, k1, k2; + float wkr, wki, xr, xi, yr, yi; + + static const ALIGN16_BEG float ALIGN16_END k_half[4] = {0.5f, 0.5f, 0.5f, + 0.5f}; + const __m128 mm_half = _mm_load_ps(k_half); + + // Vectorized code (four at once). + // Note: commented number are indexes for the first iteration of the loop. + for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) { + // Load 'wk'. + const __m128 c_j1 = _mm_loadu_ps(&c[j1]); // 1, 2, 3, 4, + const __m128 c_k1 = _mm_loadu_ps(&c[29 - j1]); // 28, 29, 30, 31, + const __m128 wkrt = _mm_sub_ps(mm_half, c_k1); // 28, 29, 30, 31, + const __m128 wkr_ = + _mm_shuffle_ps(wkrt, wkrt, _MM_SHUFFLE(0, 1, 2, 3)); // 31, 30, 29, 28, + const __m128 wki_ = c_j1; // 1, 2, 3, 4, + // Load and shuffle 'a'. + const __m128 a_j2_0 = _mm_loadu_ps(&a[0 + j2]); // 2, 3, 4, 5, + const __m128 a_j2_4 = _mm_loadu_ps(&a[4 + j2]); // 6, 7, 8, 9, + const __m128 a_k2_0 = _mm_loadu_ps(&a[122 - j2]); // 120, 121, 122, 123, + const __m128 a_k2_4 = _mm_loadu_ps(&a[126 - j2]); // 124, 125, 126, 127, + const __m128 a_j2_p0 = _mm_shuffle_ps( + a_j2_0, a_j2_4, _MM_SHUFFLE(2, 0, 2, 0)); // 2, 4, 6, 8, + const __m128 a_j2_p1 = _mm_shuffle_ps( + a_j2_0, a_j2_4, _MM_SHUFFLE(3, 1, 3, 1)); // 3, 5, 7, 9, + const __m128 a_k2_p0 = _mm_shuffle_ps( + a_k2_4, a_k2_0, _MM_SHUFFLE(0, 2, 0, 2)); // 126, 124, 122, 120, + const __m128 a_k2_p1 = _mm_shuffle_ps( + a_k2_4, a_k2_0, _MM_SHUFFLE(1, 3, 1, 3)); // 127, 125, 123, 121, + // Calculate 'x'. + const __m128 xr_ = _mm_sub_ps(a_j2_p0, a_k2_p0); + // 2-126, 4-124, 6-122, 8-120, + const __m128 xi_ = _mm_add_ps(a_j2_p1, a_k2_p1); + // 3-127, 5-125, 7-123, 9-121, + // Calculate product into 'y'. + // yr = wkr * xr - wki * xi; + // yi = wkr * xi + wki * xr; + const __m128 a_ = _mm_mul_ps(wkr_, xr_); + const __m128 b_ = _mm_mul_ps(wki_, xi_); + const __m128 c_ = _mm_mul_ps(wkr_, xi_); + const __m128 d_ = _mm_mul_ps(wki_, xr_); + const __m128 yr_ = _mm_sub_ps(a_, b_); // 2-126, 4-124, 6-122, 8-120, + const __m128 yi_ = _mm_add_ps(c_, d_); // 3-127, 5-125, 7-123, 9-121, + // Update 'a'. + // a[j2 + 0] -= yr; + // a[j2 + 1] -= yi; + // a[k2 + 0] += yr; + // a[k2 + 1] -= yi; + const __m128 a_j2_p0n = _mm_sub_ps(a_j2_p0, yr_); // 2, 4, 6, 8, + const __m128 a_j2_p1n = _mm_sub_ps(a_j2_p1, yi_); // 3, 5, 7, 9, + const __m128 a_k2_p0n = _mm_add_ps(a_k2_p0, yr_); // 126, 124, 122, 120, + const __m128 a_k2_p1n = _mm_sub_ps(a_k2_p1, yi_); // 127, 125, 123, 121, + // Shuffle in right order and store. + const __m128 a_j2_0n = _mm_unpacklo_ps(a_j2_p0n, a_j2_p1n); + // 2, 3, 4, 5, + const __m128 a_j2_4n = _mm_unpackhi_ps(a_j2_p0n, a_j2_p1n); + // 6, 7, 8, 9, + const __m128 a_k2_0nt = _mm_unpackhi_ps(a_k2_p0n, a_k2_p1n); + // 122, 123, 120, 121, + const __m128 a_k2_4nt = _mm_unpacklo_ps(a_k2_p0n, a_k2_p1n); + // 126, 127, 124, 125, + const __m128 a_k2_0n = _mm_shuffle_ps( + a_k2_0nt, a_k2_0nt, _MM_SHUFFLE(1, 0, 3, 2)); // 120, 121, 122, 123, + const __m128 a_k2_4n = _mm_shuffle_ps( + a_k2_4nt, a_k2_4nt, _MM_SHUFFLE(1, 0, 3, 2)); // 124, 125, 126, 127, + _mm_storeu_ps(&a[0 + j2], a_j2_0n); + _mm_storeu_ps(&a[4 + j2], a_j2_4n); + _mm_storeu_ps(&a[122 - j2], a_k2_0n); + _mm_storeu_ps(&a[126 - j2], a_k2_4n); + } + // Scalar code for the remaining items. + for (; j2 < 64; j1 += 1, j2 += 2) { + k2 = 128 - j2; + k1 = 32 - j1; + wkr = 0.5f - c[k1]; + wki = c[j1]; + xr = a[j2 + 0] - a[k2 + 0]; + xi = a[j2 + 1] + a[k2 + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j2 + 0] -= yr; + a[j2 + 1] -= yi; + a[k2 + 0] += yr; + a[k2 + 1] -= yi; + } +} + +void rftbsub_128_SSE2(float* a) { + const float* c = rdft_w + 32; + int j1, j2, k1, k2; + float wkr, wki, xr, xi, yr, yi; + + static const ALIGN16_BEG float ALIGN16_END k_half[4] = {0.5f, 0.5f, 0.5f, + 0.5f}; + const __m128 mm_half = _mm_load_ps(k_half); + + a[1] = -a[1]; + // Vectorized code (four at once). + // Note: commented number are indexes for the first iteration of the loop. + for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) { + // Load 'wk'. + const __m128 c_j1 = _mm_loadu_ps(&c[j1]); // 1, 2, 3, 4, + const __m128 c_k1 = _mm_loadu_ps(&c[29 - j1]); // 28, 29, 30, 31, + const __m128 wkrt = _mm_sub_ps(mm_half, c_k1); // 28, 29, 30, 31, + const __m128 wkr_ = + _mm_shuffle_ps(wkrt, wkrt, _MM_SHUFFLE(0, 1, 2, 3)); // 31, 30, 29, 28, + const __m128 wki_ = c_j1; // 1, 2, 3, 4, + // Load and shuffle 'a'. + const __m128 a_j2_0 = _mm_loadu_ps(&a[0 + j2]); // 2, 3, 4, 5, + const __m128 a_j2_4 = _mm_loadu_ps(&a[4 + j2]); // 6, 7, 8, 9, + const __m128 a_k2_0 = _mm_loadu_ps(&a[122 - j2]); // 120, 121, 122, 123, + const __m128 a_k2_4 = _mm_loadu_ps(&a[126 - j2]); // 124, 125, 126, 127, + const __m128 a_j2_p0 = _mm_shuffle_ps( + a_j2_0, a_j2_4, _MM_SHUFFLE(2, 0, 2, 0)); // 2, 4, 6, 8, + const __m128 a_j2_p1 = _mm_shuffle_ps( + a_j2_0, a_j2_4, _MM_SHUFFLE(3, 1, 3, 1)); // 3, 5, 7, 9, + const __m128 a_k2_p0 = _mm_shuffle_ps( + a_k2_4, a_k2_0, _MM_SHUFFLE(0, 2, 0, 2)); // 126, 124, 122, 120, + const __m128 a_k2_p1 = _mm_shuffle_ps( + a_k2_4, a_k2_0, _MM_SHUFFLE(1, 3, 1, 3)); // 127, 125, 123, 121, + // Calculate 'x'. + const __m128 xr_ = _mm_sub_ps(a_j2_p0, a_k2_p0); + // 2-126, 4-124, 6-122, 8-120, + const __m128 xi_ = _mm_add_ps(a_j2_p1, a_k2_p1); + // 3-127, 5-125, 7-123, 9-121, + // Calculate product into 'y'. + // yr = wkr * xr + wki * xi; + // yi = wkr * xi - wki * xr; + const __m128 a_ = _mm_mul_ps(wkr_, xr_); + const __m128 b_ = _mm_mul_ps(wki_, xi_); + const __m128 c_ = _mm_mul_ps(wkr_, xi_); + const __m128 d_ = _mm_mul_ps(wki_, xr_); + const __m128 yr_ = _mm_add_ps(a_, b_); // 2-126, 4-124, 6-122, 8-120, + const __m128 yi_ = _mm_sub_ps(c_, d_); // 3-127, 5-125, 7-123, 9-121, + // Update 'a'. + // a[j2 + 0] = a[j2 + 0] - yr; + // a[j2 + 1] = yi - a[j2 + 1]; + // a[k2 + 0] = yr + a[k2 + 0]; + // a[k2 + 1] = yi - a[k2 + 1]; + const __m128 a_j2_p0n = _mm_sub_ps(a_j2_p0, yr_); // 2, 4, 6, 8, + const __m128 a_j2_p1n = _mm_sub_ps(yi_, a_j2_p1); // 3, 5, 7, 9, + const __m128 a_k2_p0n = _mm_add_ps(a_k2_p0, yr_); // 126, 124, 122, 120, + const __m128 a_k2_p1n = _mm_sub_ps(yi_, a_k2_p1); // 127, 125, 123, 121, + // Shuffle in right order and store. + const __m128 a_j2_0n = _mm_unpacklo_ps(a_j2_p0n, a_j2_p1n); + // 2, 3, 4, 5, + const __m128 a_j2_4n = _mm_unpackhi_ps(a_j2_p0n, a_j2_p1n); + // 6, 7, 8, 9, + const __m128 a_k2_0nt = _mm_unpackhi_ps(a_k2_p0n, a_k2_p1n); + // 122, 123, 120, 121, + const __m128 a_k2_4nt = _mm_unpacklo_ps(a_k2_p0n, a_k2_p1n); + // 126, 127, 124, 125, + const __m128 a_k2_0n = _mm_shuffle_ps( + a_k2_0nt, a_k2_0nt, _MM_SHUFFLE(1, 0, 3, 2)); // 120, 121, 122, 123, + const __m128 a_k2_4n = _mm_shuffle_ps( + a_k2_4nt, a_k2_4nt, _MM_SHUFFLE(1, 0, 3, 2)); // 124, 125, 126, 127, + _mm_storeu_ps(&a[0 + j2], a_j2_0n); + _mm_storeu_ps(&a[4 + j2], a_j2_4n); + _mm_storeu_ps(&a[122 - j2], a_k2_0n); + _mm_storeu_ps(&a[126 - j2], a_k2_4n); + } + // Scalar code for the remaining items. + for (; j2 < 64; j1 += 1, j2 += 2) { + k2 = 128 - j2; + k1 = 32 - j1; + wkr = 0.5f - c[k1]; + wki = c[j1]; + xr = a[j2 + 0] - a[k2 + 0]; + xi = a[j2 + 1] + a[k2 + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j2 + 0] = a[j2 + 0] - yr; + a[j2 + 1] = yi - a[j2 + 1]; + a[k2 + 0] = yr + a[k2 + 0]; + a[k2 + 1] = yi - a[k2 + 1]; + } + a[65] = -a[65]; +} +#endif + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h new file mode 100644 index 00000000..6db1dd9a --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_COMMON_H_ + +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" + +namespace webrtc { + +// This tables used to be computed at run-time. For example, refer to: +// https://code.google.com/p/webrtc/source/browse/trunk/webrtc/modules/audio_processing/utility/apm_rdft.c?r=6564 +// to see the initialization code. +// Constants shared by all paths (C, SSE2, NEON). +const float rdft_w[64] = { + 1.0000000000f, 0.0000000000f, 0.7071067691f, 0.7071067691f, 0.9238795638f, + 0.3826834559f, 0.3826834559f, 0.9238795638f, 0.9807852507f, 0.1950903237f, + 0.5555702448f, 0.8314695954f, 0.8314695954f, 0.5555702448f, 0.1950903237f, + 0.9807852507f, 0.9951847196f, 0.0980171412f, 0.6343933344f, 0.7730104327f, + 0.8819212914f, 0.4713967443f, 0.2902846634f, 0.9569403529f, 0.9569403529f, + 0.2902846634f, 0.4713967443f, 0.8819212914f, 0.7730104327f, 0.6343933344f, + 0.0980171412f, 0.9951847196f, 0.7071067691f, 0.4993977249f, 0.4975923598f, + 0.4945882559f, 0.4903926253f, 0.4850156307f, 0.4784701765f, 0.4707720280f, + 0.4619397819f, 0.4519946277f, 0.4409606457f, 0.4288643003f, 0.4157347977f, + 0.4016037583f, 0.3865052164f, 0.3704755902f, 0.3535533845f, 0.3357794881f, + 0.3171966672f, 0.2978496552f, 0.2777851224f, 0.2570513785f, 0.2356983721f, + 0.2137775421f, 0.1913417280f, 0.1684449315f, 0.1451423317f, 0.1214900985f, + 0.0975451618f, 0.0733652338f, 0.0490085706f, 0.0245338380f, +}; + +// Constants used by the C and MIPS paths. +const float rdft_wk3ri_first[16] = { + 1.000000000f, 0.000000000f, 0.382683456f, 0.923879564f, + 0.831469536f, 0.555570245f, -0.195090353f, 0.980785251f, + 0.956940353f, 0.290284693f, 0.098017156f, 0.995184720f, + 0.634393334f, 0.773010492f, -0.471396863f, 0.881921172f, +}; +const float rdft_wk3ri_second[16] = { + -0.707106769f, 0.707106769f, -0.923879564f, -0.382683456f, + -0.980785251f, 0.195090353f, -0.555570245f, -0.831469536f, + -0.881921172f, 0.471396863f, -0.773010492f, -0.634393334f, + -0.995184720f, -0.098017156f, -0.290284693f, -0.956940353f, +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_COMMON_H_ diff --git a/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h new file mode 100644 index 00000000..a63d1870 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_NEON_SSE2_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_NEON_SSE2_H_ + +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" +#include "rtc_base/system/arch.h" + +#ifdef _MSC_VER /* visual c++ */ +#define ALIGN16_BEG __declspec(align(16)) +#define ALIGN16_END +#else /* gcc or icc */ +#define ALIGN16_BEG +#define ALIGN16_END __attribute__((aligned(16))) +#endif + +namespace webrtc { + +// These tables used to be computed at run-time. For example, refer to: +// https://code.google.com/p/webrtc/source/browse/trunk/webrtc/modules/audio_processing/utility/apm_rdft.c?r=6564 +// to see the initialization code. +#if defined(WEBRTC_ARCH_X86_FAMILY) || defined(WEBRTC_HAS_NEON) +// Constants used by SSE2 and NEON but initialized in the C path. +const ALIGN16_BEG float ALIGN16_END k_swap_sign[4] = {-1.f, 1.f, -1.f, 1.f}; + +ALIGN16_BEG const float ALIGN16_END rdft_wk1r[32] = { + 1.000000000f, 1.000000000f, 0.707106769f, 0.707106769f, 0.923879564f, + 0.923879564f, 0.382683456f, 0.382683456f, 0.980785251f, 0.980785251f, + 0.555570245f, 0.555570245f, 0.831469595f, 0.831469595f, 0.195090324f, + 0.195090324f, 0.995184720f, 0.995184720f, 0.634393334f, 0.634393334f, + 0.881921291f, 0.881921291f, 0.290284663f, 0.290284663f, 0.956940353f, + 0.956940353f, 0.471396744f, 0.471396744f, 0.773010433f, 0.773010433f, + 0.098017141f, 0.098017141f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk2r[32] = { + 1.000000000f, 1.000000000f, -0.000000000f, -0.000000000f, 0.707106769f, + 0.707106769f, -0.707106769f, -0.707106769f, 0.923879564f, 0.923879564f, + -0.382683456f, -0.382683456f, 0.382683456f, 0.382683456f, -0.923879564f, + -0.923879564f, 0.980785251f, 0.980785251f, -0.195090324f, -0.195090324f, + 0.555570245f, 0.555570245f, -0.831469595f, -0.831469595f, 0.831469595f, + 0.831469595f, -0.555570245f, -0.555570245f, 0.195090324f, 0.195090324f, + -0.980785251f, -0.980785251f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk3r[32] = { + 1.000000000f, 1.000000000f, -0.707106769f, -0.707106769f, 0.382683456f, + 0.382683456f, -0.923879564f, -0.923879564f, 0.831469536f, 0.831469536f, + -0.980785251f, -0.980785251f, -0.195090353f, -0.195090353f, -0.555570245f, + -0.555570245f, 0.956940353f, 0.956940353f, -0.881921172f, -0.881921172f, + 0.098017156f, 0.098017156f, -0.773010492f, -0.773010492f, 0.634393334f, + 0.634393334f, -0.995184720f, -0.995184720f, -0.471396863f, -0.471396863f, + -0.290284693f, -0.290284693f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk1i[32] = { + -0.000000000f, 0.000000000f, -0.707106769f, 0.707106769f, -0.382683456f, + 0.382683456f, -0.923879564f, 0.923879564f, -0.195090324f, 0.195090324f, + -0.831469595f, 0.831469595f, -0.555570245f, 0.555570245f, -0.980785251f, + 0.980785251f, -0.098017141f, 0.098017141f, -0.773010433f, 0.773010433f, + -0.471396744f, 0.471396744f, -0.956940353f, 0.956940353f, -0.290284663f, + 0.290284663f, -0.881921291f, 0.881921291f, -0.634393334f, 0.634393334f, + -0.995184720f, 0.995184720f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk2i[32] = { + -0.000000000f, 0.000000000f, -1.000000000f, 1.000000000f, -0.707106769f, + 0.707106769f, -0.707106769f, 0.707106769f, -0.382683456f, 0.382683456f, + -0.923879564f, 0.923879564f, -0.923879564f, 0.923879564f, -0.382683456f, + 0.382683456f, -0.195090324f, 0.195090324f, -0.980785251f, 0.980785251f, + -0.831469595f, 0.831469595f, -0.555570245f, 0.555570245f, -0.555570245f, + 0.555570245f, -0.831469595f, 0.831469595f, -0.980785251f, 0.980785251f, + -0.195090324f, 0.195090324f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk3i[32] = { + -0.000000000f, 0.000000000f, -0.707106769f, 0.707106769f, -0.923879564f, + 0.923879564f, 0.382683456f, -0.382683456f, -0.555570245f, 0.555570245f, + -0.195090353f, 0.195090353f, -0.980785251f, 0.980785251f, 0.831469536f, + -0.831469536f, -0.290284693f, 0.290284693f, -0.471396863f, 0.471396863f, + -0.995184720f, 0.995184720f, 0.634393334f, -0.634393334f, -0.773010492f, + 0.773010492f, 0.098017156f, -0.098017156f, -0.881921172f, 0.881921172f, + 0.956940353f, -0.956940353f, +}; +ALIGN16_BEG const float ALIGN16_END cftmdl_wk1r[4] = { + 0.707106769f, + 0.707106769f, + 0.707106769f, + -0.707106769f, +}; +#endif + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_NEON_SSE2_H_ diff --git a/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.cc b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.cc new file mode 100644 index 00000000..2573f23d --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.cc @@ -0,0 +1,866 @@ +/* + * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html + * Copyright Takuya OOURA, 1996-2001 + * + * You may use, copy, modify and distribute this code for any purpose (include + * commercial use) and without fee. Please refer to this package when you modify + * this code. + * + * Changes: + * Trivial type modifications by the WebRTC authors. + */ + +/* +Fast Fourier/Cosine/Sine Transform + dimension :one + data length :power of 2 + decimation :frequency + radix :4, 2 + data :inplace + table :use +functions + cdft: Complex Discrete Fourier Transform + rdft: Real Discrete Fourier Transform + ddct: Discrete Cosine Transform + ddst: Discrete Sine Transform + dfct: Cosine Transform of RDFT (Real Symmetric DFT) + dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) +function prototypes + void cdft(int, int, float *, int *, float *); + void rdft(size_t, int, float *, size_t *, float *); + void ddct(int, int, float *, int *, float *); + void ddst(int, int, float *, int *, float *); + void dfct(int, float *, float *, int *, float *); + void dfst(int, float *, float *, int *, float *); + + +-------- Complex DFT (Discrete Fourier Transform) -------- + [definition] + + X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k + X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k + ip[0] = 0; // first time only + cdft(2*n, 1, a, ip, w); + + ip[0] = 0; // first time only + cdft(2*n, -1, a, ip, w); + [parameters] + 2*n :data length (int) + n >= 1, n = power of 2 + a[0...2*n-1] :input/output data (float *) + input data + a[2*j] = Re(x[j]), + a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) + strictly, + length of ip >= + 2+(1<<(int)(log(n+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + cdft(2*n, -1, a, ip, w); + is + cdft(2*n, 1, a, ip, w); + for (j = 0; j <= 2 * n - 1; j++) { + a[j] *= 1.0 / n; + } + . + + +-------- Real DFT / Inverse of Real DFT -------- + [definition] + RDFT + R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 + I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0 IRDFT (excluding scale) + a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k + ip[0] = 0; // first time only + rdft(n, 1, a, ip, w); + + ip[0] = 0; // first time only + rdft(n, -1, a, ip, w); + [parameters] + n :data length (size_t) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + + output data + a[2*k] = R[k], 0<=k + input data + a[2*j] = R[j], 0<=j= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + rdft(n, 1, a, ip, w); + is + rdft(n, -1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- + [definition] + IDCT (excluding scale) + C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k DCT + C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k + ip[0] = 0; // first time only + ddct(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddct(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + output data + a[k] = C[k], 0<=k= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddct(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddct(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DST (Discrete Sine Transform) / Inverse of DST -------- + [definition] + IDST (excluding scale) + S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k DST + S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0 + ip[0] = 0; // first time only + ddst(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddst(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + + input data + a[j] = A[j], 0 + output data + a[k] = S[k], 0= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddst(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddst(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Cosine Transform of RDFT (Real Symmetric DFT) -------- + [definition] + C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n + [usage] + ip[0] = 0; // first time only + dfct(n, a, t, ip, w); + [parameters] + n :data length - 1 (int) + n >= 2, n = power of 2 + a[0...n] :input/output data (float *) + output data + a[k] = C[k], 0<=k<=n + t[0...n/2] :work area (float *) + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + is + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + for (j = 0; j <= n; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- + [definition] + S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + output data + a[k] = S[k], 0= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + dfst(n, a, t, ip, w); + is + dfst(n, a, t, ip, w); + for (j = 1; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +Appendix : + The cos/sin table is recalculated when the larger table required. + w[] and ip[] are compatible with all routines. +*/ + +#include "common_audio/third_party/ooura/fft_size_256/fft4g.h" + +#include +#include + +namespace webrtc { + +namespace { + +void makewt(size_t nw, size_t* ip, float* w); +void makect(size_t nc, size_t* ip, float* c); +void bitrv2(size_t n, size_t* ip, float* a); +void cftfsub(size_t n, float* a, float* w); +void cftbsub(size_t n, float* a, float* w); +void cft1st(size_t n, float* a, float* w); +void cftmdl(size_t n, size_t l, float* a, float* w); +void rftfsub(size_t n, float* a, size_t nc, float* c); +void rftbsub(size_t n, float* a, size_t nc, float* c); + +/* -------- initializing routines -------- */ + +void makewt(size_t nw, size_t* ip, float* w) { + size_t j, nwh; + float delta, x, y; + + ip[0] = nw; + ip[1] = 1; + if (nw > 2) { + nwh = nw >> 1; + delta = atanf(1.0f) / nwh; + w[0] = 1; + w[1] = 0; + w[nwh] = (float)cos(delta * nwh); + w[nwh + 1] = w[nwh]; + if (nwh > 2) { + for (j = 2; j < nwh; j += 2) { + x = (float)cos(delta * j); + y = (float)sin(delta * j); + w[j] = x; + w[j + 1] = y; + w[nw - j] = y; + w[nw - j + 1] = x; + } + bitrv2(nw, ip + 2, w); + } + } +} + +void makect(size_t nc, size_t* ip, float* c) { + size_t j, nch; + float delta; + + ip[1] = nc; + if (nc > 1) { + nch = nc >> 1; + delta = atanf(1.0f) / nch; + c[0] = (float)cos(delta * nch); + c[nch] = 0.5f * c[0]; + for (j = 1; j < nch; j++) { + c[j] = 0.5f * (float)cos(delta * j); + c[nc - j] = 0.5f * (float)sin(delta * j); + } + } +} + +/* -------- child routines -------- */ + +void bitrv2(size_t n, size_t* ip, float* a) { + size_t j, j1, k, k1, l, m, m2; + float xr, xi, yr, yi; + + ip[0] = 0; + l = n; + m = 1; + while ((m << 3) < l) { + l >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + l; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == l) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + j1 = 2 * k + m2 + ip[k]; + k1 = j1 + m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } else { + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } + } +} + +void cftfsub(size_t n, float* a, float* w) { + size_t j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + } else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = a[j + 1] - a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] += a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + +void cftbsub(size_t n, float* a, float* w) { + size_t j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = -a[j + 1] - a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = -a[j + 1] + a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i + x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i - x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i + x3r; + } + } else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = -a[j + 1] + a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] = -a[j + 1] - a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + +void cft1st(size_t n, float* a, float* w) { + size_t j, k1, k2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[2]; + x0i = a[1] + a[3]; + x1r = a[0] - a[2]; + x1i = a[1] - a[3]; + x2r = a[4] + a[6]; + x2i = a[5] + a[7]; + x3r = a[4] - a[6]; + x3i = a[5] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[2] = x1r - x3i; + a[3] = x1i + x3r; + a[6] = x1r + x3i; + a[7] = x1i - x3r; + wk1r = w[2]; + x0r = a[8] + a[10]; + x0i = a[9] + a[11]; + x1r = a[8] - a[10]; + x1i = a[9] - a[11]; + x2r = a[12] + a[14]; + x2i = a[13] + a[15]; + x3r = a[12] - a[14]; + x3i = a[13] - a[15]; + a[8] = x0r + x2r; + a[9] = x0i + x2i; + a[12] = x2i - x0i; + a[13] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[10] = wk1r * (x0r - x0i); + a[11] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[14] = wk1r * (x0i - x0r); + a[15] = wk1r * (x0i + x0r); + k1 = 0; + for (j = 16; j < n; j += 16) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + x0r = a[j] + a[j + 2]; + x0i = a[j + 1] + a[j + 3]; + x1r = a[j] - a[j + 2]; + x1i = a[j + 1] - a[j + 3]; + x2r = a[j + 4] + a[j + 6]; + x2i = a[j + 5] + a[j + 7]; + x3r = a[j + 4] - a[j + 6]; + x3i = a[j + 5] - a[j + 7]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 4] = wk2r * x0r - wk2i * x0i; + a[j + 5] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 2] = wk1r * x0r - wk1i * x0i; + a[j + 3] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 6] = wk3r * x0r - wk3i * x0i; + a[j + 7] = wk3r * x0i + wk3i * x0r; + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + x0r = a[j + 8] + a[j + 10]; + x0i = a[j + 9] + a[j + 11]; + x1r = a[j + 8] - a[j + 10]; + x1i = a[j + 9] - a[j + 11]; + x2r = a[j + 12] + a[j + 14]; + x2i = a[j + 13] + a[j + 15]; + x3r = a[j + 12] - a[j + 14]; + x3i = a[j + 13] - a[j + 15]; + a[j + 8] = x0r + x2r; + a[j + 9] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 12] = -wk2i * x0r - wk2r * x0i; + a[j + 13] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 10] = wk1r * x0r - wk1i * x0i; + a[j + 11] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 14] = wk3r * x0r - wk3i * x0i; + a[j + 15] = wk3r * x0i + wk3i * x0r; + } +} + +void cftmdl(size_t n, size_t l, float* a, float* w) { + size_t j, j1, j2, j3, k, k1, k2, m, m2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + m = l << 2; + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + wk1r = w[2]; + for (j = m; j < l + m; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x2i - x0i; + a[j2 + 1] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * (x0r - x0i); + a[j1 + 1] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[j3] = wk1r * (x0i - x0r); + a[j3 + 1] = wk1r * (x0i + x0r); + } + k1 = 0; + m2 = 2 * m; + for (k = m2; k < n; k += m2) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + for (j = k; j < l + k; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = wk2r * x0r - wk2i * x0i; + a[j2 + 1] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + for (j = k + m; j < l + (k + m); j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = -wk2i * x0r - wk2r * x0i; + a[j2 + 1] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + } +} + +void rftfsub(size_t n, float* a, size_t nc, float* c) { + size_t j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5f - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + +void rftbsub(size_t n, float* a, size_t nc, float* c) { + size_t j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + a[1] = -a[1]; + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5f - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j] -= yr; + a[j + 1] = yi - a[j + 1]; + a[k] += yr; + a[k + 1] = yi - a[k + 1]; + } + a[m + 1] = -a[m + 1]; +} + +} // namespace + +void WebRtc_rdft(size_t n, int isgn, float* a, size_t* ip, float* w) { + size_t nw, nc; + float xi; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 2)) { + nc = n >> 2; + makect(nc, ip, w + nw); + } + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, w); + } + xi = a[0] - a[1]; + a[0] += a[1]; + a[1] = xi; + } else { + a[1] = 0.5f * (a[0] - a[1]); + a[0] -= a[1]; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } else if (n == 4) { + cftfsub(n, a, w); + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.h b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.h new file mode 100644 index 00000000..5a465a35 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.h @@ -0,0 +1,23 @@ +/* + * 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 COMMON_AUDIO_THIRD_PARTY_OOURA_FFT_SIZE_256_FFT4G_H_ +#define COMMON_AUDIO_THIRD_PARTY_OOURA_FFT_SIZE_256_FFT4G_H_ + +#include + +namespace webrtc { + +// Refer to fft4g.c for documentation. +void WebRtc_rdft(size_t n, int isgn, float* a, size_t* ip, float* w); + +} // namespace webrtc + +#endif // COMMON_AUDIO_THIRD_PARTY_OOURA_FFT_SIZE_256_FFT4G_H_ diff --git a/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft_size_256.go b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft_size_256.go new file mode 100644 index 00000000..84805d5b --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/ooura/fft_size_256/fft_size_256.go @@ -0,0 +1,10 @@ +//go:build console + +package fft_size_256 + +// #cgo CXXFLAGS: -I${SRCDIR}/../../../.. -I${SRCDIR}/../../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c b/pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c new file mode 100644 index 00000000..b478a41b --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c @@ -0,0 +1,77 @@ +/* + * Written by Wilco Dijkstra, 1996. The following email exchange establishes the + * license. + * + * From: Wilco Dijkstra + * Date: Fri, Jun 24, 2011 at 3:20 AM + * Subject: Re: sqrt routine + * To: Kevin Ma + * Hi Kevin, + * Thanks for asking. Those routines are public domain (originally posted to + * comp.sys.arm a long time ago), so you can use them freely for any purpose. + * Cheers, + * Wilco + * + * ----- Original Message ----- + * From: "Kevin Ma" + * To: + * Sent: Thursday, June 23, 2011 11:44 PM + * Subject: Fwd: sqrt routine + * Hi Wilco, + * I saw your sqrt routine from several web sites, including + * http://www.finesse.demon.co.uk/steven/sqrt.html. + * Just wonder if there's any copyright information with your Successive + * approximation routines, or if I can freely use it for any purpose. + * Thanks. + * Kevin + */ + +// Minor modifications in code style for WebRTC, 2012. + +#include "common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h" + +/* + * Algorithm: + * Successive approximation of the equation (root + delta) ^ 2 = N + * until delta < 1. If delta < 1 we have the integer part of SQRT (N). + * Use delta = 2^i for i = 15 .. 0. + * + * Output precision is 16 bits. Note for large input values (close to + * 0x7FFFFFFF), bit 15 (the highest bit of the low 16-bit half word) + * contains the MSB information (a non-sign value). Do with caution + * if you need to cast the output to int16_t type. + * + * If the input value is negative, it returns 0. + */ + +#define WEBRTC_SPL_SQRT_ITER(N) \ + try1 = root + (1 << (N)); \ + if (value >= try1 << (N)) \ + { \ + value -= try1 << (N); \ + root |= 2 << (N); \ + } + +int32_t WebRtcSpl_SqrtFloor(int32_t value) +{ + int32_t root = 0, try1; + + WEBRTC_SPL_SQRT_ITER (15); + WEBRTC_SPL_SQRT_ITER (14); + WEBRTC_SPL_SQRT_ITER (13); + WEBRTC_SPL_SQRT_ITER (12); + WEBRTC_SPL_SQRT_ITER (11); + WEBRTC_SPL_SQRT_ITER (10); + WEBRTC_SPL_SQRT_ITER ( 9); + WEBRTC_SPL_SQRT_ITER ( 8); + WEBRTC_SPL_SQRT_ITER ( 7); + WEBRTC_SPL_SQRT_ITER ( 6); + WEBRTC_SPL_SQRT_ITER ( 5); + WEBRTC_SPL_SQRT_ITER ( 4); + WEBRTC_SPL_SQRT_ITER ( 3); + WEBRTC_SPL_SQRT_ITER ( 2); + WEBRTC_SPL_SQRT_ITER ( 1); + WEBRTC_SPL_SQRT_ITER ( 0); + + return root >> 1; +} diff --git a/pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.go b/pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.go new file mode 100644 index 00000000..ba8f2ed2 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.go @@ -0,0 +1,11 @@ +//go:build console + +package spl_sqrt_floor + + +// #cgo CFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CFLAGS: -DWEBRTC_WIN +// #cgo arm64 CFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h b/pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h new file mode 100644 index 00000000..718a18ff --- /dev/null +++ b/pkg/apm/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h @@ -0,0 +1,29 @@ +/* + * 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 + +// +// WebRtcSpl_SqrtFloor(...) +// +// Returns the square root of the input value `value`. The precision of this +// function is rounding down integer precision, i.e., sqrt(8) gives 2 as answer. +// If `value` is a negative number then 0 is returned. +// +// Algorithm: +// +// An iterative 4 cylce/bit routine +// +// Input: +// - value : Value to calculate sqrt of +// +// Return value : Result of the sqrt calculation +// +int32_t WebRtcSpl_SqrtFloor(int32_t value); diff --git a/pkg/apm/webrtc/common_audio/vad/include/vad.h b/pkg/apm/webrtc/common_audio/vad/include/vad.h new file mode 100644 index 00000000..b15275b1 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/include/vad.h @@ -0,0 +1,50 @@ +/* + * 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_VAD_INCLUDE_VAD_H_ +#define COMMON_AUDIO_VAD_INCLUDE_VAD_H_ + +#include + +#include "common_audio/vad/include/webrtc_vad.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +class Vad { + public: + enum Aggressiveness { + kVadNormal = 0, + kVadLowBitrate = 1, + kVadAggressive = 2, + kVadVeryAggressive = 3 + }; + + enum Activity { kPassive = 0, kActive = 1, kError = -1 }; + + virtual ~Vad() = default; + + // Calculates a VAD decision for the given audio frame. Valid sample rates + // are 8000, 16000, and 32000 Hz; the number of samples must be such that the + // frame is 10, 20, or 30 ms long. + virtual Activity VoiceActivity(const int16_t* audio, + size_t num_samples, + int sample_rate_hz) = 0; + + // Resets VAD state. + virtual void Reset() = 0; +}; + +// Returns a Vad instance that's implemented on top of WebRtcVad. +std::unique_ptr CreateVad(Vad::Aggressiveness aggressiveness); + +} // namespace webrtc + +#endif // COMMON_AUDIO_VAD_INCLUDE_VAD_H_ diff --git a/pkg/apm/webrtc/common_audio/vad/include/webrtc_vad.h b/pkg/apm/webrtc/common_audio/vad/include/webrtc_vad.h new file mode 100644 index 00000000..31e628f0 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/include/webrtc_vad.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +/* + * This header file includes the VAD API calls. Specific function calls are + * given below. + */ + +#ifndef COMMON_AUDIO_VAD_INCLUDE_WEBRTC_VAD_H_ // NOLINT +#define COMMON_AUDIO_VAD_INCLUDE_WEBRTC_VAD_H_ + +#include +#include + +typedef struct WebRtcVadInst VadInst; + +#ifdef __cplusplus +extern "C" { +#endif + +// Creates an instance to the VAD structure. +VadInst* WebRtcVad_Create(void); + +// Frees the dynamic memory of a specified VAD instance. +// +// - handle [i] : Pointer to VAD instance that should be freed. +void WebRtcVad_Free(VadInst* handle); + +// Initializes a VAD instance. +// +// - handle [i/o] : Instance that should be initialized. +// +// returns : 0 - (OK), +// -1 - (null pointer or Default mode could not be set). +int WebRtcVad_Init(VadInst* handle); + +// Sets the VAD operating mode. A more aggressive (higher mode) VAD is more +// restrictive in reporting speech. Put in other words the probability of being +// speech when the VAD returns 1 is increased with increasing mode. As a +// consequence also the missed detection rate goes up. +// +// - handle [i/o] : VAD instance. +// - mode [i] : Aggressiveness mode (0, 1, 2, or 3). +// +// returns : 0 - (OK), +// -1 - (null pointer, mode could not be set or the VAD instance +// has not been initialized). +int WebRtcVad_set_mode(VadInst* handle, int mode); + +// Calculates a VAD decision for the `audio_frame`. For valid sampling rates +// frame lengths, see the description of WebRtcVad_ValidRatesAndFrameLengths(). +// +// - handle [i/o] : VAD Instance. Needs to be initialized by +// WebRtcVad_Init() before call. +// - fs [i] : Sampling frequency (Hz): 8000, 16000, or 32000 +// - audio_frame [i] : Audio frame buffer. +// - frame_length [i] : Length of audio frame buffer in number of samples. +// +// returns : 1 - (Active Voice), +// 0 - (Non-active Voice), +// -1 - (Error) +int WebRtcVad_Process(VadInst* handle, + int fs, + const int16_t* audio_frame, + size_t frame_length); + +// Checks for valid combinations of `rate` and `frame_length`. We support 10, +// 20 and 30 ms frames and the rates 8000, 16000 and 32000 Hz. +// +// - rate [i] : Sampling frequency (Hz). +// - frame_length [i] : Speech frame buffer length in number of samples. +// +// returns : 0 - (valid combination), -1 - (invalid combination) +int WebRtcVad_ValidRateAndFrameLength(int rate, size_t frame_length); + +#ifdef __cplusplus +} +#endif + +#endif // COMMON_AUDIO_VAD_INCLUDE_WEBRTC_VAD_H_ // NOLINT diff --git a/pkg/apm/webrtc/common_audio/vad/vad.cc b/pkg/apm/webrtc/common_audio/vad/vad.cc new file mode 100644 index 00000000..16472465 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/vad.cc @@ -0,0 +1,66 @@ +/* + * 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/vad/include/vad.h" + +#include + +#include "common_audio/vad/include/webrtc_vad.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +class VadImpl final : public Vad { + public: + explicit VadImpl(Aggressiveness aggressiveness) + : handle_(nullptr), aggressiveness_(aggressiveness) { + Reset(); + } + + ~VadImpl() override { WebRtcVad_Free(handle_); } + + Activity VoiceActivity(const int16_t* audio, + size_t num_samples, + int sample_rate_hz) override { + int ret = WebRtcVad_Process(handle_, sample_rate_hz, audio, num_samples); + switch (ret) { + case 0: + return kPassive; + case 1: + return kActive; + default: + RTC_DCHECK_NOTREACHED() << "WebRtcVad_Process returned an error."; + return kError; + } + } + + void Reset() override { + if (handle_) + WebRtcVad_Free(handle_); + handle_ = WebRtcVad_Create(); + RTC_CHECK(handle_); + RTC_CHECK_EQ(WebRtcVad_Init(handle_), 0); + RTC_CHECK_EQ(WebRtcVad_set_mode(handle_, aggressiveness_), 0); + } + + private: + VadInst* handle_; + Aggressiveness aggressiveness_; +}; + +} // namespace + +std::unique_ptr CreateVad(Vad::Aggressiveness aggressiveness) { + return std::unique_ptr(new VadImpl(aggressiveness)); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/vad/vad.go b/pkg/apm/webrtc/common_audio/vad/vad.go new file mode 100644 index 00000000..3120e15b --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/vad.go @@ -0,0 +1,15 @@ +//go:build console + +package vad + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +// #cgo CFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CFLAGS: -DWEBRTC_WIN +// #cgo arm64 CFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/common_audio/vad/vad_core.c b/pkg/apm/webrtc/common_audio/vad/vad_core.c new file mode 100644 index 00000000..9b40f421 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/vad_core.c @@ -0,0 +1,677 @@ +/* + * 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 "common_audio/vad/vad_core.h" + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/vad/vad_filterbank.h" +#include "common_audio/vad/vad_gmm.h" +#include "common_audio/vad/vad_sp.h" +#include "rtc_base/sanitizer.h" + +// Spectrum Weighting +static const int16_t kSpectrumWeight[kNumChannels] = {6, 8, 10, 12, 14, 16}; +static const int16_t kNoiseUpdateConst = 655; // Q15 +static const int16_t kSpeechUpdateConst = 6554; // Q15 +static const int16_t kBackEta = 154; // Q8 +// Minimum difference between the two models, Q5 +static const int16_t kMinimumDifference[kNumChannels] = {544, 544, 576, + 576, 576, 576}; +// Upper limit of mean value for speech model, Q7 +static const int16_t kMaximumSpeech[kNumChannels] = {11392, 11392, 11520, + 11520, 11520, 11520}; +// Minimum value for mean value +static const int16_t kMinimumMean[kNumGaussians] = {640, 768}; +// Upper limit of mean value for noise model, Q7 +static const int16_t kMaximumNoise[kNumChannels] = {9216, 9088, 8960, + 8832, 8704, 8576}; +// Start values for the Gaussian models, Q7 +// Weights for the two Gaussians for the six channels (noise) +static const int16_t kNoiseDataWeights[kTableSize] = {34, 62, 72, 66, 53, 25, + 94, 66, 56, 62, 75, 103}; +// Weights for the two Gaussians for the six channels (speech) +static const int16_t kSpeechDataWeights[kTableSize] = {48, 82, 45, 87, 50, 47, + 80, 46, 83, 41, 78, 81}; +// Means for the two Gaussians for the six channels (noise) +static const int16_t kNoiseDataMeans[kTableSize] = { + 6738, 4892, 7065, 6715, 6771, 3369, 7646, 3863, 7820, 7266, 5020, 4362}; +// Means for the two Gaussians for the six channels (speech) +static const int16_t kSpeechDataMeans[kTableSize] = {8306, 10085, 10078, 11823, + 11843, 6309, 9473, 9571, + 10879, 7581, 8180, 7483}; +// Stds for the two Gaussians for the six channels (noise) +static const int16_t kNoiseDataStds[kTableSize] = { + 378, 1064, 493, 582, 688, 593, 474, 697, 475, 688, 421, 455}; +// Stds for the two Gaussians for the six channels (speech) +static const int16_t kSpeechDataStds[kTableSize] = { + 555, 505, 567, 524, 585, 1231, 509, 828, 492, 1540, 1079, 850}; + +// Constants used in GmmProbability(). +// +// Maximum number of counted speech (VAD = 1) frames in a row. +static const int16_t kMaxSpeechFrames = 6; +// Minimum standard deviation for both speech and noise. +static const int16_t kMinStd = 384; + +// Constants in WebRtcVad_InitCore(). +// Default aggressiveness mode. +static const short kDefaultMode = 0; +static const int kInitCheck = 42; + +// Constants used in WebRtcVad_set_mode_core(). +// +// Thresholds for different frame lengths (10 ms, 20 ms and 30 ms). +// +// Mode 0, Quality. +static const int16_t kOverHangMax1Q[3] = {8, 4, 3}; +static const int16_t kOverHangMax2Q[3] = {14, 7, 5}; +static const int16_t kLocalThresholdQ[3] = {24, 21, 24}; +static const int16_t kGlobalThresholdQ[3] = {57, 48, 57}; +// Mode 1, Low bitrate. +static const int16_t kOverHangMax1LBR[3] = {8, 4, 3}; +static const int16_t kOverHangMax2LBR[3] = {14, 7, 5}; +static const int16_t kLocalThresholdLBR[3] = {37, 32, 37}; +static const int16_t kGlobalThresholdLBR[3] = {100, 80, 100}; +// Mode 2, Aggressive. +static const int16_t kOverHangMax1AGG[3] = {6, 3, 2}; +static const int16_t kOverHangMax2AGG[3] = {9, 5, 3}; +static const int16_t kLocalThresholdAGG[3] = {82, 78, 82}; +static const int16_t kGlobalThresholdAGG[3] = {285, 260, 285}; +// Mode 3, Very aggressive. +static const int16_t kOverHangMax1VAG[3] = {6, 3, 2}; +static const int16_t kOverHangMax2VAG[3] = {9, 5, 3}; +static const int16_t kLocalThresholdVAG[3] = {94, 94, 94}; +static const int16_t kGlobalThresholdVAG[3] = {1100, 1050, 1100}; + +// Calculates the weighted average w.r.t. number of Gaussians. The `data` are +// updated with an `offset` before averaging. +// +// - data [i/o] : Data to average. +// - offset [i] : An offset added to `data`. +// - weights [i] : Weights used for averaging. +// +// returns : The weighted average. +static int32_t WeightedAverage(int16_t* data, + int16_t offset, + const int16_t* weights) { + int k; + int32_t weighted_average = 0; + + for (k = 0; k < kNumGaussians; k++) { + data[k * kNumChannels] += offset; + weighted_average += data[k * kNumChannels] * weights[k * kNumChannels]; + } + return weighted_average; +} + +// An s16 x s32 -> s32 multiplication that's allowed to overflow. (It's still +// undefined behavior, so not a good idea; this just makes UBSan ignore the +// violation, so that our old code can continue to do what it's always been +// doing.) +static inline int32_t RTC_NO_SANITIZE("signed-integer-overflow") + OverflowingMulS16ByS32ToS32(int16_t a, int32_t b) { + return a * b; +} + +// Calculates the probabilities for both speech and background noise using +// Gaussian Mixture Models (GMM). A hypothesis-test is performed to decide which +// type of signal is most probable. +// +// - self [i/o] : Pointer to VAD instance +// - features [i] : Feature vector of length `kNumChannels` +// = log10(energy in frequency band) +// - total_power [i] : Total power in audio frame. +// - frame_length [i] : Number of input samples +// +// - returns : the VAD decision (0 - noise, 1 - speech). +static int16_t GmmProbability(VadInstT* self, + int16_t* features, + int16_t total_power, + size_t frame_length) { + int channel, k; + int16_t feature_minimum; + int16_t h0, h1; + int16_t log_likelihood_ratio; + int16_t vadflag = 0; + int16_t shifts_h0, shifts_h1; + int16_t tmp_s16, tmp1_s16, tmp2_s16; + int16_t diff; + int gaussian; + int16_t nmk, nmk2, nmk3, smk, smk2, nsk, ssk; + int16_t delt, ndelt; + int16_t maxspe, maxmu; + int16_t deltaN[kTableSize], deltaS[kTableSize]; + int16_t ngprvec[kTableSize] = {0}; // Conditional probability = 0. + int16_t sgprvec[kTableSize] = {0}; // Conditional probability = 0. + int32_t h0_test, h1_test; + int32_t tmp1_s32, tmp2_s32; + int32_t sum_log_likelihood_ratios = 0; + int32_t noise_global_mean, speech_global_mean; + int32_t noise_probability[kNumGaussians], speech_probability[kNumGaussians]; + int16_t overhead1, overhead2, individualTest, totalTest; + + // Set various thresholds based on frame lengths (80, 160 or 240 samples). + if (frame_length == 80) { + overhead1 = self->over_hang_max_1[0]; + overhead2 = self->over_hang_max_2[0]; + individualTest = self->individual[0]; + totalTest = self->total[0]; + } else if (frame_length == 160) { + overhead1 = self->over_hang_max_1[1]; + overhead2 = self->over_hang_max_2[1]; + individualTest = self->individual[1]; + totalTest = self->total[1]; + } else { + overhead1 = self->over_hang_max_1[2]; + overhead2 = self->over_hang_max_2[2]; + individualTest = self->individual[2]; + totalTest = self->total[2]; + } + + if (total_power > kMinEnergy) { + // The signal power of current frame is large enough for processing. The + // processing consists of two parts: + // 1) Calculating the likelihood of speech and thereby a VAD decision. + // 2) Updating the underlying model, w.r.t., the decision made. + + // The detection scheme is an LRT with hypothesis + // H0: Noise + // H1: Speech + // + // We combine a global LRT with local tests, for each frequency sub-band, + // here defined as `channel`. + for (channel = 0; channel < kNumChannels; channel++) { + // For each channel we model the probability with a GMM consisting of + // `kNumGaussians`, with different means and standard deviations depending + // on H0 or H1. + h0_test = 0; + h1_test = 0; + for (k = 0; k < kNumGaussians; k++) { + gaussian = channel + k * kNumChannels; + // Probability under H0, that is, probability of frame being noise. + // Value given in Q27 = Q7 * Q20. + tmp1_s32 = WebRtcVad_GaussianProbability( + features[channel], self->noise_means[gaussian], + self->noise_stds[gaussian], &deltaN[gaussian]); + noise_probability[k] = kNoiseDataWeights[gaussian] * tmp1_s32; + h0_test += noise_probability[k]; // Q27 + + // Probability under H1, that is, probability of frame being speech. + // Value given in Q27 = Q7 * Q20. + tmp1_s32 = WebRtcVad_GaussianProbability( + features[channel], self->speech_means[gaussian], + self->speech_stds[gaussian], &deltaS[gaussian]); + speech_probability[k] = kSpeechDataWeights[gaussian] * tmp1_s32; + h1_test += speech_probability[k]; // Q27 + } + + // Calculate the log likelihood ratio: log2(Pr{X|H1} / Pr{X|H1}). + // Approximation: + // log2(Pr{X|H1} / Pr{X|H1}) = log2(Pr{X|H1}*2^Q) - log2(Pr{X|H1}*2^Q) + // = log2(h1_test) - log2(h0_test) + // = log2(2^(31-shifts_h1)*(1+b1)) + // - log2(2^(31-shifts_h0)*(1+b0)) + // = shifts_h0 - shifts_h1 + // + log2(1+b1) - log2(1+b0) + // ~= shifts_h0 - shifts_h1 + // + // Note that b0 and b1 are values less than 1, hence, 0 <= log2(1+b0) < 1. + // Further, b0 and b1 are independent and on the average the two terms + // cancel. + shifts_h0 = WebRtcSpl_NormW32(h0_test); + shifts_h1 = WebRtcSpl_NormW32(h1_test); + if (h0_test == 0) { + shifts_h0 = 31; + } + if (h1_test == 0) { + shifts_h1 = 31; + } + log_likelihood_ratio = shifts_h0 - shifts_h1; + + // Update `sum_log_likelihood_ratios` with spectrum weighting. This is + // used for the global VAD decision. + sum_log_likelihood_ratios += + (int32_t)(log_likelihood_ratio * kSpectrumWeight[channel]); + + // Local VAD decision. + if ((log_likelihood_ratio * 4) > individualTest) { + vadflag = 1; + } + + // TODO(bjornv): The conditional probabilities below are applied on the + // hard coded number of Gaussians set to two. Find a way to generalize. + // Calculate local noise probabilities used later when updating the GMM. + h0 = (int16_t)(h0_test >> 12); // Q15 + if (h0 > 0) { + // High probability of noise. Assign conditional probabilities for each + // Gaussian in the GMM. + tmp1_s32 = (noise_probability[0] & 0xFFFFF000) << 2; // Q29 + ngprvec[channel] = (int16_t)WebRtcSpl_DivW32W16(tmp1_s32, h0); // Q14 + ngprvec[channel + kNumChannels] = 16384 - ngprvec[channel]; + } else { + // Low noise probability. Assign conditional probability 1 to the first + // Gaussian and 0 to the rest (which is already set at initialization). + ngprvec[channel] = 16384; + } + + // Calculate local speech probabilities used later when updating the GMM. + h1 = (int16_t)(h1_test >> 12); // Q15 + if (h1 > 0) { + // High probability of speech. Assign conditional probabilities for each + // Gaussian in the GMM. Otherwise use the initialized values, i.e., 0. + tmp1_s32 = (speech_probability[0] & 0xFFFFF000) << 2; // Q29 + sgprvec[channel] = (int16_t)WebRtcSpl_DivW32W16(tmp1_s32, h1); // Q14 + sgprvec[channel + kNumChannels] = 16384 - sgprvec[channel]; + } + } + + // Make a global VAD decision. + vadflag |= (sum_log_likelihood_ratios >= totalTest); + + // Update the model parameters. + maxspe = 12800; + for (channel = 0; channel < kNumChannels; channel++) { + // Get minimum value in past which is used for long term correction in Q4. + feature_minimum = WebRtcVad_FindMinimum(self, features[channel], channel); + + // Compute the "global" mean, that is the sum of the two means weighted. + noise_global_mean = WeightedAverage(&self->noise_means[channel], 0, + &kNoiseDataWeights[channel]); + tmp1_s16 = (int16_t)(noise_global_mean >> 6); // Q8 + + for (k = 0; k < kNumGaussians; k++) { + gaussian = channel + k * kNumChannels; + + nmk = self->noise_means[gaussian]; + smk = self->speech_means[gaussian]; + nsk = self->noise_stds[gaussian]; + ssk = self->speech_stds[gaussian]; + + // Update noise mean vector if the frame consists of noise only. + nmk2 = nmk; + if (!vadflag) { + // deltaN = (x-mu)/sigma^2 + // ngprvec[k] = `noise_probability[k]` / + // (`noise_probability[0]` + `noise_probability[1]`) + + // (Q14 * Q11 >> 11) = Q14. + delt = (int16_t)((ngprvec[gaussian] * deltaN[gaussian]) >> 11); + // Q7 + (Q14 * Q15 >> 22) = Q7. + nmk2 = nmk + (int16_t)((delt * kNoiseUpdateConst) >> 22); + } + + // Long term correction of the noise mean. + // Q8 - Q8 = Q8. + ndelt = (feature_minimum << 4) - tmp1_s16; + // Q7 + (Q8 * Q8) >> 9 = Q7. + nmk3 = nmk2 + (int16_t)((ndelt * kBackEta) >> 9); + + // Control that the noise mean does not drift to much. + tmp_s16 = (int16_t)((k + 5) << 7); + if (nmk3 < tmp_s16) { + nmk3 = tmp_s16; + } + tmp_s16 = (int16_t)((72 + k - channel) << 7); + if (nmk3 > tmp_s16) { + nmk3 = tmp_s16; + } + self->noise_means[gaussian] = nmk3; + + if (vadflag) { + // Update speech mean vector: + // `deltaS` = (x-mu)/sigma^2 + // sgprvec[k] = `speech_probability[k]` / + // (`speech_probability[0]` + `speech_probability[1]`) + + // (Q14 * Q11) >> 11 = Q14. + delt = (int16_t)((sgprvec[gaussian] * deltaS[gaussian]) >> 11); + // Q14 * Q15 >> 21 = Q8. + tmp_s16 = (int16_t)((delt * kSpeechUpdateConst) >> 21); + // Q7 + (Q8 >> 1) = Q7. With rounding. + smk2 = smk + ((tmp_s16 + 1) >> 1); + + // Control that the speech mean does not drift to much. + maxmu = maxspe + 640; + if (smk2 < kMinimumMean[k]) { + smk2 = kMinimumMean[k]; + } + if (smk2 > maxmu) { + smk2 = maxmu; + } + self->speech_means[gaussian] = smk2; // Q7. + + // (Q7 >> 3) = Q4. With rounding. + tmp_s16 = ((smk + 4) >> 3); + + tmp_s16 = features[channel] - tmp_s16; // Q4 + // (Q11 * Q4 >> 3) = Q12. + tmp1_s32 = (deltaS[gaussian] * tmp_s16) >> 3; + tmp2_s32 = tmp1_s32 - 4096; + tmp_s16 = sgprvec[gaussian] >> 2; + // (Q14 >> 2) * Q12 = Q24. + tmp1_s32 = tmp_s16 * tmp2_s32; + + tmp2_s32 = tmp1_s32 >> 4; // Q20 + + // 0.1 * Q20 / Q7 = Q13. + if (tmp2_s32 > 0) { + tmp_s16 = (int16_t)WebRtcSpl_DivW32W16(tmp2_s32, ssk * 10); + } else { + tmp_s16 = (int16_t)WebRtcSpl_DivW32W16(-tmp2_s32, ssk * 10); + tmp_s16 = -tmp_s16; + } + // Divide by 4 giving an update factor of 0.025 (= 0.1 / 4). + // Note that division by 4 equals shift by 2, hence, + // (Q13 >> 8) = (Q13 >> 6) / 4 = Q7. + tmp_s16 += 128; // Rounding. + ssk += (tmp_s16 >> 8); + if (ssk < kMinStd) { + ssk = kMinStd; + } + self->speech_stds[gaussian] = ssk; + } else { + // Update GMM variance vectors. + // deltaN * (features[channel] - nmk) - 1 + // Q4 - (Q7 >> 3) = Q4. + tmp_s16 = features[channel] - (nmk >> 3); + // (Q11 * Q4 >> 3) = Q12. + tmp1_s32 = (deltaN[gaussian] * tmp_s16) >> 3; + tmp1_s32 -= 4096; + + // (Q14 >> 2) * Q12 = Q24. + tmp_s16 = (ngprvec[gaussian] + 2) >> 2; + tmp2_s32 = OverflowingMulS16ByS32ToS32(tmp_s16, tmp1_s32); + // Q20 * approx 0.001 (2^-10=0.0009766), hence, + // (Q24 >> 14) = (Q24 >> 4) / 2^10 = Q20. + tmp1_s32 = tmp2_s32 >> 14; + + // Q20 / Q7 = Q13. + if (tmp1_s32 > 0) { + tmp_s16 = (int16_t)WebRtcSpl_DivW32W16(tmp1_s32, nsk); + } else { + tmp_s16 = (int16_t)WebRtcSpl_DivW32W16(-tmp1_s32, nsk); + tmp_s16 = -tmp_s16; + } + tmp_s16 += 32; // Rounding + nsk += tmp_s16 >> 6; // Q13 >> 6 = Q7. + if (nsk < kMinStd) { + nsk = kMinStd; + } + self->noise_stds[gaussian] = nsk; + } + } + + // Separate models if they are too close. + // `noise_global_mean` in Q14 (= Q7 * Q7). + noise_global_mean = WeightedAverage(&self->noise_means[channel], 0, + &kNoiseDataWeights[channel]); + + // `speech_global_mean` in Q14 (= Q7 * Q7). + speech_global_mean = WeightedAverage(&self->speech_means[channel], 0, + &kSpeechDataWeights[channel]); + + // `diff` = "global" speech mean - "global" noise mean. + // (Q14 >> 9) - (Q14 >> 9) = Q5. + diff = (int16_t)(speech_global_mean >> 9) - + (int16_t)(noise_global_mean >> 9); + if (diff < kMinimumDifference[channel]) { + tmp_s16 = kMinimumDifference[channel] - diff; + + // `tmp1_s16` = ~0.8 * (kMinimumDifference - diff) in Q7. + // `tmp2_s16` = ~0.2 * (kMinimumDifference - diff) in Q7. + tmp1_s16 = (int16_t)((13 * tmp_s16) >> 2); + tmp2_s16 = (int16_t)((3 * tmp_s16) >> 2); + + // Move Gaussian means for speech model by `tmp1_s16` and update + // `speech_global_mean`. Note that `self->speech_means[channel]` is + // changed after the call. + speech_global_mean = + WeightedAverage(&self->speech_means[channel], tmp1_s16, + &kSpeechDataWeights[channel]); + + // Move Gaussian means for noise model by -`tmp2_s16` and update + // `noise_global_mean`. Note that `self->noise_means[channel]` is + // changed after the call. + noise_global_mean = + WeightedAverage(&self->noise_means[channel], -tmp2_s16, + &kNoiseDataWeights[channel]); + } + + // Control that the speech & noise means do not drift to much. + maxspe = kMaximumSpeech[channel]; + tmp2_s16 = (int16_t)(speech_global_mean >> 7); + if (tmp2_s16 > maxspe) { + // Upper limit of speech model. + tmp2_s16 -= maxspe; + + for (k = 0; k < kNumGaussians; k++) { + self->speech_means[channel + k * kNumChannels] -= tmp2_s16; + } + } + + tmp2_s16 = (int16_t)(noise_global_mean >> 7); + if (tmp2_s16 > kMaximumNoise[channel]) { + tmp2_s16 -= kMaximumNoise[channel]; + + for (k = 0; k < kNumGaussians; k++) { + self->noise_means[channel + k * kNumChannels] -= tmp2_s16; + } + } + } + self->frame_counter++; + } + + // Smooth with respect to transition hysteresis. + if (!vadflag) { + if (self->over_hang > 0) { + vadflag = 2 + self->over_hang; + self->over_hang--; + } + self->num_of_speech = 0; + } else { + self->num_of_speech++; + if (self->num_of_speech > kMaxSpeechFrames) { + self->num_of_speech = kMaxSpeechFrames; + self->over_hang = overhead2; + } else { + self->over_hang = overhead1; + } + } + return vadflag; +} + +// Initialize the VAD. Set aggressiveness mode to default value. +int WebRtcVad_InitCore(VadInstT* self) { + int i; + + if (self == NULL) { + return -1; + } + + // Initialization of general struct variables. + self->vad = 1; // Speech active (=1). + self->frame_counter = 0; + self->over_hang = 0; + self->num_of_speech = 0; + + // Initialization of downsampling filter state. + memset(self->downsampling_filter_states, 0, + sizeof(self->downsampling_filter_states)); + + // Initialization of 48 to 8 kHz downsampling. + WebRtcSpl_ResetResample48khzTo8khz(&self->state_48_to_8); + + // Read initial PDF parameters. + for (i = 0; i < kTableSize; i++) { + self->noise_means[i] = kNoiseDataMeans[i]; + self->speech_means[i] = kSpeechDataMeans[i]; + self->noise_stds[i] = kNoiseDataStds[i]; + self->speech_stds[i] = kSpeechDataStds[i]; + } + + // Initialize Index and Minimum value vectors. + for (i = 0; i < 16 * kNumChannels; i++) { + self->low_value_vector[i] = 10000; + self->index_vector[i] = 0; + } + + // Initialize splitting filter states. + memset(self->upper_state, 0, sizeof(self->upper_state)); + memset(self->lower_state, 0, sizeof(self->lower_state)); + + // Initialize high pass filter states. + memset(self->hp_filter_state, 0, sizeof(self->hp_filter_state)); + + // Initialize mean value memory, for WebRtcVad_FindMinimum(). + for (i = 0; i < kNumChannels; i++) { + self->mean_value[i] = 1600; + } + + // Set aggressiveness mode to default (=`kDefaultMode`). + if (WebRtcVad_set_mode_core(self, kDefaultMode) != 0) { + return -1; + } + + self->init_flag = kInitCheck; + + return 0; +} + +// Set aggressiveness mode +int WebRtcVad_set_mode_core(VadInstT* self, int mode) { + int return_value = 0; + + switch (mode) { + case 0: + // Quality mode. + memcpy(self->over_hang_max_1, kOverHangMax1Q, + sizeof(self->over_hang_max_1)); + memcpy(self->over_hang_max_2, kOverHangMax2Q, + sizeof(self->over_hang_max_2)); + memcpy(self->individual, kLocalThresholdQ, sizeof(self->individual)); + memcpy(self->total, kGlobalThresholdQ, sizeof(self->total)); + break; + case 1: + // Low bitrate mode. + memcpy(self->over_hang_max_1, kOverHangMax1LBR, + sizeof(self->over_hang_max_1)); + memcpy(self->over_hang_max_2, kOverHangMax2LBR, + sizeof(self->over_hang_max_2)); + memcpy(self->individual, kLocalThresholdLBR, sizeof(self->individual)); + memcpy(self->total, kGlobalThresholdLBR, sizeof(self->total)); + break; + case 2: + // Aggressive mode. + memcpy(self->over_hang_max_1, kOverHangMax1AGG, + sizeof(self->over_hang_max_1)); + memcpy(self->over_hang_max_2, kOverHangMax2AGG, + sizeof(self->over_hang_max_2)); + memcpy(self->individual, kLocalThresholdAGG, sizeof(self->individual)); + memcpy(self->total, kGlobalThresholdAGG, sizeof(self->total)); + break; + case 3: + // Very aggressive mode. + memcpy(self->over_hang_max_1, kOverHangMax1VAG, + sizeof(self->over_hang_max_1)); + memcpy(self->over_hang_max_2, kOverHangMax2VAG, + sizeof(self->over_hang_max_2)); + memcpy(self->individual, kLocalThresholdVAG, sizeof(self->individual)); + memcpy(self->total, kGlobalThresholdVAG, sizeof(self->total)); + break; + default: + return_value = -1; + break; + } + + return return_value; +} + +// Calculate VAD decision by first extracting feature values and then calculate +// probability for both speech and background noise. + +int WebRtcVad_CalcVad48khz(VadInstT* inst, + const int16_t* speech_frame, + size_t frame_length) { + int vad; + size_t i; + int16_t speech_nb[240]; // 30 ms in 8 kHz. + // `tmp_mem` is a temporary memory used by resample function, length is + // frame length in 10 ms (480 samples) + 256 extra. + int32_t tmp_mem[480 + 256] = {0}; + const size_t kFrameLen10ms48khz = 480; + const size_t kFrameLen10ms8khz = 80; + size_t num_10ms_frames = frame_length / kFrameLen10ms48khz; + + for (i = 0; i < num_10ms_frames; i++) { + WebRtcSpl_Resample48khzTo8khz(speech_frame, + &speech_nb[i * kFrameLen10ms8khz], + &inst->state_48_to_8, tmp_mem); + } + + // Do VAD on an 8 kHz signal + vad = WebRtcVad_CalcVad8khz(inst, speech_nb, frame_length / 6); + + return vad; +} + +int WebRtcVad_CalcVad32khz(VadInstT* inst, + const int16_t* speech_frame, + size_t frame_length) { + size_t len; + int vad; + int16_t speechWB[480]; // Downsampled speech frame: 960 samples (30ms in SWB) + int16_t speechNB[240]; // Downsampled speech frame: 480 samples (30ms in WB) + + // Downsample signal 32->16->8 before doing VAD + WebRtcVad_Downsampling(speech_frame, speechWB, + &(inst->downsampling_filter_states[2]), frame_length); + len = frame_length / 2; + + WebRtcVad_Downsampling(speechWB, speechNB, inst->downsampling_filter_states, + len); + len /= 2; + + // Do VAD on an 8 kHz signal + vad = WebRtcVad_CalcVad8khz(inst, speechNB, len); + + return vad; +} + +int WebRtcVad_CalcVad16khz(VadInstT* inst, + const int16_t* speech_frame, + size_t frame_length) { + size_t len; + int vad; + int16_t speechNB[240]; // Downsampled speech frame: 480 samples (30ms in WB) + + // Wideband: Downsample signal before doing VAD + WebRtcVad_Downsampling(speech_frame, speechNB, + inst->downsampling_filter_states, frame_length); + + len = frame_length / 2; + vad = WebRtcVad_CalcVad8khz(inst, speechNB, len); + + return vad; +} + +int WebRtcVad_CalcVad8khz(VadInstT* inst, + const int16_t* speech_frame, + size_t frame_length) { + int16_t feature_vector[kNumChannels], total_power; + + // Get power in the bands + total_power = WebRtcVad_CalculateFeatures(inst, speech_frame, frame_length, + feature_vector); + + // Make a VAD + inst->vad = GmmProbability(inst, feature_vector, total_power, frame_length); + + return inst->vad; +} diff --git a/pkg/apm/webrtc/common_audio/vad/vad_core.h b/pkg/apm/webrtc/common_audio/vad/vad_core.h new file mode 100644 index 00000000..fbaf9700 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/vad_core.h @@ -0,0 +1,123 @@ +/* + * 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. + */ + +/* + * This header file includes the descriptions of the core VAD calls. + */ + +#ifndef COMMON_AUDIO_VAD_VAD_CORE_H_ +#define COMMON_AUDIO_VAD_VAD_CORE_H_ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +// TODO(https://bugs.webrtc.org/14476): When converted to C++, remove the macro. +#if defined(__cplusplus) +#define CONSTEXPR_INT(x) constexpr int x +#else +#define CONSTEXPR_INT(x) enum { x } +#endif + +CONSTEXPR_INT(kNumChannels = 6); // Number of frequency bands (named channels). +CONSTEXPR_INT( + kNumGaussians = 2); // Number of Gaussians per channel in the GMM. +CONSTEXPR_INT(kTableSize = kNumChannels * kNumGaussians); +CONSTEXPR_INT( + kMinEnergy = 10); // Minimum energy required to trigger audio signal. + +typedef struct VadInstT_ { + int vad; + int32_t downsampling_filter_states[4]; + WebRtcSpl_State48khzTo8khz state_48_to_8; + int16_t noise_means[kTableSize]; + int16_t speech_means[kTableSize]; + int16_t noise_stds[kTableSize]; + int16_t speech_stds[kTableSize]; + // TODO(bjornv): Change to `frame_count`. + int32_t frame_counter; + int16_t over_hang; // Over Hang + int16_t num_of_speech; + // TODO(bjornv): Change to `age_vector`. + int16_t index_vector[16 * kNumChannels]; + int16_t low_value_vector[16 * kNumChannels]; + // TODO(bjornv): Change to `median`. + int16_t mean_value[kNumChannels]; + int16_t upper_state[5]; + int16_t lower_state[5]; + int16_t hp_filter_state[4]; + int16_t over_hang_max_1[3]; + int16_t over_hang_max_2[3]; + int16_t individual[3]; + int16_t total[3]; + + int init_flag; +} VadInstT; + +// Initializes the core VAD component. The default aggressiveness mode is +// controlled by `kDefaultMode` in vad_core.c. +// +// - self [i/o] : Instance that should be initialized +// +// returns : 0 (OK), -1 (null pointer in or if the default mode can't be +// set) +int WebRtcVad_InitCore(VadInstT* self); + +/**************************************************************************** + * WebRtcVad_set_mode_core(...) + * + * This function changes the VAD settings + * + * Input: + * - inst : VAD instance + * - mode : Aggressiveness degree + * 0 (High quality) - 3 (Highly aggressive) + * + * Output: + * - inst : Changed instance + * + * Return value : 0 - Ok + * -1 - Error + */ + +int WebRtcVad_set_mode_core(VadInstT* self, int mode); + +/**************************************************************************** + * WebRtcVad_CalcVad48khz(...) + * WebRtcVad_CalcVad32khz(...) + * WebRtcVad_CalcVad16khz(...) + * WebRtcVad_CalcVad8khz(...) + * + * Calculate probability for active speech and make VAD decision. + * + * Input: + * - inst : Instance that should be initialized + * - speech_frame : Input speech frame + * - frame_length : Number of input samples + * + * Output: + * - inst : Updated filter states etc. + * + * Return value : VAD decision + * 0 - No active speech + * 1-6 - Active speech + */ +int WebRtcVad_CalcVad48khz(VadInstT* inst, + const int16_t* speech_frame, + size_t frame_length); +int WebRtcVad_CalcVad32khz(VadInstT* inst, + const int16_t* speech_frame, + size_t frame_length); +int WebRtcVad_CalcVad16khz(VadInstT* inst, + const int16_t* speech_frame, + size_t frame_length); +int WebRtcVad_CalcVad8khz(VadInstT* inst, + const int16_t* speech_frame, + size_t frame_length); + +#endif // COMMON_AUDIO_VAD_VAD_CORE_H_ diff --git a/pkg/apm/webrtc/common_audio/vad/vad_filterbank.c b/pkg/apm/webrtc/common_audio/vad/vad_filterbank.c new file mode 100644 index 00000000..32830fa0 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/vad_filterbank.c @@ -0,0 +1,339 @@ +/* + * 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 "common_audio/vad/vad_filterbank.h" + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" + +// Constants used in LogOfEnergy(). +static const int16_t kLogConst = 24660; // 160*log10(2) in Q9. +static const int16_t kLogEnergyIntPart = 14336; // 14 in Q10 + +// Coefficients used by HighPassFilter, Q14. +static const int16_t kHpZeroCoefs[3] = {6631, -13262, 6631}; +static const int16_t kHpPoleCoefs[3] = {16384, -7756, 5620}; + +// Allpass filter coefficients, upper and lower, in Q15. +// Upper: 0.64, Lower: 0.17 +static const int16_t kAllPassCoefsQ15[2] = {20972, 5571}; + +// Adjustment for division with two in SplitFilter. +static const int16_t kOffsetVector[6] = {368, 368, 272, 176, 176, 176}; + +// High pass filtering, with a cut-off frequency at 80 Hz, if the `data_in` is +// sampled at 500 Hz. +// +// - data_in [i] : Input audio data sampled at 500 Hz. +// - data_length [i] : Length of input and output data. +// - filter_state [i/o] : State of the filter. +// - data_out [o] : Output audio data in the frequency interval +// 80 - 250 Hz. +static void HighPassFilter(const int16_t* data_in, + size_t data_length, + int16_t* filter_state, + int16_t* data_out) { + size_t i; + const int16_t* in_ptr = data_in; + int16_t* out_ptr = data_out; + int32_t tmp32 = 0; + + // The sum of the absolute values of the impulse response: + // The zero/pole-filter has a max amplification of a single sample of: 1.4546 + // Impulse response: 0.4047 -0.6179 -0.0266 0.1993 0.1035 -0.0194 + // The all-zero section has a max amplification of a single sample of: 1.6189 + // Impulse response: 0.4047 -0.8094 0.4047 0 0 0 + // The all-pole section has a max amplification of a single sample of: 1.9931 + // Impulse response: 1.0000 0.4734 -0.1189 -0.2187 -0.0627 0.04532 + + for (i = 0; i < data_length; i++) { + // All-zero section (filter coefficients in Q14). + tmp32 = kHpZeroCoefs[0] * *in_ptr; + tmp32 += kHpZeroCoefs[1] * filter_state[0]; + tmp32 += kHpZeroCoefs[2] * filter_state[1]; + filter_state[1] = filter_state[0]; + filter_state[0] = *in_ptr++; + + // All-pole section (filter coefficients in Q14). + tmp32 -= kHpPoleCoefs[1] * filter_state[2]; + tmp32 -= kHpPoleCoefs[2] * filter_state[3]; + filter_state[3] = filter_state[2]; + filter_state[2] = (int16_t)(tmp32 >> 14); + *out_ptr++ = filter_state[2]; + } +} + +// All pass filtering of `data_in`, used before splitting the signal into two +// frequency bands (low pass vs high pass). +// Note that `data_in` and `data_out` can NOT correspond to the same address. +// +// - data_in [i] : Input audio signal given in Q0. +// - data_length [i] : Length of input and output data. +// - filter_coefficient [i] : Given in Q15. +// - filter_state [i/o] : State of the filter given in Q(-1). +// - data_out [o] : Output audio signal given in Q(-1). +static void AllPassFilter(const int16_t* data_in, + size_t data_length, + int16_t filter_coefficient, + int16_t* filter_state, + int16_t* data_out) { + // The filter can only cause overflow (in the w16 output variable) + // if more than 4 consecutive input numbers are of maximum value and + // has the the same sign as the impulse responses first taps. + // First 6 taps of the impulse response: + // 0.6399 0.5905 -0.3779 0.2418 -0.1547 0.0990 + + size_t i; + int16_t tmp16 = 0; + int32_t tmp32 = 0; + int32_t state32 = ((int32_t)(*filter_state) * (1 << 16)); // Q15 + + for (i = 0; i < data_length; i++) { + tmp32 = state32 + filter_coefficient * *data_in; + tmp16 = (int16_t)(tmp32 >> 16); // Q(-1) + *data_out++ = tmp16; + state32 = (*data_in * (1 << 14)) - filter_coefficient * tmp16; // Q14 + state32 *= 2; // Q15. + data_in += 2; + } + + *filter_state = (int16_t)(state32 >> 16); // Q(-1) +} + +// Splits `data_in` into `hp_data_out` and `lp_data_out` corresponding to +// an upper (high pass) part and a lower (low pass) part respectively. +// +// - data_in [i] : Input audio data to be split into two frequency bands. +// - data_length [i] : Length of `data_in`. +// - upper_state [i/o] : State of the upper filter, given in Q(-1). +// - lower_state [i/o] : State of the lower filter, given in Q(-1). +// - hp_data_out [o] : Output audio data of the upper half of the spectrum. +// The length is `data_length` / 2. +// - lp_data_out [o] : Output audio data of the lower half of the spectrum. +// The length is `data_length` / 2. +static void SplitFilter(const int16_t* data_in, + size_t data_length, + int16_t* upper_state, + int16_t* lower_state, + int16_t* hp_data_out, + int16_t* lp_data_out) { + size_t i; + size_t half_length = data_length >> 1; // Downsampling by 2. + int16_t tmp_out; + + // All-pass filtering upper branch. + AllPassFilter(&data_in[0], half_length, kAllPassCoefsQ15[0], upper_state, + hp_data_out); + + // All-pass filtering lower branch. + AllPassFilter(&data_in[1], half_length, kAllPassCoefsQ15[1], lower_state, + lp_data_out); + + // Make LP and HP signals. + for (i = 0; i < half_length; i++) { + tmp_out = *hp_data_out; + *hp_data_out++ -= *lp_data_out; + *lp_data_out++ += tmp_out; + } +} + +// Calculates the energy of `data_in` in dB, and also updates an overall +// `total_energy` if necessary. +// +// - data_in [i] : Input audio data for energy calculation. +// - data_length [i] : Length of input data. +// - offset [i] : Offset value added to `log_energy`. +// - total_energy [i/o] : An external energy updated with the energy of +// `data_in`. +// NOTE: `total_energy` is only updated if +// `total_energy` <= `kMinEnergy`. +// - log_energy [o] : 10 * log10("energy of `data_in`") given in Q4. +static void LogOfEnergy(const int16_t* data_in, + size_t data_length, + int16_t offset, + int16_t* total_energy, + int16_t* log_energy) { + // `tot_rshifts` accumulates the number of right shifts performed on `energy`. + int tot_rshifts = 0; + // The `energy` will be normalized to 15 bits. We use unsigned integer because + // we eventually will mask out the fractional part. + uint32_t energy = 0; + + RTC_DCHECK(data_in); + RTC_DCHECK_GT(data_length, 0); + + energy = + (uint32_t)WebRtcSpl_Energy((int16_t*)data_in, data_length, &tot_rshifts); + + if (energy != 0) { + // By construction, normalizing to 15 bits is equivalent with 17 leading + // zeros of an unsigned 32 bit value. + int normalizing_rshifts = 17 - WebRtcSpl_NormU32(energy); + // In a 15 bit representation the leading bit is 2^14. log2(2^14) in Q10 is + // (14 << 10), which is what we initialize `log2_energy` with. For a more + // detailed derivations, see below. + int16_t log2_energy = kLogEnergyIntPart; + + tot_rshifts += normalizing_rshifts; + // Normalize `energy` to 15 bits. + // `tot_rshifts` is now the total number of right shifts performed on + // `energy` after normalization. This means that `energy` is in + // Q(-tot_rshifts). + if (normalizing_rshifts < 0) { + energy <<= -normalizing_rshifts; + } else { + energy >>= normalizing_rshifts; + } + + // Calculate the energy of `data_in` in dB, in Q4. + // + // 10 * log10("true energy") in Q4 = 2^4 * 10 * log10("true energy") = + // 160 * log10(`energy` * 2^`tot_rshifts`) = + // 160 * log10(2) * log2(`energy` * 2^`tot_rshifts`) = + // 160 * log10(2) * (log2(`energy`) + log2(2^`tot_rshifts`)) = + // (160 * log10(2)) * (log2(`energy`) + `tot_rshifts`) = + // `kLogConst` * (`log2_energy` + `tot_rshifts`) + // + // We know by construction that `energy` is normalized to 15 bits. Hence, + // `energy` = 2^14 + frac_Q15, where frac_Q15 is a fractional part in Q15. + // Further, we'd like `log2_energy` in Q10 + // log2(`energy`) in Q10 = 2^10 * log2(2^14 + frac_Q15) = + // 2^10 * log2(2^14 * (1 + frac_Q15 * 2^-14)) = + // 2^10 * (14 + log2(1 + frac_Q15 * 2^-14)) ~= + // (14 << 10) + 2^10 * (frac_Q15 * 2^-14) = + // (14 << 10) + (frac_Q15 * 2^-4) = (14 << 10) + (frac_Q15 >> 4) + // + // Note that frac_Q15 = (`energy` & 0x00003FFF) + + // Calculate and add the fractional part to `log2_energy`. + log2_energy += (int16_t)((energy & 0x00003FFF) >> 4); + + // `kLogConst` is in Q9, `log2_energy` in Q10 and `tot_rshifts` in Q0. + // Note that we in our derivation above have accounted for an output in Q4. + *log_energy = (int16_t)(((kLogConst * log2_energy) >> 19) + + ((tot_rshifts * kLogConst) >> 9)); + + if (*log_energy < 0) { + *log_energy = 0; + } + } else { + *log_energy = offset; + return; + } + + *log_energy += offset; + + // Update the approximate `total_energy` with the energy of `data_in`, if + // `total_energy` has not exceeded `kMinEnergy`. `total_energy` is used as an + // energy indicator in WebRtcVad_GmmProbability() in vad_core.c. + if (*total_energy <= kMinEnergy) { + if (tot_rshifts >= 0) { + // We know by construction that the `energy` > `kMinEnergy` in Q0, so add + // an arbitrary value such that `total_energy` exceeds `kMinEnergy`. + *total_energy += kMinEnergy + 1; + } else { + // By construction `energy` is represented by 15 bits, hence any number of + // right shifted `energy` will fit in an int16_t. In addition, adding the + // value to `total_energy` is wrap around safe as long as + // `kMinEnergy` < 8192. + *total_energy += (int16_t)(energy >> -tot_rshifts); // Q0. + } + } +} + +int16_t WebRtcVad_CalculateFeatures(VadInstT* self, + const int16_t* data_in, + size_t data_length, + int16_t* features) { + int16_t total_energy = 0; + // We expect `data_length` to be 80, 160 or 240 samples, which corresponds to + // 10, 20 or 30 ms in 8 kHz. Therefore, the intermediate downsampled data will + // have at most 120 samples after the first split and at most 60 samples after + // the second split. + int16_t hp_120[120], lp_120[120]; + int16_t hp_60[60], lp_60[60]; + const size_t half_data_length = data_length >> 1; + size_t length = half_data_length; // `data_length` / 2, corresponds to + // bandwidth = 2000 Hz after downsampling. + + // Initialize variables for the first SplitFilter(). + int frequency_band = 0; + const int16_t* in_ptr = data_in; // [0 - 4000] Hz. + int16_t* hp_out_ptr = hp_120; // [2000 - 4000] Hz. + int16_t* lp_out_ptr = lp_120; // [0 - 2000] Hz. + + RTC_DCHECK_LE(data_length, 240); + RTC_DCHECK_LT(4, kNumChannels - 1); // Checking maximum `frequency_band`. + + // Split at 2000 Hz and downsample. + SplitFilter(in_ptr, data_length, &self->upper_state[frequency_band], + &self->lower_state[frequency_band], hp_out_ptr, lp_out_ptr); + + // For the upper band (2000 Hz - 4000 Hz) split at 3000 Hz and downsample. + frequency_band = 1; + in_ptr = hp_120; // [2000 - 4000] Hz. + hp_out_ptr = hp_60; // [3000 - 4000] Hz. + lp_out_ptr = lp_60; // [2000 - 3000] Hz. + SplitFilter(in_ptr, length, &self->upper_state[frequency_band], + &self->lower_state[frequency_band], hp_out_ptr, lp_out_ptr); + + // Energy in 3000 Hz - 4000 Hz. + length >>= 1; // `data_length` / 4 <=> bandwidth = 1000 Hz. + + LogOfEnergy(hp_60, length, kOffsetVector[5], &total_energy, &features[5]); + + // Energy in 2000 Hz - 3000 Hz. + LogOfEnergy(lp_60, length, kOffsetVector[4], &total_energy, &features[4]); + + // For the lower band (0 Hz - 2000 Hz) split at 1000 Hz and downsample. + frequency_band = 2; + in_ptr = lp_120; // [0 - 2000] Hz. + hp_out_ptr = hp_60; // [1000 - 2000] Hz. + lp_out_ptr = lp_60; // [0 - 1000] Hz. + length = half_data_length; // `data_length` / 2 <=> bandwidth = 2000 Hz. + SplitFilter(in_ptr, length, &self->upper_state[frequency_band], + &self->lower_state[frequency_band], hp_out_ptr, lp_out_ptr); + + // Energy in 1000 Hz - 2000 Hz. + length >>= 1; // `data_length` / 4 <=> bandwidth = 1000 Hz. + LogOfEnergy(hp_60, length, kOffsetVector[3], &total_energy, &features[3]); + + // For the lower band (0 Hz - 1000 Hz) split at 500 Hz and downsample. + frequency_band = 3; + in_ptr = lp_60; // [0 - 1000] Hz. + hp_out_ptr = hp_120; // [500 - 1000] Hz. + lp_out_ptr = lp_120; // [0 - 500] Hz. + SplitFilter(in_ptr, length, &self->upper_state[frequency_band], + &self->lower_state[frequency_band], hp_out_ptr, lp_out_ptr); + + // Energy in 500 Hz - 1000 Hz. + length >>= 1; // `data_length` / 8 <=> bandwidth = 500 Hz. + LogOfEnergy(hp_120, length, kOffsetVector[2], &total_energy, &features[2]); + + // For the lower band (0 Hz - 500 Hz) split at 250 Hz and downsample. + frequency_band = 4; + in_ptr = lp_120; // [0 - 500] Hz. + hp_out_ptr = hp_60; // [250 - 500] Hz. + lp_out_ptr = lp_60; // [0 - 250] Hz. + SplitFilter(in_ptr, length, &self->upper_state[frequency_band], + &self->lower_state[frequency_band], hp_out_ptr, lp_out_ptr); + + // Energy in 250 Hz - 500 Hz. + length >>= 1; // `data_length` / 16 <=> bandwidth = 250 Hz. + LogOfEnergy(hp_60, length, kOffsetVector[1], &total_energy, &features[1]); + + // Remove 0 Hz - 80 Hz, by high pass filtering the lower band. + HighPassFilter(lp_60, length, self->hp_filter_state, hp_120); + + // Energy in 80 Hz - 250 Hz. + LogOfEnergy(hp_120, length, kOffsetVector[0], &total_energy, &features[0]); + + return total_energy; +} diff --git a/pkg/apm/webrtc/common_audio/vad/vad_filterbank.h b/pkg/apm/webrtc/common_audio/vad/vad_filterbank.h new file mode 100644 index 00000000..205eac83 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/vad_filterbank.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +/* + * This file includes feature calculating functionality used in vad_core.c. + */ + +#ifndef COMMON_AUDIO_VAD_VAD_FILTERBANK_H_ +#define COMMON_AUDIO_VAD_VAD_FILTERBANK_H_ + +#include "common_audio/vad/vad_core.h" + +// Takes `data_length` samples of `data_in` and calculates the logarithm of the +// energy of each of the `kNumChannels` = 6 frequency bands used by the VAD: +// 80 Hz - 250 Hz +// 250 Hz - 500 Hz +// 500 Hz - 1000 Hz +// 1000 Hz - 2000 Hz +// 2000 Hz - 3000 Hz +// 3000 Hz - 4000 Hz +// +// The values are given in Q4 and written to `features`. Further, an approximate +// overall energy is returned. The return value is used in +// WebRtcVad_GmmProbability() as a signal indicator, hence it is arbitrary above +// the threshold `kMinEnergy`. +// +// - self [i/o] : State information of the VAD. +// - data_in [i] : Input audio data, for feature extraction. +// - data_length [i] : Audio data size, in number of samples. +// - features [o] : 10 * log10(energy in each frequency band), Q4. +// - returns : Total energy of the signal (NOTE! This value is not +// exact. It is only used in a comparison.) +int16_t WebRtcVad_CalculateFeatures(VadInstT* self, + const int16_t* data_in, + size_t data_length, + int16_t* features); + +#endif // COMMON_AUDIO_VAD_VAD_FILTERBANK_H_ diff --git a/pkg/apm/webrtc/common_audio/vad/vad_gmm.c b/pkg/apm/webrtc/common_audio/vad/vad_gmm.c new file mode 100644 index 00000000..46d2de16 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/vad_gmm.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "common_audio/vad/vad_gmm.h" + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +static const int32_t kCompVar = 22005; +static const int16_t kLog2Exp = 5909; // log2(exp(1)) in Q12. + +// For a normal distribution, the probability of `input` is calculated and +// returned (in Q20). The formula for normal distributed probability is +// +// 1 / s * exp(-(x - m)^2 / (2 * s^2)) +// +// where the parameters are given in the following Q domains: +// m = `mean` (Q7) +// s = `std` (Q7) +// x = `input` (Q4) +// in addition to the probability we output `delta` (in Q11) used when updating +// the noise/speech model. +int32_t WebRtcVad_GaussianProbability(int16_t input, + int16_t mean, + int16_t std, + int16_t* delta) { + int16_t tmp16, inv_std, inv_std2, exp_value = 0; + int32_t tmp32; + + // Calculate `inv_std` = 1 / s, in Q10. + // 131072 = 1 in Q17, and (`std` >> 1) is for rounding instead of truncation. + // Q-domain: Q17 / Q7 = Q10. + tmp32 = (int32_t)131072 + (int32_t)(std >> 1); + inv_std = (int16_t)WebRtcSpl_DivW32W16(tmp32, std); + + // Calculate `inv_std2` = 1 / s^2, in Q14. + tmp16 = (inv_std >> 2); // Q10 -> Q8. + // Q-domain: (Q8 * Q8) >> 2 = Q14. + inv_std2 = (int16_t)((tmp16 * tmp16) >> 2); + // TODO(bjornv): Investigate if changing to + // inv_std2 = (int16_t)((inv_std * inv_std) >> 6); + // gives better accuracy. + + tmp16 = (input << 3); // Q4 -> Q7 + tmp16 = tmp16 - mean; // Q7 - Q7 = Q7 + + // To be used later, when updating noise/speech model. + // `delta` = (x - m) / s^2, in Q11. + // Q-domain: (Q14 * Q7) >> 10 = Q11. + *delta = (int16_t)((inv_std2 * tmp16) >> 10); + + // Calculate the exponent `tmp32` = (x - m)^2 / (2 * s^2), in Q10. Replacing + // division by two with one shift. + // Q-domain: (Q11 * Q7) >> 8 = Q10. + tmp32 = (*delta * tmp16) >> 9; + + // If the exponent is small enough to give a non-zero probability we calculate + // `exp_value` ~= exp(-(x - m)^2 / (2 * s^2)) + // ~= exp2(-log2(exp(1)) * `tmp32`). + if (tmp32 < kCompVar) { + // Calculate `tmp16` = log2(exp(1)) * `tmp32`, in Q10. + // Q-domain: (Q12 * Q10) >> 12 = Q10. + tmp16 = (int16_t)((kLog2Exp * tmp32) >> 12); + tmp16 = -tmp16; + exp_value = (0x0400 | (tmp16 & 0x03FF)); + tmp16 ^= 0xFFFF; + tmp16 >>= 10; + tmp16 += 1; + // Get `exp_value` = exp(-`tmp32`) in Q10. + exp_value >>= tmp16; + } + + // Calculate and return (1 / s) * exp(-(x - m)^2 / (2 * s^2)), in Q20. + // Q-domain: Q10 * Q10 = Q20. + return inv_std * exp_value; +} diff --git a/pkg/apm/webrtc/common_audio/vad/vad_gmm.h b/pkg/apm/webrtc/common_audio/vad/vad_gmm.h new file mode 100644 index 00000000..ada51897 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/vad_gmm.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Gaussian probability calculations internally used in vad_core.c. + +#ifndef COMMON_AUDIO_VAD_VAD_GMM_H_ +#define COMMON_AUDIO_VAD_VAD_GMM_H_ + +#include + +// Calculates the probability for `input`, given that `input` comes from a +// normal distribution with mean and standard deviation (`mean`, `std`). +// +// Inputs: +// - input : input sample in Q4. +// - mean : mean input in the statistical model, Q7. +// - std : standard deviation, Q7. +// +// Output: +// +// - delta : input used when updating the model, Q11. +// `delta` = (`input` - `mean`) / `std`^2. +// +// Return: +// (probability for `input`) = +// 1 / `std` * exp(-(`input` - `mean`)^2 / (2 * `std`^2)); +int32_t WebRtcVad_GaussianProbability(int16_t input, + int16_t mean, + int16_t std, + int16_t* delta); + +#endif // COMMON_AUDIO_VAD_VAD_GMM_H_ diff --git a/pkg/apm/webrtc/common_audio/vad/vad_sp.c b/pkg/apm/webrtc/common_audio/vad/vad_sp.c new file mode 100644 index 00000000..b745465a --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/vad_sp.c @@ -0,0 +1,176 @@ +/* + * 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 "common_audio/vad/vad_sp.h" + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/vad/vad_core.h" +#include "rtc_base/checks.h" + +// Allpass filter coefficients, upper and lower, in Q13. +// Upper: 0.64, Lower: 0.17. +static const int16_t kAllPassCoefsQ13[2] = {5243, 1392}; // Q13. +static const int16_t kSmoothingDown = 6553; // 0.2 in Q15. +static const int16_t kSmoothingUp = 32439; // 0.99 in Q15. + +// TODO(bjornv): Move this function to vad_filterbank.c. +// Downsampling filter based on splitting filter and allpass functions. +void WebRtcVad_Downsampling(const int16_t* signal_in, + int16_t* signal_out, + int32_t* filter_state, + size_t in_length) { + int16_t tmp16_1 = 0, tmp16_2 = 0; + int32_t tmp32_1 = filter_state[0]; + int32_t tmp32_2 = filter_state[1]; + size_t n = 0; + // Downsampling by 2 gives half length. + size_t half_length = (in_length >> 1); + + // Filter coefficients in Q13, filter state in Q0. + for (n = 0; n < half_length; n++) { + // All-pass filtering upper branch. + tmp16_1 = + (int16_t)((tmp32_1 >> 1) + ((kAllPassCoefsQ13[0] * *signal_in) >> 14)); + *signal_out = tmp16_1; + tmp32_1 = (int32_t)(*signal_in++) - ((kAllPassCoefsQ13[0] * tmp16_1) >> 12); + + // All-pass filtering lower branch. + tmp16_2 = + (int16_t)((tmp32_2 >> 1) + ((kAllPassCoefsQ13[1] * *signal_in) >> 14)); + *signal_out++ += tmp16_2; + tmp32_2 = (int32_t)(*signal_in++) - ((kAllPassCoefsQ13[1] * tmp16_2) >> 12); + } + // Store the filter states. + filter_state[0] = tmp32_1; + filter_state[1] = tmp32_2; +} + +// Inserts `feature_value` into `low_value_vector`, if it is one of the 16 +// smallest values the last 100 frames. Then calculates and returns the median +// of the five smallest values. +int16_t WebRtcVad_FindMinimum(VadInstT* self, + int16_t feature_value, + int channel) { + int i = 0, j = 0; + int position = -1; + // Offset to beginning of the 16 minimum values in memory. + const int offset = (channel << 4); + int16_t current_median = 1600; + int16_t alpha = 0; + int32_t tmp32 = 0; + // Pointer to memory for the 16 minimum values and the age of each value of + // the `channel`. + int16_t* age = &self->index_vector[offset]; + int16_t* smallest_values = &self->low_value_vector[offset]; + + RTC_DCHECK_LT(channel, kNumChannels); + + // Each value in `smallest_values` is getting 1 loop older. Update `age`, and + // remove old values. + for (i = 0; i < 16; i++) { + if (age[i] != 100) { + age[i]++; + } else { + // Too old value. Remove from memory and shift larger values downwards. + for (j = i; j < 15; j++) { + smallest_values[j] = smallest_values[j + 1]; + age[j] = age[j + 1]; + } + age[15] = 101; + smallest_values[15] = 10000; + } + } + + // Check if `feature_value` is smaller than any of the values in + // `smallest_values`. If so, find the `position` where to insert the new value + // (`feature_value`). + if (feature_value < smallest_values[7]) { + if (feature_value < smallest_values[3]) { + if (feature_value < smallest_values[1]) { + if (feature_value < smallest_values[0]) { + position = 0; + } else { + position = 1; + } + } else if (feature_value < smallest_values[2]) { + position = 2; + } else { + position = 3; + } + } else if (feature_value < smallest_values[5]) { + if (feature_value < smallest_values[4]) { + position = 4; + } else { + position = 5; + } + } else if (feature_value < smallest_values[6]) { + position = 6; + } else { + position = 7; + } + } else if (feature_value < smallest_values[15]) { + if (feature_value < smallest_values[11]) { + if (feature_value < smallest_values[9]) { + if (feature_value < smallest_values[8]) { + position = 8; + } else { + position = 9; + } + } else if (feature_value < smallest_values[10]) { + position = 10; + } else { + position = 11; + } + } else if (feature_value < smallest_values[13]) { + if (feature_value < smallest_values[12]) { + position = 12; + } else { + position = 13; + } + } else if (feature_value < smallest_values[14]) { + position = 14; + } else { + position = 15; + } + } + + // If we have detected a new small value, insert it at the correct position + // and shift larger values up. + if (position > -1) { + for (i = 15; i > position; i--) { + smallest_values[i] = smallest_values[i - 1]; + age[i] = age[i - 1]; + } + smallest_values[position] = feature_value; + age[position] = 1; + } + + // Get `current_median`. + if (self->frame_counter > 2) { + current_median = smallest_values[2]; + } else if (self->frame_counter > 0) { + current_median = smallest_values[0]; + } + + // Smooth the median value. + if (self->frame_counter > 0) { + if (current_median < self->mean_value[channel]) { + alpha = kSmoothingDown; // 0.2 in Q15. + } else { + alpha = kSmoothingUp; // 0.99 in Q15. + } + } + tmp32 = (alpha + 1) * self->mean_value[channel]; + tmp32 += (WEBRTC_SPL_WORD16_MAX - alpha) * current_median; + tmp32 += 16384; + self->mean_value[channel] = (int16_t)(tmp32 >> 15); + + return self->mean_value[channel]; +} diff --git a/pkg/apm/webrtc/common_audio/vad/vad_sp.h b/pkg/apm/webrtc/common_audio/vad/vad_sp.h new file mode 100644 index 00000000..89138c57 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/vad_sp.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file includes specific signal processing tools used in vad_core.c. + +#ifndef COMMON_AUDIO_VAD_VAD_SP_H_ +#define COMMON_AUDIO_VAD_VAD_SP_H_ + +#include "common_audio/vad/vad_core.h" + +// Downsamples the signal by a factor 2, eg. 32->16 or 16->8. +// +// Inputs: +// - signal_in : Input signal. +// - in_length : Length of input signal in samples. +// +// Input & Output: +// - filter_state : Current filter states of the two all-pass filters. The +// `filter_state` is updated after all samples have been +// processed. +// +// Output: +// - signal_out : Downsampled signal (of length `in_length` / 2). +void WebRtcVad_Downsampling(const int16_t* signal_in, + int16_t* signal_out, + int32_t* filter_state, + size_t in_length); + +// Updates and returns the smoothed feature minimum. As minimum we use the +// median of the five smallest feature values in a 100 frames long window. +// As long as `handle->frame_counter` is zero, that is, we haven't received any +// "valid" data, FindMinimum() outputs the default value of 1600. +// +// Inputs: +// - feature_value : New feature value to update with. +// - channel : Channel number. +// +// Input & Output: +// - handle : State information of the VAD. +// +// Returns: +// : Smoothed minimum value for a moving window. +int16_t WebRtcVad_FindMinimum(VadInstT* handle, + int16_t feature_value, + int channel); + +#endif // COMMON_AUDIO_VAD_VAD_SP_H_ diff --git a/pkg/apm/webrtc/common_audio/vad/webrtc_vad.c b/pkg/apm/webrtc/common_audio/vad/webrtc_vad.c new file mode 100644 index 00000000..d3c8b08c --- /dev/null +++ b/pkg/apm/webrtc/common_audio/vad/webrtc_vad.c @@ -0,0 +1,116 @@ +/* + * 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 "common_audio/vad/include/webrtc_vad.h" + +#include +#include + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/vad/vad_core.h" + +static const int kInitCheck = 42; +static const int kValidRates[] = {8000, 16000, 32000, 48000}; +static const size_t kRatesSize = sizeof(kValidRates) / sizeof(*kValidRates); +static const int kMaxFrameLengthMs = 30; + +VadInst* WebRtcVad_Create(void) { + VadInstT* self = (VadInstT*)malloc(sizeof(VadInstT)); + + self->init_flag = 0; + + return (VadInst*)self; +} + +void WebRtcVad_Free(VadInst* handle) { + free(handle); +} + +// TODO(bjornv): Move WebRtcVad_InitCore() code here. +int WebRtcVad_Init(VadInst* handle) { + // Initialize the core VAD component. + return WebRtcVad_InitCore((VadInstT*)handle); +} + +// TODO(bjornv): Move WebRtcVad_set_mode_core() code here. +int WebRtcVad_set_mode(VadInst* handle, int mode) { + VadInstT* self = (VadInstT*)handle; + + if (handle == NULL) { + return -1; + } + if (self->init_flag != kInitCheck) { + return -1; + } + + return WebRtcVad_set_mode_core(self, mode); +} + +int WebRtcVad_Process(VadInst* handle, + int fs, + const int16_t* audio_frame, + size_t frame_length) { + int vad = -1; + VadInstT* self = (VadInstT*)handle; + + if (handle == NULL) { + return -1; + } + + if (self->init_flag != kInitCheck) { + return -1; + } + if (audio_frame == NULL) { + return -1; + } + if (WebRtcVad_ValidRateAndFrameLength(fs, frame_length) != 0) { + return -1; + } + + if (fs == 48000) { + vad = WebRtcVad_CalcVad48khz(self, audio_frame, frame_length); + } else if (fs == 32000) { + vad = WebRtcVad_CalcVad32khz(self, audio_frame, frame_length); + } else if (fs == 16000) { + vad = WebRtcVad_CalcVad16khz(self, audio_frame, frame_length); + } else if (fs == 8000) { + vad = WebRtcVad_CalcVad8khz(self, audio_frame, frame_length); + } + + if (vad > 0) { + vad = 1; + } + return vad; +} + +int WebRtcVad_ValidRateAndFrameLength(int rate, size_t frame_length) { + int return_value = -1; + size_t i; + int valid_length_ms; + size_t valid_length; + + // We only allow 10, 20 or 30 ms frames. Loop through valid frame rates and + // see if we have a matching pair. + for (i = 0; i < kRatesSize; i++) { + if (kValidRates[i] == rate) { + for (valid_length_ms = 10; valid_length_ms <= kMaxFrameLengthMs; + valid_length_ms += 10) { + valid_length = (size_t)(kValidRates[i] / 1000 * valid_length_ms); + if (frame_length == valid_length) { + return_value = 0; + break; + } + } + break; + } + } + + return return_value; +} diff --git a/pkg/apm/webrtc/common_audio/wav_file.cc b/pkg/apm/webrtc/common_audio/wav_file.cc new file mode 100644 index 00000000..127c9c07 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/wav_file.cc @@ -0,0 +1,290 @@ +/* + * 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/wav_file.h" + +#include + +#include +#include +#include +#include +#include + +#include "common_audio/include/audio_util.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace { + +static_assert(std::is_trivially_destructible::value, ""); + +// Checks whether the format is supported or not. +bool FormatSupported(WavFormat format) { + // Only PCM and IEEE Float formats are supported. + return format == WavFormat::kWavFormatPcm || + format == WavFormat::kWavFormatIeeeFloat; +} + +// Doesn't take ownership of the file handle and won't close it. +class WavHeaderFileReader : public WavHeaderReader { + public: + explicit WavHeaderFileReader(FileWrapper* file) : file_(file) {} + + WavHeaderFileReader(const WavHeaderFileReader&) = delete; + WavHeaderFileReader& operator=(const WavHeaderFileReader&) = delete; + + size_t Read(void* buf, size_t num_bytes) override { + size_t count = file_->Read(buf, num_bytes); + pos_ += count; + return count; + } + bool SeekForward(uint32_t num_bytes) override { + bool success = file_->SeekRelative(num_bytes); + if (success) { + pos_ += num_bytes; + } + return success; + } + int64_t GetPosition() override { return pos_; } + + private: + FileWrapper* file_; + int64_t pos_ = 0; +}; + +constexpr size_t kMaxChunksize = 4096; + +} // namespace + +WavReader::WavReader(absl::string_view filename) + : WavReader(FileWrapper::OpenReadOnly(filename)) {} + +WavReader::WavReader(FileWrapper file) : file_(std::move(file)) { + RTC_CHECK(file_.is_open()) + << "Invalid file. Could not create file handle for wav file."; + + WavHeaderFileReader readable(&file_); + size_t bytes_per_sample; + RTC_CHECK(ReadWavHeader(&readable, &num_channels_, &sample_rate_, &format_, + &bytes_per_sample, &num_samples_in_file_, + &data_start_pos_)); + num_unread_samples_ = num_samples_in_file_; + RTC_CHECK(FormatSupported(format_)) << "Non-implemented wav-format"; +} + +void WavReader::Reset() { + RTC_CHECK(file_.SeekTo(data_start_pos_)) + << "Failed to set position in the file to WAV data start position"; + num_unread_samples_ = num_samples_in_file_; +} + +size_t WavReader::ReadSamples(const size_t num_samples, + int16_t* const samples) { +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Need to convert samples to big-endian when reading from WAV file" +#endif + + size_t num_samples_left_to_read = num_samples; + size_t next_chunk_start = 0; + while (num_samples_left_to_read > 0 && num_unread_samples_ > 0) { + const size_t chunk_size = std::min( + std::min(kMaxChunksize, num_samples_left_to_read), num_unread_samples_); + size_t num_bytes_read; + size_t num_samples_read; + if (format_ == WavFormat::kWavFormatIeeeFloat) { + std::array samples_to_convert; + num_bytes_read = file_.Read(samples_to_convert.data(), + chunk_size * sizeof(samples_to_convert[0])); + num_samples_read = num_bytes_read / sizeof(samples_to_convert[0]); + + for (size_t j = 0; j < num_samples_read; ++j) { + samples[next_chunk_start + j] = FloatToS16(samples_to_convert[j]); + } + } else { + RTC_CHECK_EQ(format_, WavFormat::kWavFormatPcm); + num_bytes_read = file_.Read(&samples[next_chunk_start], + chunk_size * sizeof(samples[0])); + num_samples_read = num_bytes_read / sizeof(samples[0]); + } + RTC_CHECK(num_samples_read == 0 || (num_bytes_read % num_samples_read) == 0) + << "Corrupt file: file ended in the middle of a sample."; + RTC_CHECK(num_samples_read == chunk_size || file_.ReadEof()) + << "Corrupt file: payload size does not match header."; + + next_chunk_start += num_samples_read; + num_unread_samples_ -= num_samples_read; + num_samples_left_to_read -= num_samples_read; + } + + return num_samples - num_samples_left_to_read; +} + +size_t WavReader::ReadSamples(const size_t num_samples, float* const samples) { +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Need to convert samples to big-endian when reading from WAV file" +#endif + + size_t num_samples_left_to_read = num_samples; + size_t next_chunk_start = 0; + while (num_samples_left_to_read > 0 && num_unread_samples_ > 0) { + const size_t chunk_size = std::min( + std::min(kMaxChunksize, num_samples_left_to_read), num_unread_samples_); + size_t num_bytes_read; + size_t num_samples_read; + if (format_ == WavFormat::kWavFormatPcm) { + std::array samples_to_convert; + num_bytes_read = file_.Read(samples_to_convert.data(), + chunk_size * sizeof(samples_to_convert[0])); + num_samples_read = num_bytes_read / sizeof(samples_to_convert[0]); + + for (size_t j = 0; j < num_samples_read; ++j) { + samples[next_chunk_start + j] = + static_cast(samples_to_convert[j]); + } + } else { + RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat); + num_bytes_read = file_.Read(&samples[next_chunk_start], + chunk_size * sizeof(samples[0])); + num_samples_read = num_bytes_read / sizeof(samples[0]); + + for (size_t j = 0; j < num_samples_read; ++j) { + samples[next_chunk_start + j] = + FloatToFloatS16(samples[next_chunk_start + j]); + } + } + RTC_CHECK(num_samples_read == 0 || (num_bytes_read % num_samples_read) == 0) + << "Corrupt file: file ended in the middle of a sample."; + RTC_CHECK(num_samples_read == chunk_size || file_.ReadEof()) + << "Corrupt file: payload size does not match header."; + + next_chunk_start += num_samples_read; + num_unread_samples_ -= num_samples_read; + num_samples_left_to_read -= num_samples_read; + } + + return num_samples - num_samples_left_to_read; +} + +void WavReader::Close() { + file_.Close(); +} + +WavWriter::WavWriter(absl::string_view filename, + int sample_rate, + size_t num_channels, + SampleFormat sample_format) + // Unlike plain fopen, OpenWriteOnly takes care of filename utf8 -> + // wchar conversion on windows. + : WavWriter(FileWrapper::OpenWriteOnly(filename), + sample_rate, + num_channels, + sample_format) {} + +WavWriter::WavWriter(FileWrapper file, + int sample_rate, + size_t num_channels, + SampleFormat sample_format) + : sample_rate_(sample_rate), + num_channels_(num_channels), + num_samples_written_(0), + format_(sample_format == SampleFormat::kInt16 + ? WavFormat::kWavFormatPcm + : WavFormat::kWavFormatIeeeFloat), + file_(std::move(file)) { + // Handle errors from the OpenWriteOnly call in above constructor. + RTC_CHECK(file_.is_open()) << "Invalid file. Could not create wav file."; + + RTC_CHECK(CheckWavParameters(num_channels_, sample_rate_, format_, + num_samples_written_)); + + // Write a blank placeholder header, since we need to know the total number + // of samples before we can fill in the real data. + static const uint8_t blank_header[MaxWavHeaderSize()] = {0}; + RTC_CHECK(file_.Write(blank_header, WavHeaderSize(format_))); +} + +void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) { +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Need to convert samples to little-endian when writing to WAV file" +#endif + + for (size_t i = 0; i < num_samples; i += kMaxChunksize) { + const size_t num_remaining_samples = num_samples - i; + const size_t num_samples_to_write = + std::min(kMaxChunksize, num_remaining_samples); + + if (format_ == WavFormat::kWavFormatPcm) { + RTC_CHECK( + file_.Write(&samples[i], num_samples_to_write * sizeof(samples[0]))); + } else { + RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat); + std::array converted_samples; + for (size_t j = 0; j < num_samples_to_write; ++j) { + converted_samples[j] = S16ToFloat(samples[i + j]); + } + RTC_CHECK( + file_.Write(converted_samples.data(), + num_samples_to_write * sizeof(converted_samples[0]))); + } + + num_samples_written_ += num_samples_to_write; + RTC_CHECK_GE(num_samples_written_, + num_samples_to_write); // detect size_t overflow + } +} + +void WavWriter::WriteSamples(const float* samples, size_t num_samples) { +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Need to convert samples to little-endian when writing to WAV file" +#endif + + for (size_t i = 0; i < num_samples; i += kMaxChunksize) { + const size_t num_remaining_samples = num_samples - i; + const size_t num_samples_to_write = + std::min(kMaxChunksize, num_remaining_samples); + + if (format_ == WavFormat::kWavFormatPcm) { + std::array converted_samples; + for (size_t j = 0; j < num_samples_to_write; ++j) { + converted_samples[j] = FloatS16ToS16(samples[i + j]); + } + RTC_CHECK( + file_.Write(converted_samples.data(), + num_samples_to_write * sizeof(converted_samples[0]))); + } else { + RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat); + std::array converted_samples; + for (size_t j = 0; j < num_samples_to_write; ++j) { + converted_samples[j] = FloatS16ToFloat(samples[i + j]); + } + RTC_CHECK( + file_.Write(converted_samples.data(), + num_samples_to_write * sizeof(converted_samples[0]))); + } + + num_samples_written_ += num_samples_to_write; + RTC_CHECK(num_samples_written_ >= + num_samples_to_write); // detect size_t overflow + } +} + +void WavWriter::Close() { + RTC_CHECK(file_.Rewind()); + std::array header; + size_t header_size; + WriteWavHeader(num_channels_, sample_rate_, format_, num_samples_written_, + header.data(), &header_size); + RTC_CHECK(file_.Write(header.data(), header_size)); + RTC_CHECK(file_.Close()); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/wav_file.h b/pkg/apm/webrtc/common_audio/wav_file.h new file mode 100644 index 00000000..72a4db79 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/wav_file.h @@ -0,0 +1,115 @@ +/* + * 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_WAV_FILE_H_ +#define COMMON_AUDIO_WAV_FILE_H_ + +#include + +#include +#include + +#include "common_audio/wav_header.h" +#include "rtc_base/system/file_wrapper.h" + +namespace webrtc { + +// Interface to provide access WAV file parameters. +class WavFile { + public: + enum class SampleFormat { kInt16, kFloat }; + + virtual ~WavFile() {} + + virtual int sample_rate() const = 0; + virtual size_t num_channels() const = 0; + virtual size_t num_samples() const = 0; +}; + +// Simple C++ class for writing 16-bit integer and 32 bit floating point PCM WAV +// files. All error handling is by calls to RTC_CHECK(), making it unsuitable +// for anything but debug code. +class WavWriter final : public WavFile { + public: + // Opens a new WAV file for writing. + WavWriter(absl::string_view filename, + int sample_rate, + size_t num_channels, + SampleFormat sample_format = SampleFormat::kInt16); + WavWriter(FileWrapper file, + int sample_rate, + size_t num_channels, + SampleFormat sample_format = SampleFormat::kInt16); + + // Closes the WAV file, after writing its header. + ~WavWriter() { Close(); } + + WavWriter(const WavWriter&) = delete; + WavWriter& operator=(const WavWriter&) = delete; + + // Write additional samples to the file. Each sample is in the range + // [-32768.0,32767.0], and there must be the previously specified number of + // interleaved channels. + void WriteSamples(const float* samples, size_t num_samples); + void WriteSamples(const int16_t* samples, size_t num_samples); + + int sample_rate() const override { return sample_rate_; } + size_t num_channels() const override { return num_channels_; } + size_t num_samples() const override { return num_samples_written_; } + + private: + void Close(); + const int sample_rate_; + const size_t num_channels_; + size_t num_samples_written_; + WavFormat format_; + FileWrapper file_; +}; + +// Follows the conventions of WavWriter. +class WavReader final : public WavFile { + public: + // Opens an existing WAV file for reading. + explicit WavReader(absl::string_view filename); + explicit WavReader(FileWrapper file); + + // Close the WAV file. + ~WavReader() { Close(); } + + WavReader(const WavReader&) = delete; + WavReader& operator=(const WavReader&) = delete; + + // Resets position to the beginning of the file. + void Reset(); + + // Returns the number of samples read. If this is less than requested, + // verifies that the end of the file was reached. + size_t ReadSamples(size_t num_samples, float* samples); + size_t ReadSamples(size_t num_samples, int16_t* samples); + + int sample_rate() const override { return sample_rate_; } + size_t num_channels() const override { return num_channels_; } + size_t num_samples() const override { return num_samples_in_file_; } + + private: + void Close(); + int sample_rate_; + size_t num_channels_; + WavFormat format_; + size_t num_samples_in_file_; + size_t num_unread_samples_; + FileWrapper file_; + int64_t + data_start_pos_; // Position in the file immediately after WAV header. +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_WAV_FILE_H_ diff --git a/pkg/apm/webrtc/common_audio/wav_header.cc b/pkg/apm/webrtc/common_audio/wav_header.cc new file mode 100644 index 00000000..467dcc78 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/wav_header.cc @@ -0,0 +1,435 @@ +/* + * 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. + */ + +// Based on the WAV file format documentation at +// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ and +// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html + +#include "common_audio/wav_header.h" + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/sanitizer.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace { + +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Code not working properly for big endian platforms." +#endif + +#pragma pack(2) +struct ChunkHeader { + uint32_t ID; + uint32_t Size; +}; +static_assert(sizeof(ChunkHeader) == 8, "ChunkHeader size"); + +#pragma pack(2) +struct RiffHeader { + ChunkHeader header; + uint32_t Format; +}; +static_assert(sizeof(RiffHeader) == sizeof(ChunkHeader) + 4, "RiffHeader size"); + +// We can't nest this definition in WavHeader, because VS2013 gives an error +// on sizeof(WavHeader::fmt): "error C2070: 'unknown': illegal sizeof operand". +#pragma pack(2) +struct FmtPcmSubchunk { + ChunkHeader header; + uint16_t AudioFormat; + uint16_t NumChannels; + uint32_t SampleRate; + uint32_t ByteRate; + uint16_t BlockAlign; + uint16_t BitsPerSample; +}; +static_assert(sizeof(FmtPcmSubchunk) == 24, "FmtPcmSubchunk size"); +const uint32_t kFmtPcmSubchunkSize = + sizeof(FmtPcmSubchunk) - sizeof(ChunkHeader); + +// Pack struct to avoid additional padding bytes. +#pragma pack(2) +struct FmtIeeeFloatSubchunk { + ChunkHeader header; + uint16_t AudioFormat; + uint16_t NumChannels; + uint32_t SampleRate; + uint32_t ByteRate; + uint16_t BlockAlign; + uint16_t BitsPerSample; + uint16_t ExtensionSize; +}; +static_assert(sizeof(FmtIeeeFloatSubchunk) == 26, "FmtIeeeFloatSubchunk size"); +const uint32_t kFmtIeeeFloatSubchunkSize = + sizeof(FmtIeeeFloatSubchunk) - sizeof(ChunkHeader); + +// Simple PCM wav header. It does not include chunks that are not essential to +// read audio samples. +#pragma pack(2) +struct WavHeaderPcm { + RiffHeader riff; + FmtPcmSubchunk fmt; + struct { + ChunkHeader header; + } data; +}; +static_assert(sizeof(WavHeaderPcm) == kPcmWavHeaderSize, + "no padding in header"); + +// IEEE Float Wav header, includes extra chunks necessary for proper non-PCM +// WAV implementation. +#pragma pack(2) +struct WavHeaderIeeeFloat { + RiffHeader riff; + FmtIeeeFloatSubchunk fmt; + struct { + ChunkHeader header; + uint32_t SampleLength; + } fact; + struct { + ChunkHeader header; + } data; +}; +static_assert(sizeof(WavHeaderIeeeFloat) == kIeeeFloatWavHeaderSize, + "no padding in header"); + +uint32_t PackFourCC(char a, char b, char c, char d) { + uint32_t packed_value = + static_cast(a) | static_cast(b) << 8 | + static_cast(c) << 16 | static_cast(d) << 24; + return packed_value; +} + +std::string ReadFourCC(uint32_t x) { + return std::string(reinterpret_cast(&x), 4); +} + +uint16_t MapWavFormatToHeaderField(WavFormat format) { + switch (format) { + case WavFormat::kWavFormatPcm: + return 1; + case WavFormat::kWavFormatIeeeFloat: + return 3; + case WavFormat::kWavFormatALaw: + return 6; + case WavFormat::kWavFormatMuLaw: + return 7; + } + RTC_CHECK_NOTREACHED(); +} + +WavFormat MapHeaderFieldToWavFormat(uint16_t format_header_value) { + if (format_header_value == 1) { + return WavFormat::kWavFormatPcm; + } + if (format_header_value == 3) { + return WavFormat::kWavFormatIeeeFloat; + } + + RTC_CHECK(false) << "Unsupported WAV format"; +} + +uint32_t RiffChunkSize(size_t bytes_in_payload, size_t header_size) { + return static_cast(bytes_in_payload + header_size - + sizeof(ChunkHeader)); +} + +uint32_t ByteRate(size_t num_channels, + int sample_rate, + size_t bytes_per_sample) { + return static_cast(num_channels * sample_rate * bytes_per_sample); +} + +uint16_t BlockAlign(size_t num_channels, size_t bytes_per_sample) { + return static_cast(num_channels * bytes_per_sample); +} + +// Finds a chunk having the sought ID. If found, then `readable` points to the +// first byte of the sought chunk data. If not found, the end of the file is +// reached. +bool FindWaveChunk(ChunkHeader* chunk_header, + WavHeaderReader* readable, + const std::string sought_chunk_id) { + RTC_DCHECK_EQ(sought_chunk_id.size(), 4); + while (true) { + if (readable->Read(chunk_header, sizeof(*chunk_header)) != + sizeof(*chunk_header)) + return false; // EOF. + if (ReadFourCC(chunk_header->ID) == sought_chunk_id) + return true; // Sought chunk found. + // Ignore current chunk by skipping its payload. + if (!readable->SeekForward(chunk_header->Size)) + return false; // EOF or error. + } +} + +bool ReadFmtChunkData(FmtPcmSubchunk* fmt_subchunk, WavHeaderReader* readable) { + // Reads "fmt " chunk payload. + if (readable->Read(&(fmt_subchunk->AudioFormat), kFmtPcmSubchunkSize) != + kFmtPcmSubchunkSize) + return false; + const uint32_t fmt_size = fmt_subchunk->header.Size; + if (fmt_size != kFmtPcmSubchunkSize) { + // There is an optional two-byte extension field permitted to be present + // with PCM, but which must be zero. + int16_t ext_size; + if (kFmtPcmSubchunkSize + sizeof(ext_size) != fmt_size) + return false; + if (readable->Read(&ext_size, sizeof(ext_size)) != sizeof(ext_size)) + return false; + if (ext_size != 0) + return false; + } + return true; +} + +void WritePcmWavHeader(size_t num_channels, + int sample_rate, + size_t bytes_per_sample, + size_t num_samples, + uint8_t* buf, + size_t* header_size) { + RTC_CHECK(buf); + RTC_CHECK(header_size); + *header_size = kPcmWavHeaderSize; + auto header = MsanUninitialized({}); + const size_t bytes_in_payload = bytes_per_sample * num_samples; + + header.riff.header.ID = PackFourCC('R', 'I', 'F', 'F'); + header.riff.header.Size = RiffChunkSize(bytes_in_payload, *header_size); + header.riff.Format = PackFourCC('W', 'A', 'V', 'E'); + header.fmt.header.ID = PackFourCC('f', 'm', 't', ' '); + header.fmt.header.Size = kFmtPcmSubchunkSize; + header.fmt.AudioFormat = MapWavFormatToHeaderField(WavFormat::kWavFormatPcm); + header.fmt.NumChannels = static_cast(num_channels); + header.fmt.SampleRate = sample_rate; + header.fmt.ByteRate = ByteRate(num_channels, sample_rate, bytes_per_sample); + header.fmt.BlockAlign = BlockAlign(num_channels, bytes_per_sample); + header.fmt.BitsPerSample = static_cast(8 * bytes_per_sample); + header.data.header.ID = PackFourCC('d', 'a', 't', 'a'); + header.data.header.Size = static_cast(bytes_in_payload); + + // Do an extra copy rather than writing everything to buf directly, since buf + // might not be correctly aligned. + memcpy(buf, &header, *header_size); +} + +void WriteIeeeFloatWavHeader(size_t num_channels, + int sample_rate, + size_t bytes_per_sample, + size_t num_samples, + uint8_t* buf, + size_t* header_size) { + RTC_CHECK(buf); + RTC_CHECK(header_size); + *header_size = kIeeeFloatWavHeaderSize; + auto header = MsanUninitialized({}); + const size_t bytes_in_payload = bytes_per_sample * num_samples; + + header.riff.header.ID = PackFourCC('R', 'I', 'F', 'F'); + header.riff.header.Size = RiffChunkSize(bytes_in_payload, *header_size); + header.riff.Format = PackFourCC('W', 'A', 'V', 'E'); + header.fmt.header.ID = PackFourCC('f', 'm', 't', ' '); + header.fmt.header.Size = kFmtIeeeFloatSubchunkSize; + header.fmt.AudioFormat = + MapWavFormatToHeaderField(WavFormat::kWavFormatIeeeFloat); + header.fmt.NumChannels = static_cast(num_channels); + header.fmt.SampleRate = sample_rate; + header.fmt.ByteRate = ByteRate(num_channels, sample_rate, bytes_per_sample); + header.fmt.BlockAlign = BlockAlign(num_channels, bytes_per_sample); + header.fmt.BitsPerSample = static_cast(8 * bytes_per_sample); + header.fmt.ExtensionSize = 0; + header.fact.header.ID = PackFourCC('f', 'a', 'c', 't'); + header.fact.header.Size = 4; + header.fact.SampleLength = static_cast(num_channels * num_samples); + header.data.header.ID = PackFourCC('d', 'a', 't', 'a'); + header.data.header.Size = static_cast(bytes_in_payload); + + // Do an extra copy rather than writing everything to buf directly, since buf + // might not be correctly aligned. + memcpy(buf, &header, *header_size); +} + +// Returns the number of bytes per sample for the format. +size_t GetFormatBytesPerSample(WavFormat format) { + switch (format) { + case WavFormat::kWavFormatPcm: + // Other values may be OK, but for now we're conservative. + return 2; + case WavFormat::kWavFormatALaw: + case WavFormat::kWavFormatMuLaw: + return 1; + case WavFormat::kWavFormatIeeeFloat: + return 4; + } + RTC_CHECK_NOTREACHED(); +} + +bool CheckWavParameters(size_t num_channels, + int sample_rate, + WavFormat format, + size_t bytes_per_sample, + size_t num_samples) { + // num_channels, sample_rate, and bytes_per_sample must be positive, must fit + // in their respective fields, and their product must fit in the 32-bit + // ByteRate field. + if (num_channels == 0 || sample_rate <= 0 || bytes_per_sample == 0) + return false; + if (static_cast(sample_rate) > std::numeric_limits::max()) + return false; + if (num_channels > std::numeric_limits::max()) + return false; + if (static_cast(bytes_per_sample) * 8 > + std::numeric_limits::max()) + return false; + if (static_cast(sample_rate) * num_channels * bytes_per_sample > + std::numeric_limits::max()) + return false; + + // format and bytes_per_sample must agree. + switch (format) { + case WavFormat::kWavFormatPcm: + // Other values may be OK, but for now we're conservative: + if (bytes_per_sample != 1 && bytes_per_sample != 2) + return false; + break; + case WavFormat::kWavFormatALaw: + case WavFormat::kWavFormatMuLaw: + if (bytes_per_sample != 1) + return false; + break; + case WavFormat::kWavFormatIeeeFloat: + if (bytes_per_sample != 4) + return false; + break; + default: + return false; + } + + // The number of bytes in the file, not counting the first ChunkHeader, must + // be less than 2^32; otherwise, the ChunkSize field overflows. + const size_t header_size = kPcmWavHeaderSize - sizeof(ChunkHeader); + const size_t max_samples = + (std::numeric_limits::max() - header_size) / bytes_per_sample; + if (num_samples > max_samples) + return false; + + // Each channel must have the same number of samples. + if (num_samples % num_channels != 0) + return false; + + return true; +} + +} // namespace + +bool CheckWavParameters(size_t num_channels, + int sample_rate, + WavFormat format, + size_t num_samples) { + return CheckWavParameters(num_channels, sample_rate, format, + GetFormatBytesPerSample(format), num_samples); +} + +void WriteWavHeader(size_t num_channels, + int sample_rate, + WavFormat format, + size_t num_samples, + uint8_t* buf, + size_t* header_size) { + RTC_CHECK(buf); + RTC_CHECK(header_size); + + const size_t bytes_per_sample = GetFormatBytesPerSample(format); + RTC_CHECK(CheckWavParameters(num_channels, sample_rate, format, + bytes_per_sample, num_samples)); + if (format == WavFormat::kWavFormatPcm) { + WritePcmWavHeader(num_channels, sample_rate, bytes_per_sample, num_samples, + buf, header_size); + } else { + RTC_CHECK_EQ(format, WavFormat::kWavFormatIeeeFloat); + WriteIeeeFloatWavHeader(num_channels, sample_rate, bytes_per_sample, + num_samples, buf, header_size); + } +} + +bool ReadWavHeader(WavHeaderReader* readable, + size_t* num_channels, + int* sample_rate, + WavFormat* format, + size_t* bytes_per_sample, + size_t* num_samples, + int64_t* data_start_pos) { + // Read using the PCM header, even though it might be float Wav file + auto header = MsanUninitialized({}); + + // Read RIFF chunk. + if (readable->Read(&header.riff, sizeof(header.riff)) != sizeof(header.riff)) + return false; + if (ReadFourCC(header.riff.header.ID) != "RIFF") + return false; + if (ReadFourCC(header.riff.Format) != "WAVE") + return false; + + // Find "fmt " and "data" chunks. While the official Wave file specification + // does not put requirements on the chunks order, it is uncommon to find the + // "data" chunk before the "fmt " one. The code below fails if this is not the + // case. + if (!FindWaveChunk(&header.fmt.header, readable, "fmt ")) { + RTC_LOG(LS_ERROR) << "Cannot find 'fmt ' chunk."; + return false; + } + if (!ReadFmtChunkData(&header.fmt, readable)) { + RTC_LOG(LS_ERROR) << "Cannot read 'fmt ' chunk."; + return false; + } + if (!FindWaveChunk(&header.data.header, readable, "data")) { + RTC_LOG(LS_ERROR) << "Cannot find 'data' chunk."; + return false; + } + + // Parse needed fields. + *format = MapHeaderFieldToWavFormat(header.fmt.AudioFormat); + *num_channels = header.fmt.NumChannels; + *sample_rate = header.fmt.SampleRate; + *bytes_per_sample = header.fmt.BitsPerSample / 8; + const size_t bytes_in_payload = header.data.header.Size; + if (*bytes_per_sample == 0) + return false; + *num_samples = bytes_in_payload / *bytes_per_sample; + + const size_t header_size = *format == WavFormat::kWavFormatPcm + ? kPcmWavHeaderSize + : kIeeeFloatWavHeaderSize; + + if (header.riff.header.Size < RiffChunkSize(bytes_in_payload, header_size)) + return false; + if (header.fmt.ByteRate != + ByteRate(*num_channels, *sample_rate, *bytes_per_sample)) + return false; + if (header.fmt.BlockAlign != BlockAlign(*num_channels, *bytes_per_sample)) + return false; + + if (!CheckWavParameters(*num_channels, *sample_rate, *format, + *bytes_per_sample, *num_samples)) { + return false; + } + + *data_start_pos = readable->GetPosition(); + return true; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/wav_header.h b/pkg/apm/webrtc/common_audio/wav_header.h new file mode 100644 index 00000000..a1aa942a --- /dev/null +++ b/pkg/apm/webrtc/common_audio/wav_header.h @@ -0,0 +1,91 @@ +/* + * 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_WAV_HEADER_H_ +#define COMMON_AUDIO_WAV_HEADER_H_ + +#include +#include + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +// Interface providing header reading functionality. +class WavHeaderReader { + public: + // Returns the number of bytes read. + virtual size_t Read(void* buf, size_t num_bytes) = 0; + virtual bool SeekForward(uint32_t num_bytes) = 0; + virtual ~WavHeaderReader() = default; + virtual int64_t GetPosition() = 0; +}; + +// Possible WAV formats. +enum class WavFormat { + kWavFormatPcm = 1, // PCM, each sample of size bytes_per_sample. + kWavFormatIeeeFloat = 3, // IEEE float. + kWavFormatALaw = 6, // 8-bit ITU-T G.711 A-law. + kWavFormatMuLaw = 7, // 8-bit ITU-T G.711 mu-law. +}; + +// Header sizes for supported WAV formats. +constexpr size_t kPcmWavHeaderSize = 44; +constexpr size_t kIeeeFloatWavHeaderSize = 58; + +// Returns the size of the WAV header for the specified format. +constexpr size_t WavHeaderSize(WavFormat format) { + if (format == WavFormat::kWavFormatPcm) { + return kPcmWavHeaderSize; + } + RTC_CHECK_EQ(format, WavFormat::kWavFormatIeeeFloat); + return kIeeeFloatWavHeaderSize; +} + +// Returns the maximum size of the supported WAV formats. +constexpr size_t MaxWavHeaderSize() { + return std::max(WavHeaderSize(WavFormat::kWavFormatPcm), + WavHeaderSize(WavFormat::kWavFormatIeeeFloat)); +} + +// Return true if the given parameters will make a well-formed WAV header. +bool CheckWavParameters(size_t num_channels, + int sample_rate, + WavFormat format, + size_t num_samples); + +// Write a kWavHeaderSize bytes long WAV header to buf. The payload that +// follows the header is supposed to have the specified number of interleaved +// channels and contain the specified total number of samples of the specified +// type. The size of the header is returned in header_size. CHECKs the input +// parameters for validity. +void WriteWavHeader(size_t num_channels, + int sample_rate, + WavFormat format, + size_t num_samples, + uint8_t* buf, + size_t* header_size); + +// Read a WAV header from an implemented WavHeaderReader and parse the values +// into the provided output parameters. WavHeaderReader is used because the +// header can be variably sized. Returns false if the header is invalid. +bool ReadWavHeader(WavHeaderReader* readable, + size_t* num_channels, + int* sample_rate, + WavFormat* format, + size_t* bytes_per_sample, + size_t* num_samples, + int64_t* data_start_pos); + +} // namespace webrtc + +#endif // COMMON_AUDIO_WAV_HEADER_H_ diff --git a/pkg/apm/webrtc/common_audio/window_generator.cc b/pkg/apm/webrtc/common_audio/window_generator.cc new file mode 100644 index 00000000..da5603d9 --- /dev/null +++ b/pkg/apm/webrtc/common_audio/window_generator.cc @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#define _USE_MATH_DEFINES + +#include "common_audio/window_generator.h" + +#include +#include + +#include "rtc_base/checks.h" + +using std::complex; + +namespace { + +// Modified Bessel function of order 0 for complex inputs. +complex I0(complex x) { + complex y = x / 3.75f; + y *= y; + return 1.0f + y * (3.5156229f + + y * (3.0899424f + + y * (1.2067492f + + y * (0.2659732f + + y * (0.360768e-1f + y * 0.45813e-2f))))); +} + +} // namespace + +namespace webrtc { + +void WindowGenerator::Hanning(int length, float* window) { + RTC_CHECK_GT(length, 1); + RTC_CHECK(window != nullptr); + for (int i = 0; i < length; ++i) { + window[i] = + 0.5f * (1 - cosf(2 * static_cast(M_PI) * i / (length - 1))); + } +} + +void WindowGenerator::KaiserBesselDerived(float alpha, + size_t length, + float* window) { + RTC_CHECK_GT(length, 1U); + RTC_CHECK(window != nullptr); + + const size_t half = (length + 1) / 2; + float sum = 0.0f; + + for (size_t i = 0; i <= half; ++i) { + complex r = (4.0f * i) / length - 1.0f; + sum += I0(static_cast(M_PI) * alpha * sqrt(1.0f - r * r)).real(); + window[i] = sum; + } + for (size_t i = length - 1; i >= half; --i) { + window[length - i - 1] = sqrtf(window[length - i - 1] / sum); + window[i] = window[length - i - 1]; + } + if (length % 2 == 1) { + window[half - 1] = sqrtf(window[half - 1] / sum); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/common_audio/window_generator.h b/pkg/apm/webrtc/common_audio/window_generator.h new file mode 100644 index 00000000..c0a89c4f --- /dev/null +++ b/pkg/apm/webrtc/common_audio/window_generator.h @@ -0,0 +1,31 @@ +/* + * 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_WINDOW_GENERATOR_H_ +#define COMMON_AUDIO_WINDOW_GENERATOR_H_ + +#include + +namespace webrtc { + +// Helper class with generators for various signal transform windows. +class WindowGenerator { + public: + WindowGenerator() = delete; + WindowGenerator(const WindowGenerator&) = delete; + WindowGenerator& operator=(const WindowGenerator&) = delete; + + static void Hanning(int length, float* window); + static void KaiserBesselDerived(float alpha, size_t length, float* window); +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_WINDOW_GENERATOR_H_ diff --git a/pkg/apm/webrtc/experiments/registered_field_trials.h b/pkg/apm/webrtc/experiments/registered_field_trials.h new file mode 100644 index 00000000..2b5ab7f7 --- /dev/null +++ b/pkg/apm/webrtc/experiments/registered_field_trials.h @@ -0,0 +1,8 @@ +#ifndef EXPERIMENTS_REGISTERED_FIELD_TRIALS_H_ +#define EXPERIMENTS_REGISTERED_FIELD_TRIALS_H_ +#include +#include +namespace webrtc { +inline std::vector GetRegisteredFieldTrials() { return {}; } +} +#endif diff --git a/pkg/apm/webrtc/include/modules/audio_processing/aec_dump.h b/pkg/apm/webrtc/include/modules/audio_processing/aec_dump.h new file mode 100644 index 00000000..532fa210 --- /dev/null +++ b/pkg/apm/webrtc/include/modules/audio_processing/aec_dump.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ + +#include + +#include +#include + +#include "absl/base/attributes.h" +#include "api/audio/audio_processing.h" +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { + +// Struct for passing current config from APM without having to +// include protobuf headers. +struct InternalAPMConfig { + InternalAPMConfig(); + InternalAPMConfig(const InternalAPMConfig&); + InternalAPMConfig(InternalAPMConfig&&); + + InternalAPMConfig& operator=(const InternalAPMConfig&); + InternalAPMConfig& operator=(InternalAPMConfig&&) = delete; + + bool operator==(const InternalAPMConfig& other) const; + + bool aec_enabled = false; + bool aec_delay_agnostic_enabled = false; + bool aec_drift_compensation_enabled = false; + bool aec_extended_filter_enabled = false; + int aec_suppression_level = 0; + bool aecm_enabled = false; + bool aecm_comfort_noise_enabled = false; + int aecm_routing_mode = 0; + bool agc_enabled = false; + int agc_mode = 0; + bool agc_limiter_enabled = false; + bool hpf_enabled = false; + bool ns_enabled = false; + int ns_level = 0; + bool transient_suppression_enabled = false; + bool noise_robust_agc_enabled = false; + bool pre_amplifier_enabled = false; + float pre_amplifier_fixed_gain_factor = 1.f; + std::string experiments_description = ""; +}; + +// An interface for recording configuration and input/output streams +// of the Audio Processing Module. The recordings are called +// 'aec-dumps' and are stored in a protobuf format defined in +// debug.proto. +// The Write* methods are always safe to call concurrently or +// otherwise for all implementing subclasses. The intended mode of +// operation is to create a protobuf object from the input, and send +// it away to be written to file asynchronously. +class AecDump { + public: + struct AudioProcessingState { + int delay; + int drift; + std::optional applied_input_volume; + bool keypress; + }; + + virtual ~AecDump() = default; + + // Logs Event::Type INIT message. + virtual void WriteInitMessage(const ProcessingConfig& api_format, + int64_t time_now_ms) = 0; + ABSL_DEPRECATED("") + void WriteInitMessage(const ProcessingConfig& api_format) { + WriteInitMessage(api_format, 0); + } + + // Logs Event::Type STREAM message. To log an input/output pair, + // call the AddCapture* and AddAudioProcessingState methods followed + // by a WriteCaptureStreamMessage call. + virtual void AddCaptureStreamInput( + const AudioFrameView& src) = 0; + virtual void AddCaptureStreamOutput( + const AudioFrameView& src) = 0; + virtual void AddCaptureStreamInput(const int16_t* const data, + int num_channels, + int samples_per_channel) = 0; + virtual void AddCaptureStreamOutput(const int16_t* const data, + int num_channels, + int samples_per_channel) = 0; + virtual void AddAudioProcessingState(const AudioProcessingState& state) = 0; + virtual void WriteCaptureStreamMessage() = 0; + + // Logs Event::Type REVERSE_STREAM message. + virtual void WriteRenderStreamMessage(const int16_t* const data, + int num_channels, + int samples_per_channel) = 0; + virtual void WriteRenderStreamMessage( + const AudioFrameView& src) = 0; + + virtual void WriteRuntimeSetting( + const AudioProcessing::RuntimeSetting& runtime_setting) = 0; + + // Logs Event::Type CONFIG message. + virtual void WriteConfig(const InternalAPMConfig& config) = 0; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ diff --git a/pkg/apm/webrtc/include/modules/audio_processing/audio_frame_proxies.h b/pkg/apm/webrtc/include/modules/audio_processing/audio_frame_proxies.h new file mode 100644 index 00000000..5dd111ca --- /dev/null +++ b/pkg/apm/webrtc/include/modules/audio_processing/audio_frame_proxies.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_PROXIES_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_PROXIES_H_ + +namespace webrtc { + +class AudioFrame; +class AudioProcessing; + +// Processes a 10 ms `frame` of the primary audio stream using the provided +// AudioProcessing object. On the client-side, this is the near-end (or +// captured) audio. The `sample_rate_hz_`, `num_channels_`, and +// `samples_per_channel_` members of `frame` must be valid. If changed from the +// previous call to this function, it will trigger an initialization of the +// provided AudioProcessing object. +// The function returns any error codes passed from the AudioProcessing +// ProcessStream method. +int ProcessAudioFrame(AudioProcessing* ap, AudioFrame* frame); + +// Processes a 10 ms `frame` of the reverse direction audio stream using the +// provided AudioProcessing object. The frame may be modified. On the +// client-side, this is the far-end (or to be rendered) audio. The +// `sample_rate_hz_`, `num_channels_`, and `samples_per_channel_` members of +// `frame` must be valid. If changed from the previous call to this function, it +// will trigger an initialization of the provided AudioProcessing object. +// The function returns any error codes passed from the AudioProcessing +// ProcessReverseStream method. +int ProcessReverseAudioFrame(AudioProcessing* ap, AudioFrame* frame); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_PROXIES_H_ diff --git a/pkg/apm/webrtc/include/modules/audio_processing/audio_frame_view.h b/pkg/apm/webrtc/include/modules/audio_processing/audio_frame_view.h new file mode 100644 index 00000000..27e20090 --- /dev/null +++ b/pkg/apm/webrtc/include/modules/audio_processing/audio_frame_view.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_VIEW_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_VIEW_H_ + +#include "api/audio/audio_view.h" + +namespace webrtc { + +// Class to pass audio data in T** format, where T is a numeric type. +template +class AudioFrameView { + public: + // `num_channels` and `channel_size` describe the T** + // `audio_samples`. `audio_samples` is assumed to point to a + // two-dimensional |num_channels * channel_size| array of floats. + // + // Note: The implementation now only requires the first channel pointer. + // The previous implementation retained a pointer to externally owned array + // of channel pointers, but since the channel size and count are provided + // and the array is assumed to be a single two-dimensional array, the other + // channel pointers can be calculated based on that (which is what the class + // now uses `DeinterleavedView<>` internally for). + AudioFrameView(T* const* audio_samples, int num_channels, int channel_size) + : view_(num_channels && channel_size ? audio_samples[0] : nullptr, + channel_size, + num_channels) { + RTC_DCHECK_GE(view_.num_channels(), 0); + RTC_DCHECK_GE(view_.samples_per_channel(), 0); + } + + // Implicit cast to allow converting AudioFrameView to + // AudioFrameView. + template + AudioFrameView(AudioFrameView other) : view_(other.view()) {} + + // Allow constructing AudioFrameView from a DeinterleavedView. + template + explicit AudioFrameView(DeinterleavedView view) : view_(view) {} + + AudioFrameView() = delete; + + int num_channels() const { return view_.num_channels(); } + int samples_per_channel() const { return view_.samples_per_channel(); } + MonoView channel(int idx) { return view_[idx]; } + MonoView channel(int idx) const { return view_[idx]; } + MonoView operator[](int idx) { return view_[idx]; } + MonoView operator[](int idx) const { return view_[idx]; } + + DeinterleavedView view() { return view_; } + DeinterleavedView view() const { return view_; } + + private: + DeinterleavedView view_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_VIEW_H_ diff --git a/pkg/apm/webrtc/include/modules/audio_processing/audio_processing.h b/pkg/apm/webrtc/include/modules/audio_processing/audio_processing.h new file mode 100644 index 00000000..fe938f86 --- /dev/null +++ b/pkg/apm/webrtc/include/modules/audio_processing/audio_processing.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ + +// This is a transitional header forwarding to the new version in the api/ +// folder. +#include "api/audio/audio_processing.h" + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ diff --git a/pkg/apm/webrtc/include/modules/audio_processing/audio_processing_statistics.h b/pkg/apm/webrtc/include/modules/audio_processing/audio_processing_statistics.h new file mode 100644 index 00000000..594d3f5a --- /dev/null +++ b/pkg/apm/webrtc/include/modules/audio_processing/audio_processing_statistics.h @@ -0,0 +1,18 @@ +/* + * 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 MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_STATISTICS_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_STATISTICS_H_ + +// This is a transitional header forwarding to the new version in the api/ +// folder. +#include "api/audio/audio_processing_statistics.h" + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_STATISTICS_H_ diff --git a/pkg/apm/webrtc/include/modules/audio_processing/mock_audio_processing.h b/pkg/apm/webrtc/include/modules/audio_processing/mock_audio_processing.h new file mode 100644 index 00000000..55ad087f --- /dev/null +++ b/pkg/apm/webrtc/include/modules/audio_processing/mock_audio_processing.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_MOCK_AUDIO_PROCESSING_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_MOCK_AUDIO_PROCESSING_H_ + +#include + +#include "absl/base/nullability.h" +#include "absl/strings/string_view.h" +#include "api/audio/audio_processing.h" +#include "api/audio/audio_processing_statistics.h" +#include "api/environment/environment.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_base.h" +#include "modules/audio_processing/include/aec_dump.h" +#include "test/gmock.h" + +namespace webrtc { + +namespace test { +class MockCustomProcessing : public CustomProcessing { + public: + virtual ~MockCustomProcessing() {} + MOCK_METHOD(void, + Initialize, + (int sample_rate_hz, int num_channels), + (override)); + MOCK_METHOD(void, Process, (AudioBuffer * audio), (override)); + MOCK_METHOD(void, + SetRuntimeSetting, + (AudioProcessing::RuntimeSetting setting), + (override)); + MOCK_METHOD(std::string, ToString, (), (const, override)); +}; + +class MockCustomAudioAnalyzer : public CustomAudioAnalyzer { + public: + virtual ~MockCustomAudioAnalyzer() {} + MOCK_METHOD(void, + Initialize, + (int sample_rate_hz, int num_channels), + (override)); + MOCK_METHOD(void, Analyze, (const AudioBuffer* audio), (override)); + MOCK_METHOD(std::string, ToString, (), (const, override)); +}; + +class MockEchoControl : public EchoControl { + public: + virtual ~MockEchoControl() {} + MOCK_METHOD(void, AnalyzeRender, (AudioBuffer * render), (override)); + MOCK_METHOD(void, AnalyzeCapture, (AudioBuffer * capture), (override)); + MOCK_METHOD(void, + ProcessCapture, + (AudioBuffer * capture, bool echo_path_change), + (override)); + MOCK_METHOD(void, + ProcessCapture, + (AudioBuffer * capture, + AudioBuffer* linear_output, + bool echo_path_change), + (override)); + MOCK_METHOD(Metrics, GetMetrics, (), (const, override)); + MOCK_METHOD(void, SetAudioBufferDelay, (int delay_ms), (override)); + MOCK_METHOD(bool, ActiveProcessing, (), (const, override)); +}; + +class MockEchoDetector : public EchoDetector { + public: + virtual ~MockEchoDetector() {} + MOCK_METHOD(void, + Initialize, + (int capture_sample_rate_hz, + int num_capture_channels, + int render_sample_rate_hz, + int num_render_channels), + (override)); + MOCK_METHOD(void, + AnalyzeRenderAudio, + (webrtc::ArrayView render_audio), + (override)); + MOCK_METHOD(void, + AnalyzeCaptureAudio, + (webrtc::ArrayView capture_audio), + (override)); + MOCK_METHOD(Metrics, GetMetrics, (), (const, override)); +}; + +class MockAudioProcessing : public AudioProcessing { + public: + MockAudioProcessing() {} + + virtual ~MockAudioProcessing() {} + + MOCK_METHOD(int, Initialize, (), (override)); + MOCK_METHOD(int, + Initialize, + (const ProcessingConfig& processing_config), + (override)); + MOCK_METHOD(void, ApplyConfig, (const Config& config), (override)); + MOCK_METHOD(int, proc_sample_rate_hz, (), (const, override)); + MOCK_METHOD(int, proc_split_sample_rate_hz, (), (const, override)); + MOCK_METHOD(size_t, num_input_channels, (), (const, override)); + MOCK_METHOD(size_t, num_proc_channels, (), (const, override)); + MOCK_METHOD(size_t, num_output_channels, (), (const, override)); + MOCK_METHOD(size_t, num_reverse_channels, (), (const, override)); + MOCK_METHOD(void, set_output_will_be_muted, (bool muted), (override)); + MOCK_METHOD(bool, get_output_will_be_muted, (), (override)); + MOCK_METHOD(void, SetRuntimeSetting, (RuntimeSetting setting), (override)); + MOCK_METHOD(bool, PostRuntimeSetting, (RuntimeSetting setting), (override)); + MOCK_METHOD(int, + ProcessStream, + (const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest), + (override)); + MOCK_METHOD(int, + ProcessStream, + (const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config, + float* const* dest), + (override)); + MOCK_METHOD(int, + ProcessReverseStream, + (const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest), + (override)); + MOCK_METHOD(int, + AnalyzeReverseStream, + (const float* const* data, const StreamConfig& reverse_config), + (override)); + MOCK_METHOD(int, + ProcessReverseStream, + (const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config, + float* const* dest), + (override)); + MOCK_METHOD(bool, + GetLinearAecOutput, + ((webrtc::ArrayView> linear_output)), + (const, override)); + MOCK_METHOD(int, set_stream_delay_ms, (int delay), (override)); + MOCK_METHOD(int, stream_delay_ms, (), (const, override)); + MOCK_METHOD(void, set_stream_key_pressed, (bool key_pressed), (override)); + MOCK_METHOD(void, set_stream_analog_level, (int), (override)); + MOCK_METHOD(int, recommended_stream_analog_level, (), (const, override)); + MOCK_METHOD(bool, + CreateAndAttachAecDump, + (absl::string_view file_name, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull worker_queue), + (override)); + MOCK_METHOD(bool, + CreateAndAttachAecDump, + (FILE * handle, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull worker_queue), + (override)); + MOCK_METHOD(void, AttachAecDump, (std::unique_ptr), (override)); + MOCK_METHOD(void, DetachAecDump, (), (override)); + + MOCK_METHOD(AudioProcessingStats, GetStatistics, (), (override)); + MOCK_METHOD(AudioProcessingStats, GetStatistics, (bool), (override)); + + MOCK_METHOD(AudioProcessing::Config, GetConfig, (), (const, override)); +}; + +class MockAudioProcessingBuilder : public AudioProcessingBuilderInterface { + public: + MOCK_METHOD(scoped_refptr, + Build, + (const Environment&), + (override)); +}; + +} // namespace test +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_MOCK_AUDIO_PROCESSING_H_ diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/bandwidth_info.h b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/bandwidth_info.h new file mode 100644 index 00000000..c3830a5f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/bandwidth_info.h @@ -0,0 +1,24 @@ +/* + * 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 MODULES_AUDIO_CODING_CODECS_ISAC_BANDWIDTH_INFO_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_BANDWIDTH_INFO_H_ + +#include + +typedef struct { + int in_use; + int32_t send_bw_avg; + int32_t send_max_delay_avg; + int16_t bottleneck_idx; + int16_t jitter_info; +} IsacBandwidthInfo; + +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_BANDWIDTH_INFO_H_ diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c new file mode 100644 index 00000000..d359e8f7 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#ifdef WEBRTC_ANDROID +#include +#endif + +#include "modules/audio_coding/codecs/isac/main/source/isac_vad.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" + +static void WebRtcIsac_AllPoleFilter(double* InOut, + double* Coef, + size_t lengthInOut, + int orderCoef) { + /* the state of filter is assumed to be in InOut[-1] to InOut[-orderCoef] */ + double scal; + double sum; + size_t n; + int k; + + // if (fabs(Coef[0]-1.0)<0.001) { + if ((Coef[0] > 0.9999) && (Coef[0] < 1.0001)) { + for (n = 0; n < lengthInOut; n++) { + sum = Coef[1] * InOut[-1]; + for (k = 2; k <= orderCoef; k++) { + sum += Coef[k] * InOut[-k]; + } + *InOut++ -= sum; + } + } else { + scal = 1.0 / Coef[0]; + for (n = 0; n < lengthInOut; n++) { + *InOut *= scal; + for (k = 1; k <= orderCoef; k++) { + *InOut -= scal * Coef[k] * InOut[-k]; + } + InOut++; + } + } +} + +static void WebRtcIsac_AllZeroFilter(double* In, + double* Coef, + size_t lengthInOut, + int orderCoef, + double* Out) { + /* the state of filter is assumed to be in In[-1] to In[-orderCoef] */ + + size_t n; + int k; + double tmp; + + for (n = 0; n < lengthInOut; n++) { + tmp = In[0] * Coef[0]; + + for (k = 1; k <= orderCoef; k++) { + tmp += Coef[k] * In[-k]; + } + + *Out++ = tmp; + In++; + } +} + +static void WebRtcIsac_ZeroPoleFilter(double* In, + double* ZeroCoef, + double* PoleCoef, + size_t lengthInOut, + int orderCoef, + double* Out) { + /* the state of the zero section is assumed to be in In[-1] to In[-orderCoef] + */ + /* the state of the pole section is assumed to be in Out[-1] to + * Out[-orderCoef] */ + + WebRtcIsac_AllZeroFilter(In, ZeroCoef, lengthInOut, orderCoef, Out); + WebRtcIsac_AllPoleFilter(Out, PoleCoef, lengthInOut, orderCoef); +} + +void WebRtcIsac_AutoCorr(double* r, const double* x, size_t N, size_t order) { + size_t lag, n; + double sum, prod; + const double* x_lag; + + for (lag = 0; lag <= order; lag++) { + sum = 0.0f; + x_lag = &x[lag]; + prod = x[0] * x_lag[0]; + for (n = 1; n < N - lag; n++) { + sum += prod; + prod = x[n] * x_lag[n]; + } + sum += prod; + r[lag] = sum; + } +} + +static void WebRtcIsac_BwExpand(double* out, + double* in, + double coef, + size_t length) { + size_t i; + double chirp; + + chirp = coef; + + out[0] = in[0]; + for (i = 1; i < length; i++) { + out[i] = chirp * in[i]; + chirp *= coef; + } +} + +void WebRtcIsac_WeightingFilter(const double* in, + double* weiout, + double* whiout, + WeightFiltstr* wfdata) { + double tmpbuffer[PITCH_FRAME_LEN + PITCH_WLPCBUFLEN]; + double corr[PITCH_WLPCORDER + 1], rc[PITCH_WLPCORDER + 1]; + double apol[PITCH_WLPCORDER + 1], apolr[PITCH_WLPCORDER + 1]; + double rho = 0.9, *inp, *dp, *dp2; + double whoutbuf[PITCH_WLPCBUFLEN + PITCH_WLPCORDER]; + double weoutbuf[PITCH_WLPCBUFLEN + PITCH_WLPCORDER]; + double *weo, *who, opol[PITCH_WLPCORDER + 1], ext[PITCH_WLPCWINLEN]; + int k, n, endpos, start; + + /* Set up buffer and states */ + memcpy(tmpbuffer, wfdata->buffer, sizeof(double) * PITCH_WLPCBUFLEN); + memcpy(tmpbuffer + PITCH_WLPCBUFLEN, in, sizeof(double) * PITCH_FRAME_LEN); + memcpy(wfdata->buffer, tmpbuffer + PITCH_FRAME_LEN, + sizeof(double) * PITCH_WLPCBUFLEN); + + dp = weoutbuf; + dp2 = whoutbuf; + for (k = 0; k < PITCH_WLPCORDER; k++) { + *dp++ = wfdata->weostate[k]; + *dp2++ = wfdata->whostate[k]; + opol[k] = 0.0; + } + opol[0] = 1.0; + opol[PITCH_WLPCORDER] = 0.0; + weo = dp; + who = dp2; + + endpos = PITCH_WLPCBUFLEN + PITCH_SUBFRAME_LEN; + inp = tmpbuffer + PITCH_WLPCBUFLEN; + + for (n = 0; n < PITCH_SUBFRAMES; n++) { + /* Windowing */ + start = endpos - PITCH_WLPCWINLEN; + for (k = 0; k < PITCH_WLPCWINLEN; k++) { + ext[k] = wfdata->window[k] * tmpbuffer[start + k]; + } + + /* Get LPC polynomial */ + WebRtcIsac_AutoCorr(corr, ext, PITCH_WLPCWINLEN, PITCH_WLPCORDER); + corr[0] = 1.01 * corr[0] + 1.0; /* White noise correction */ + WebRtcIsac_LevDurb(apol, rc, corr, PITCH_WLPCORDER); + WebRtcIsac_BwExpand(apolr, apol, rho, PITCH_WLPCORDER + 1); + + /* Filtering */ + WebRtcIsac_ZeroPoleFilter(inp, apol, apolr, PITCH_SUBFRAME_LEN, + PITCH_WLPCORDER, weo); + WebRtcIsac_ZeroPoleFilter(inp, apolr, opol, PITCH_SUBFRAME_LEN, + PITCH_WLPCORDER, who); + + inp += PITCH_SUBFRAME_LEN; + endpos += PITCH_SUBFRAME_LEN; + weo += PITCH_SUBFRAME_LEN; + who += PITCH_SUBFRAME_LEN; + } + + /* Export filter states */ + for (k = 0; k < PITCH_WLPCORDER; k++) { + wfdata->weostate[k] = weoutbuf[PITCH_FRAME_LEN + k]; + wfdata->whostate[k] = whoutbuf[PITCH_FRAME_LEN + k]; + } + + /* Export output data */ + memcpy(weiout, weoutbuf + PITCH_WLPCORDER, sizeof(double) * PITCH_FRAME_LEN); + memcpy(whiout, whoutbuf + PITCH_WLPCORDER, sizeof(double) * PITCH_FRAME_LEN); +} diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.h b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.h new file mode 100644 index 00000000..a747a7f5 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_FILTER_FUNCTIONS_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_FILTER_FUNCTIONS_H_ + +#include + +#include "modules/audio_coding/codecs/isac/main/source/structs.h" + +void WebRtcIsac_AutoCorr(double* r, const double* x, size_t N, size_t order); + +void WebRtcIsac_WeightingFilter(const double* in, + double* weiout, + double* whiout, + WeightFiltstr* wfdata); + +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_FILTER_FUNCTIONS_H_ diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac.go b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac.go new file mode 100644 index 00000000..f09044b7 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac.go @@ -0,0 +1,9 @@ +//go:build console + +package source + +// #cgo CFLAGS: -I${SRCDIR}/../../../../../.. -std=c11 -Wno-unused-parameter -Wno-sign-compare -Wno-deprecated-declarations +// #cgo darwin CFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CFLAGS: -DWEBRTC_WIN +import "C" diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.c b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.c new file mode 100644 index 00000000..57cf0c39 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.c @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_coding/codecs/isac/main/source/isac_vad.h" + +#include + +void WebRtcIsac_InitPitchFilter(PitchFiltstr* pitchfiltdata) { + int k; + + for (k = 0; k < PITCH_BUFFSIZE; k++) { + pitchfiltdata->ubuf[k] = 0.0; + } + pitchfiltdata->ystate[0] = 0.0; + for (k = 1; k < (PITCH_DAMPORDER); k++) { + pitchfiltdata->ystate[k] = 0.0; + } + pitchfiltdata->oldlagp[0] = 50.0; + pitchfiltdata->oldgainp[0] = 0.0; +} + +static void WebRtcIsac_InitWeightingFilter(WeightFiltstr* wfdata) { + int k; + double t, dtmp, dtmp2, denum, denum2; + + for (k = 0; k < PITCH_WLPCBUFLEN; k++) + wfdata->buffer[k] = 0.0; + + for (k = 0; k < PITCH_WLPCORDER; k++) { + wfdata->istate[k] = 0.0; + wfdata->weostate[k] = 0.0; + wfdata->whostate[k] = 0.0; + } + + /* next part should be in Matlab, writing to a global table */ + t = 0.5; + denum = 1.0 / ((double)PITCH_WLPCWINLEN); + denum2 = denum * denum; + for (k = 0; k < PITCH_WLPCWINLEN; k++) { + dtmp = PITCH_WLPCASYM * t * denum + (1 - PITCH_WLPCASYM) * t * t * denum2; + dtmp *= 3.14159265; + dtmp2 = sin(dtmp); + wfdata->window[k] = dtmp2 * dtmp2; + t++; + } +} + +void WebRtcIsac_InitPitchAnalysis(PitchAnalysisStruct* State) { + int k; + + for (k = 0; k < PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + PITCH_MAX_LAG / 2 - + PITCH_FRAME_LEN / 2 + 2; + k++) + State->dec_buffer[k] = 0.0; + for (k = 0; k < 2 * ALLPASSSECTIONS + 1; k++) + State->decimator_state[k] = 0.0; + for (k = 0; k < 2; k++) + State->hp_state[k] = 0.0; + for (k = 0; k < QLOOKAHEAD; k++) + State->whitened_buf[k] = 0.0; + for (k = 0; k < QLOOKAHEAD; k++) + State->inbuf[k] = 0.0; + + WebRtcIsac_InitPitchFilter(&(State->PFstr_wght)); + + WebRtcIsac_InitPitchFilter(&(State->PFstr)); + + WebRtcIsac_InitWeightingFilter(&(State->Wghtstr)); +} + +void WebRtcIsac_InitPreFilterbank(PreFiltBankstr* prefiltdata) { + int k; + + for (k = 0; k < QLOOKAHEAD; k++) { + prefiltdata->INLABUF1[k] = 0; + prefiltdata->INLABUF2[k] = 0; + + prefiltdata->INLABUF1_float[k] = 0; + prefiltdata->INLABUF2_float[k] = 0; + } + for (k = 0; k < 2 * (QORDER - 1); k++) { + prefiltdata->INSTAT1[k] = 0; + prefiltdata->INSTAT2[k] = 0; + prefiltdata->INSTATLA1[k] = 0; + prefiltdata->INSTATLA2[k] = 0; + + prefiltdata->INSTAT1_float[k] = 0; + prefiltdata->INSTAT2_float[k] = 0; + prefiltdata->INSTATLA1_float[k] = 0; + prefiltdata->INSTATLA2_float[k] = 0; + } + + /* High pass filter states */ + prefiltdata->HPstates[0] = 0.0; + prefiltdata->HPstates[1] = 0.0; + + prefiltdata->HPstates_float[0] = 0.0f; + prefiltdata->HPstates_float[1] = 0.0f; + + return; +} + +double WebRtcIsac_LevDurb(double* a, double* k, double* r, size_t order) { + const double LEVINSON_EPS = 1.0e-10; + + double sum, alpha; + size_t m, m_h, i; + alpha = 0; // warning -DH + a[0] = 1.0; + if (r[0] < LEVINSON_EPS) { /* if r[0] <= 0, set LPC coeff. to zero */ + for (i = 0; i < order; i++) { + k[i] = 0; + a[i + 1] = 0; + } + } else { + a[1] = k[0] = -r[1] / r[0]; + alpha = r[0] + r[1] * k[0]; + for (m = 1; m < order; m++) { + sum = r[m + 1]; + for (i = 0; i < m; i++) { + sum += a[i + 1] * r[m - i]; + } + k[m] = -sum / alpha; + alpha += k[m] * sum; + m_h = (m + 1) >> 1; + for (i = 0; i < m_h; i++) { + sum = a[i + 1] + k[m] * a[m - i]; + a[m - i] += k[m] * a[i + 1]; + a[i + 1] = sum; + } + a[m + 1] = k[m]; + } + } + return alpha; +} + +/* The upper channel all-pass filter factors */ +const float WebRtcIsac_kUpperApFactorsFloat[2] = {0.03470000000000f, + 0.38260000000000f}; + +/* The lower channel all-pass filter factors */ +const float WebRtcIsac_kLowerApFactorsFloat[2] = {0.15440000000000f, + 0.74400000000000f}; + +/* This function performs all-pass filtering--a series of first order all-pass + * sections are used to filter the input in a cascade manner. + * The input is overwritten!! + */ +void WebRtcIsac_AllPassFilter2Float(float* InOut, + const float* APSectionFactors, + int lengthInOut, + int NumberOfSections, + float* FilterState) { + int n, j; + float temp; + for (j = 0; j < NumberOfSections; j++) { + for (n = 0; n < lengthInOut; n++) { + temp = FilterState[j] + APSectionFactors[j] * InOut[n]; + FilterState[j] = -APSectionFactors[j] * temp + InOut[n]; + InOut[n] = temp; + } + } +} + +/* The number of composite all-pass filter factors */ +#define NUMBEROFCOMPOSITEAPSECTIONS 4 + +/* Function WebRtcIsac_SplitAndFilter + * This function creates low-pass and high-pass decimated versions of part of + the input signal, and part of the signal in the input 'lookahead buffer'. + + INPUTS: + in: a length FRAMESAMPLES array of input samples + prefiltdata: input data structure containing the filterbank states + and lookahead samples from the previous encoding + iteration. + OUTPUTS: + LP: a FRAMESAMPLES_HALF array of low-pass filtered samples that + have been phase equalized. The first QLOOKAHEAD samples are + based on the samples in the two prefiltdata->INLABUFx arrays + each of length QLOOKAHEAD. + The remaining FRAMESAMPLES_HALF-QLOOKAHEAD samples are based + on the first FRAMESAMPLES_HALF-QLOOKAHEAD samples of the input + array in[]. + HP: a FRAMESAMPLES_HALF array of high-pass filtered samples that + have been phase equalized. The first QLOOKAHEAD samples are + based on the samples in the two prefiltdata->INLABUFx arrays + each of length QLOOKAHEAD. + The remaining FRAMESAMPLES_HALF-QLOOKAHEAD samples are based + on the first FRAMESAMPLES_HALF-QLOOKAHEAD samples of the input + array in[]. + + LP_la: a FRAMESAMPLES_HALF array of low-pass filtered samples. + These samples are not phase equalized. They are computed + from the samples in the in[] array. + HP_la: a FRAMESAMPLES_HALF array of high-pass filtered samples + that are not phase equalized. They are computed from + the in[] vector. + prefiltdata: this input data structure's filterbank state and + lookahead sample buffers are updated for the next + encoding iteration. +*/ +void WebRtcIsac_SplitAndFilterFloat(float* pin, + float* LP, + float* HP, + double* LP_la, + double* HP_la, + PreFiltBankstr* prefiltdata) { + int k, n; + float CompositeAPFilterState[NUMBEROFCOMPOSITEAPSECTIONS]; + float ForTransform_CompositeAPFilterState[NUMBEROFCOMPOSITEAPSECTIONS]; + float ForTransform_CompositeAPFilterState2[NUMBEROFCOMPOSITEAPSECTIONS]; + float tempinoutvec[FRAMESAMPLES + MAX_AR_MODEL_ORDER]; + float tempin_ch1[FRAMESAMPLES + MAX_AR_MODEL_ORDER]; + float tempin_ch2[FRAMESAMPLES + MAX_AR_MODEL_ORDER]; + float in[FRAMESAMPLES]; + float ftmp; + + /* HPstcoeff_in = {a1, a2, b1 - b0 * a1, b2 - b0 * a2}; */ + static const float kHpStCoefInFloat[4] = { + -1.94895953203325f, 0.94984516000000f, -0.05101826139794f, + 0.05015484000000f}; + + /* The composite all-pass filter factors */ + static const float WebRtcIsac_kCompositeApFactorsFloat[4] = { + 0.03470000000000f, 0.15440000000000f, 0.38260000000000f, + 0.74400000000000f}; + + // The matrix for transforming the backward composite state to upper channel + // state. + static const float WebRtcIsac_kTransform1Float[8] = { + -0.00158678506084f, 0.00127157815343f, -0.00104805672709f, + 0.00084837248079f, 0.00134467983258f, -0.00107756549387f, + 0.00088814793277f, -0.00071893072525f}; + + // The matrix for transforming the backward composite state to lower channel + // state. + static const float WebRtcIsac_kTransform2Float[8] = { + -0.00170686041697f, 0.00136780109829f, -0.00112736532350f, + 0.00091257055385f, 0.00103094281812f, -0.00082615076557f, + 0.00068092756088f, -0.00055119165484f}; + + /* High pass filter */ + + for (k = 0; k < FRAMESAMPLES; k++) { + in[k] = pin[k] + kHpStCoefInFloat[2] * prefiltdata->HPstates_float[0] + + kHpStCoefInFloat[3] * prefiltdata->HPstates_float[1]; + ftmp = pin[k] - kHpStCoefInFloat[0] * prefiltdata->HPstates_float[0] - + kHpStCoefInFloat[1] * prefiltdata->HPstates_float[1]; + prefiltdata->HPstates_float[1] = prefiltdata->HPstates_float[0]; + prefiltdata->HPstates_float[0] = ftmp; + } + + /* First Channel */ + + /*initial state of composite filter is zero */ + for (k = 0; k < NUMBEROFCOMPOSITEAPSECTIONS; k++) { + CompositeAPFilterState[k] = 0.0; + } + /* put every other sample of input into a temporary vector in reverse + * (backward) order*/ + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + tempinoutvec[k] = in[FRAMESAMPLES - 1 - 2 * k]; + } + + /* now all-pass filter the backwards vector. Output values overwrite the + * input vector. */ + WebRtcIsac_AllPassFilter2Float( + tempinoutvec, WebRtcIsac_kCompositeApFactorsFloat, FRAMESAMPLES_HALF, + NUMBEROFCOMPOSITEAPSECTIONS, CompositeAPFilterState); + + /* save the backwards filtered output for later forward filtering, + but write it in forward order*/ + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + tempin_ch1[FRAMESAMPLES_HALF + QLOOKAHEAD - 1 - k] = tempinoutvec[k]; + } + + /* save the backwards filter state becaue it will be transformed + later into a forward state */ + for (k = 0; k < NUMBEROFCOMPOSITEAPSECTIONS; k++) { + ForTransform_CompositeAPFilterState[k] = CompositeAPFilterState[k]; + } + + /* now backwards filter the samples in the lookahead buffer. The samples were + placed there in the encoding of the previous frame. The output samples + overwrite the input samples */ + WebRtcIsac_AllPassFilter2Float( + prefiltdata->INLABUF1_float, WebRtcIsac_kCompositeApFactorsFloat, + QLOOKAHEAD, NUMBEROFCOMPOSITEAPSECTIONS, CompositeAPFilterState); + + /* save the output, but write it in forward order */ + /* write the lookahead samples for the next encoding iteration. Every other + sample at the end of the input frame is written in reverse order for the + lookahead length. Exported in the prefiltdata structure. */ + for (k = 0; k < QLOOKAHEAD; k++) { + tempin_ch1[QLOOKAHEAD - 1 - k] = prefiltdata->INLABUF1_float[k]; + prefiltdata->INLABUF1_float[k] = in[FRAMESAMPLES - 1 - 2 * k]; + } + + /* Second Channel. This is exactly like the first channel, except that the + even samples are now filtered instead (lower channel). */ + for (k = 0; k < NUMBEROFCOMPOSITEAPSECTIONS; k++) { + CompositeAPFilterState[k] = 0.0; + } + + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + tempinoutvec[k] = in[FRAMESAMPLES - 2 - 2 * k]; + } + + WebRtcIsac_AllPassFilter2Float( + tempinoutvec, WebRtcIsac_kCompositeApFactorsFloat, FRAMESAMPLES_HALF, + NUMBEROFCOMPOSITEAPSECTIONS, CompositeAPFilterState); + + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + tempin_ch2[FRAMESAMPLES_HALF + QLOOKAHEAD - 1 - k] = tempinoutvec[k]; + } + + for (k = 0; k < NUMBEROFCOMPOSITEAPSECTIONS; k++) { + ForTransform_CompositeAPFilterState2[k] = CompositeAPFilterState[k]; + } + + WebRtcIsac_AllPassFilter2Float( + prefiltdata->INLABUF2_float, WebRtcIsac_kCompositeApFactorsFloat, + QLOOKAHEAD, NUMBEROFCOMPOSITEAPSECTIONS, CompositeAPFilterState); + + for (k = 0; k < QLOOKAHEAD; k++) { + tempin_ch2[QLOOKAHEAD - 1 - k] = prefiltdata->INLABUF2_float[k]; + prefiltdata->INLABUF2_float[k] = in[FRAMESAMPLES - 2 - 2 * k]; + } + + /* Transform filter states from backward to forward */ + /*At this point, each of the states of the backwards composite filters for the + two channels are transformed into forward filtering states for the + corresponding forward channel filters. Each channel's forward filtering + state from the previous + encoding iteration is added to the transformed state to get a proper forward + state */ + + /* So the existing NUMBEROFCOMPOSITEAPSECTIONS x 1 (4x1) state vector is + multiplied by a NUMBEROFCHANNELAPSECTIONSxNUMBEROFCOMPOSITEAPSECTIONS (2x4) + transform matrix to get the new state that is added to the previous 2x1 + input state */ + + for (k = 0; k < NUMBEROFCHANNELAPSECTIONS; k++) { /* k is row variable */ + for (n = 0; n < NUMBEROFCOMPOSITEAPSECTIONS; + n++) { /* n is column variable */ + prefiltdata->INSTAT1_float[k] += + ForTransform_CompositeAPFilterState[n] * + WebRtcIsac_kTransform1Float[k * NUMBEROFCHANNELAPSECTIONS + n]; + prefiltdata->INSTAT2_float[k] += + ForTransform_CompositeAPFilterState2[n] * + WebRtcIsac_kTransform2Float[k * NUMBEROFCHANNELAPSECTIONS + n]; + } + } + + /*obtain polyphase components by forward all-pass filtering through each + * channel */ + /* the backward filtered samples are now forward filtered with the + * corresponding channel filters */ + /* The all pass filtering automatically updates the filter states which are + exported in the prefiltdata structure */ + WebRtcIsac_AllPassFilter2Float(tempin_ch1, WebRtcIsac_kUpperApFactorsFloat, + FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, + prefiltdata->INSTAT1_float); + WebRtcIsac_AllPassFilter2Float(tempin_ch2, WebRtcIsac_kLowerApFactorsFloat, + FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, + prefiltdata->INSTAT2_float); + + /* Now Construct low-pass and high-pass signals as combinations of polyphase + * components */ + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + LP[k] = 0.5f * (tempin_ch1[k] + tempin_ch2[k]); /* low pass signal*/ + HP[k] = 0.5f * (tempin_ch1[k] - tempin_ch2[k]); /* high pass signal*/ + } + + /* Lookahead LP and HP signals */ + /* now create low pass and high pass signals of the input vector. However, no + backwards filtering is performed, and hence no phase equalization is + involved. Also, the input contains some samples that are lookahead samples. + The high pass and low pass signals that are created are used outside this + function for analysis (not encoding) purposes */ + + /* set up input */ + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + tempin_ch1[k] = in[2 * k + 1]; + tempin_ch2[k] = in[2 * k]; + } + + /* the input filter states are passed in and updated by the all-pass filtering + routine and exported in the prefiltdata structure*/ + WebRtcIsac_AllPassFilter2Float(tempin_ch1, WebRtcIsac_kUpperApFactorsFloat, + FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, + prefiltdata->INSTATLA1_float); + WebRtcIsac_AllPassFilter2Float(tempin_ch2, WebRtcIsac_kLowerApFactorsFloat, + FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, + prefiltdata->INSTATLA2_float); + + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + LP_la[k] = (float)(0.5f * (tempin_ch1[k] + tempin_ch2[k])); /*low pass */ + HP_la[k] = (double)(0.5f * (tempin_ch1[k] - tempin_ch2[k])); /* high pass */ + } +} diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.h b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.h new file mode 100644 index 00000000..1aecfc40 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_VAD_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_VAD_H_ + +#include + +#include "modules/audio_coding/codecs/isac/main/source/structs.h" + +void WebRtcIsac_InitPitchFilter(PitchFiltstr* pitchfiltdata); +void WebRtcIsac_InitPitchAnalysis(PitchAnalysisStruct* state); +void WebRtcIsac_InitPreFilterbank(PreFiltBankstr* prefiltdata); + +double WebRtcIsac_LevDurb(double* a, double* k, double* r, size_t order); + +/* The number of all-pass filter factors in an upper or lower channel*/ +#define NUMBEROFCHANNELAPSECTIONS 2 + +/* The upper channel all-pass filter factors */ +extern const float WebRtcIsac_kUpperApFactorsFloat[2]; + +/* The lower channel all-pass filter factors */ +extern const float WebRtcIsac_kLowerApFactorsFloat[2]; + +void WebRtcIsac_AllPassFilter2Float(float* InOut, + const float* APSectionFactors, + int lengthInOut, + int NumberOfSections, + float* FilterState); +void WebRtcIsac_SplitAndFilterFloat(float* in, + float* LP, + float* HP, + double* LP_la, + double* HP_la, + PreFiltBankstr* prefiltdata); + +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_VAD_H_ diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h new file mode 100644 index 00000000..fe9afa4b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_OS_SPECIFIC_INLINE_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_OS_SPECIFIC_INLINE_H_ + +#include + +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_POSIX) +#define WebRtcIsac_lrint lrint +#elif (defined(WEBRTC_ARCH_X86) && defined(WIN32)) +static __inline long int WebRtcIsac_lrint(double x_dbl) { + long int x_int; + + __asm { + fld x_dbl + fistp x_int + } + ; + + return x_int; +} +#else // Do a slow but correct implementation of lrint + +static __inline long int WebRtcIsac_lrint(double x_dbl) { + long int x_int; + x_int = (long int)floor(x_dbl + 0.499999999999); + return x_int; +} + +#endif + +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_OS_SPECIFIC_INLINE_H_ diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c new file mode 100644 index 00000000..157eb195 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c @@ -0,0 +1,737 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" + +#include +#include +#include +#ifdef WEBRTC_ANDROID +#include +#endif + +#include "modules/audio_coding/codecs/isac/main/source/filter_functions.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_filter.h" +#include "rtc_base/system/ignore_warnings.h" + +static const double kInterpolWin[8] = { + -0.00067556028640, 0.02184247643159, -0.12203175715679, 0.60086484101160, + 0.60086484101160, -0.12203175715679, 0.02184247643159, -0.00067556028640}; + +/* interpolation filter */ +__inline static void IntrepolFilter(double* data_ptr, double* intrp) { + *intrp = kInterpolWin[0] * data_ptr[-3]; + *intrp += kInterpolWin[1] * data_ptr[-2]; + *intrp += kInterpolWin[2] * data_ptr[-1]; + *intrp += kInterpolWin[3] * data_ptr[0]; + *intrp += kInterpolWin[4] * data_ptr[1]; + *intrp += kInterpolWin[5] * data_ptr[2]; + *intrp += kInterpolWin[6] * data_ptr[3]; + *intrp += kInterpolWin[7] * data_ptr[4]; +} + +/* 2D parabolic interpolation */ +/* probably some 0.5 factors can be eliminated, and the square-roots can be + * removed from the Cholesky fact. */ +__inline static void Intrpol2D(double T[3][3], + double* x, + double* y, + double* peak_val) { + double c, b[2], A[2][2]; + double t1, t2, d; + double delta1, delta2; + + // double T[3][3] = {{-1.25, -.25,-.25}, {-.25, .75, .75}, {-.25, .75, .75}}; + // should result in: delta1 = 0.5; delta2 = 0.0; peak_val = 1.0 + + c = T[1][1]; + b[0] = 0.5 * (T[1][2] + T[2][1] - T[0][1] - T[1][0]); + b[1] = 0.5 * (T[1][0] + T[2][1] - T[0][1] - T[1][2]); + A[0][1] = -0.5 * (T[0][1] + T[2][1] - T[1][0] - T[1][2]); + t1 = 0.5 * (T[0][0] + T[2][2]) - c; + t2 = 0.5 * (T[2][0] + T[0][2]) - c; + d = (T[0][1] + T[1][2] + T[1][0] + T[2][1]) - 4.0 * c - t1 - t2; + A[0][0] = -t1 - 0.5 * d; + A[1][1] = -t2 - 0.5 * d; + + /* deal with singularities or ill-conditioned cases */ + if ((A[0][0] < 1e-7) || ((A[0][0] * A[1][1] - A[0][1] * A[0][1]) < 1e-7)) { + *peak_val = T[1][1]; + return; + } + + /* Cholesky decomposition: replace A by upper-triangular factor */ + A[0][0] = sqrt(A[0][0]); + A[0][1] = A[0][1] / A[0][0]; + A[1][1] = sqrt(A[1][1] - A[0][1] * A[0][1]); + + /* compute [x; y] = -0.5 * inv(A) * b */ + t1 = b[0] / A[0][0]; + t2 = (b[1] - t1 * A[0][1]) / A[1][1]; + delta2 = t2 / A[1][1]; + delta1 = 0.5 * (t1 - delta2 * A[0][1]) / A[0][0]; + delta2 *= 0.5; + + /* limit norm */ + t1 = delta1 * delta1 + delta2 * delta2; + if (t1 > 1.0) { + delta1 /= t1; + delta2 /= t1; + } + + *peak_val = 0.5 * (b[0] * delta1 + b[1] * delta2) + c; + + *x += delta1; + *y += delta2; +} + +static void PCorr(const double* in, double* outcorr) { + double sum, ysum, prod; + const double *x, *inptr; + int k, n; + + // ysum = 1e-6; /* use this with float (i.s.o. double)! */ + ysum = 1e-13; + sum = 0.0; + x = in + PITCH_MAX_LAG / 2 + 2; + for (n = 0; n < PITCH_CORR_LEN2; n++) { + ysum += in[n] * in[n]; + sum += x[n] * in[n]; + } + + outcorr += PITCH_LAG_SPAN2 - 1; /* index of last element in array */ + *outcorr = sum / sqrt(ysum); + + for (k = 1; k < PITCH_LAG_SPAN2; k++) { + ysum -= in[k - 1] * in[k - 1]; + ysum += in[PITCH_CORR_LEN2 + k - 1] * in[PITCH_CORR_LEN2 + k - 1]; + sum = 0.0; + inptr = &in[k]; + prod = x[0] * inptr[0]; + for (n = 1; n < PITCH_CORR_LEN2; n++) { + sum += prod; + prod = x[n] * inptr[n]; + } + sum += prod; + outcorr--; + *outcorr = sum / sqrt(ysum); + } +} + +static void WebRtcIsac_AllpassFilterForDec(double* InOut, + const double* APSectionFactors, + size_t lengthInOut, + double* FilterState) { + // This performs all-pass filtering--a series of first order all-pass + // sections are used to filter the input in a cascade manner. + size_t n, j; + double temp; + for (j = 0; j < ALLPASSSECTIONS; j++) { + for (n = 0; n < lengthInOut; n += 2) { + temp = InOut[n]; // store input + InOut[n] = FilterState[j] + APSectionFactors[j] * temp; + FilterState[j] = -APSectionFactors[j] * InOut[n] + temp; + } + } +} + +static void WebRtcIsac_DecimateAllpass( + const double* in, + double* state_in, // array of size: 2*ALLPASSSECTIONS+1 + size_t N, // number of input samples + double* out) { // array of size N/2 + + static const double APupper[ALLPASSSECTIONS] = {0.0347, 0.3826}; + static const double APlower[ALLPASSSECTIONS] = {0.1544, 0.744}; + + size_t n; + double data_vec[PITCH_FRAME_LEN]; + + /* copy input */ + memcpy(data_vec + 1, in, sizeof(double) * (N - 1)); + + data_vec[0] = state_in[2 * ALLPASSSECTIONS]; // the z^(-1) state + state_in[2 * ALLPASSSECTIONS] = in[N - 1]; + + WebRtcIsac_AllpassFilterForDec(data_vec + 1, APupper, N, state_in); + WebRtcIsac_AllpassFilterForDec(data_vec, APlower, N, + state_in + ALLPASSSECTIONS); + + for (n = 0; n < N / 2; n++) + out[n] = data_vec[2 * n] + data_vec[2 * n + 1]; +} + +RTC_PUSH_IGNORING_WFRAME_LARGER_THAN() + +static void WebRtcIsac_InitializePitch(const double* in, + const double old_lag, + const double old_gain, + PitchAnalysisStruct* State, + double* lags) { + double buf_dec[PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + PITCH_MAX_LAG / 2 + 2]; + double ratio, log_lag, gain_bias; + double bias; + double corrvec1[PITCH_LAG_SPAN2]; + double corrvec2[PITCH_LAG_SPAN2]; + int m, k; + // Allocating 10 extra entries at the begining of the CorrSurf + double corrSurfBuff[10 + (2 * PITCH_BW + 3) * (PITCH_LAG_SPAN2 + 4)]; + double* CorrSurf[2 * PITCH_BW + 3]; + double *CorrSurfPtr1, *CorrSurfPtr2; + double LagWin[3] = {0.2, 0.5, 0.98}; + int ind1, ind2, peaks_ind, peak, max_ind; + int peaks[PITCH_MAX_NUM_PEAKS]; + double adj, gain_tmp; + double corr, corr_max; + double intrp_a, intrp_b, intrp_c, intrp_d; + double peak_vals[PITCH_MAX_NUM_PEAKS]; + double lags1[PITCH_MAX_NUM_PEAKS]; + double lags2[PITCH_MAX_NUM_PEAKS]; + double T[3][3]; + int row; + + for (k = 0; k < 2 * PITCH_BW + 3; k++) { + CorrSurf[k] = &corrSurfBuff[10 + k * (PITCH_LAG_SPAN2 + 4)]; + } + /* reset CorrSurf matrix */ + memset(corrSurfBuff, 0, + sizeof(double) * (10 + (2 * PITCH_BW + 3) * (PITCH_LAG_SPAN2 + 4))); + + // warnings -DH + max_ind = 0; + peak = 0; + + /* copy old values from state buffer */ + memcpy(buf_dec, State->dec_buffer, + sizeof(double) * (PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + + PITCH_MAX_LAG / 2 - PITCH_FRAME_LEN / 2 + 2)); + + /* decimation; put result after the old values */ + WebRtcIsac_DecimateAllpass( + in, State->decimator_state, PITCH_FRAME_LEN, + &buf_dec[PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + PITCH_MAX_LAG / 2 - + PITCH_FRAME_LEN / 2 + 2]); + + /* low-pass filtering */ + for (k = PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + PITCH_MAX_LAG / 2 - + PITCH_FRAME_LEN / 2 + 2; + k < PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + PITCH_MAX_LAG / 2 + 2; k++) + buf_dec[k] += 0.75 * buf_dec[k - 1] - 0.25 * buf_dec[k - 2]; + + /* copy end part back into state buffer */ + memcpy(State->dec_buffer, buf_dec + PITCH_FRAME_LEN / 2, + sizeof(double) * (PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + + PITCH_MAX_LAG / 2 - PITCH_FRAME_LEN / 2 + 2)); + + /* compute correlation for first and second half of the frame */ + PCorr(buf_dec, corrvec1); + PCorr(buf_dec + PITCH_CORR_STEP2, corrvec2); + + /* bias towards pitch lag of previous frame */ + log_lag = log(0.5 * old_lag); + gain_bias = 4.0 * old_gain * old_gain; + if (gain_bias > 0.8) + gain_bias = 0.8; + for (k = 0; k < PITCH_LAG_SPAN2; k++) { + ratio = log((double)(k + (PITCH_MIN_LAG / 2 - 2))) - log_lag; + bias = 1.0 + gain_bias * exp(-5.0 * ratio * ratio); + corrvec1[k] *= bias; + } + + /* taper correlation functions */ + for (k = 0; k < 3; k++) { + gain_tmp = LagWin[k]; + corrvec1[k] *= gain_tmp; + corrvec2[k] *= gain_tmp; + corrvec1[PITCH_LAG_SPAN2 - 1 - k] *= gain_tmp; + corrvec2[PITCH_LAG_SPAN2 - 1 - k] *= gain_tmp; + } + + corr_max = 0.0; + /* fill middle row of correlation surface */ + ind1 = 0; + ind2 = 0; + CorrSurfPtr1 = &CorrSurf[PITCH_BW][2]; + for (k = 0; k < PITCH_LAG_SPAN2; k++) { + corr = corrvec1[ind1++] + corrvec2[ind2++]; + CorrSurfPtr1[k] = corr; + if (corr > corr_max) { + corr_max = corr; /* update maximum */ + max_ind = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); + } + } + /* fill first and last rows of correlation surface */ + ind1 = 0; + ind2 = PITCH_BW; + CorrSurfPtr1 = &CorrSurf[0][2]; + CorrSurfPtr2 = &CorrSurf[2 * PITCH_BW][PITCH_BW + 2]; + for (k = 0; k < PITCH_LAG_SPAN2 - PITCH_BW; k++) { + ratio = ((double)(ind1 + 12)) / ((double)(ind2 + 12)); + adj = 0.2 * ratio * (2.0 - ratio); /* adjustment factor; inverse parabola as + a function of ratio */ + corr = adj * (corrvec1[ind1] + corrvec2[ind2]); + CorrSurfPtr1[k] = corr; + if (corr > corr_max) { + corr_max = corr; /* update maximum */ + max_ind = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); + } + corr = adj * (corrvec1[ind2++] + corrvec2[ind1++]); + CorrSurfPtr2[k] = corr; + if (corr > corr_max) { + corr_max = corr; /* update maximum */ + max_ind = (int)(&CorrSurfPtr2[k] - &CorrSurf[0][0]); + } + } + /* fill second and next to last rows of correlation surface */ + ind1 = 0; + ind2 = PITCH_BW - 1; + CorrSurfPtr1 = &CorrSurf[1][2]; + CorrSurfPtr2 = &CorrSurf[2 * PITCH_BW - 1][PITCH_BW + 1]; + for (k = 0; k < PITCH_LAG_SPAN2 - PITCH_BW + 1; k++) { + ratio = ((double)(ind1 + 12)) / ((double)(ind2 + 12)); + adj = 0.9 * ratio * (2.0 - ratio); /* adjustment factor; inverse parabola as + a function of ratio */ + corr = adj * (corrvec1[ind1] + corrvec2[ind2]); + CorrSurfPtr1[k] = corr; + if (corr > corr_max) { + corr_max = corr; /* update maximum */ + max_ind = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); + } + corr = adj * (corrvec1[ind2++] + corrvec2[ind1++]); + CorrSurfPtr2[k] = corr; + if (corr > corr_max) { + corr_max = corr; /* update maximum */ + max_ind = (int)(&CorrSurfPtr2[k] - &CorrSurf[0][0]); + } + } + /* fill remainder of correlation surface */ + for (m = 2; m < PITCH_BW; m++) { + ind1 = 0; + ind2 = PITCH_BW - m; /* always larger than ind1 */ + CorrSurfPtr1 = &CorrSurf[m][2]; + CorrSurfPtr2 = &CorrSurf[2 * PITCH_BW - m][PITCH_BW + 2 - m]; + for (k = 0; k < PITCH_LAG_SPAN2 - PITCH_BW + m; k++) { + ratio = ((double)(ind1 + 12)) / ((double)(ind2 + 12)); + adj = ratio * (2.0 - ratio); /* adjustment factor; inverse parabola as a + function of ratio */ + corr = adj * (corrvec1[ind1] + corrvec2[ind2]); + CorrSurfPtr1[k] = corr; + if (corr > corr_max) { + corr_max = corr; /* update maximum */ + max_ind = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); + } + corr = adj * (corrvec1[ind2++] + corrvec2[ind1++]); + CorrSurfPtr2[k] = corr; + if (corr > corr_max) { + corr_max = corr; /* update maximum */ + max_ind = (int)(&CorrSurfPtr2[k] - &CorrSurf[0][0]); + } + } + } + + /* threshold value to qualify as a peak */ + corr_max *= 0.6; + + peaks_ind = 0; + /* find peaks */ + for (m = 1; m < PITCH_BW + 1; m++) { + if (peaks_ind == PITCH_MAX_NUM_PEAKS) + break; + CorrSurfPtr1 = &CorrSurf[m][2]; + for (k = 2; k < PITCH_LAG_SPAN2 - PITCH_BW - 2 + m; k++) { + corr = CorrSurfPtr1[k]; + if (corr > corr_max) { + if ((corr > CorrSurfPtr1[k - (PITCH_LAG_SPAN2 + 5)]) && + (corr > CorrSurfPtr1[k - (PITCH_LAG_SPAN2 + 4)])) { + if ((corr > CorrSurfPtr1[k + (PITCH_LAG_SPAN2 + 4)]) && + (corr > CorrSurfPtr1[k + (PITCH_LAG_SPAN2 + 5)])) { + /* found a peak; store index into matrix */ + peaks[peaks_ind++] = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); + if (peaks_ind == PITCH_MAX_NUM_PEAKS) + break; + } + } + } + } + } + for (m = PITCH_BW + 1; m < 2 * PITCH_BW; m++) { + if (peaks_ind == PITCH_MAX_NUM_PEAKS) + break; + CorrSurfPtr1 = &CorrSurf[m][2]; + for (k = 2 + m - PITCH_BW; k < PITCH_LAG_SPAN2 - 2; k++) { + corr = CorrSurfPtr1[k]; + if (corr > corr_max) { + if ((corr > CorrSurfPtr1[k - (PITCH_LAG_SPAN2 + 5)]) && + (corr > CorrSurfPtr1[k - (PITCH_LAG_SPAN2 + 4)])) { + if ((corr > CorrSurfPtr1[k + (PITCH_LAG_SPAN2 + 4)]) && + (corr > CorrSurfPtr1[k + (PITCH_LAG_SPAN2 + 5)])) { + /* found a peak; store index into matrix */ + peaks[peaks_ind++] = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); + if (peaks_ind == PITCH_MAX_NUM_PEAKS) + break; + } + } + } + } + } + + if (peaks_ind > 0) { + /* examine each peak */ + CorrSurfPtr1 = &CorrSurf[0][0]; + for (k = 0; k < peaks_ind; k++) { + peak = peaks[k]; + + /* compute four interpolated values around current peak */ + IntrepolFilter(&CorrSurfPtr1[peak - (PITCH_LAG_SPAN2 + 5)], &intrp_a); + IntrepolFilter(&CorrSurfPtr1[peak - 1], &intrp_b); + IntrepolFilter(&CorrSurfPtr1[peak], &intrp_c); + IntrepolFilter(&CorrSurfPtr1[peak + (PITCH_LAG_SPAN2 + 4)], &intrp_d); + + /* determine maximum of the interpolated values */ + corr = CorrSurfPtr1[peak]; + corr_max = intrp_a; + if (intrp_b > corr_max) + corr_max = intrp_b; + if (intrp_c > corr_max) + corr_max = intrp_c; + if (intrp_d > corr_max) + corr_max = intrp_d; + + /* determine where the peak sits and fill a 3x3 matrix around it */ + row = peak / (PITCH_LAG_SPAN2 + 4); + lags1[k] = (double)((peak - row * (PITCH_LAG_SPAN2 + 4)) + + PITCH_MIN_LAG / 2 - 4); + lags2[k] = (double)(lags1[k] + PITCH_BW - row); + if (corr > corr_max) { + T[0][0] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2 + 5)]; + T[2][0] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2 + 4)]; + T[1][1] = corr; + T[0][2] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2 + 4)]; + T[2][2] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2 + 5)]; + T[1][0] = intrp_a; + T[0][1] = intrp_b; + T[2][1] = intrp_c; + T[1][2] = intrp_d; + } else { + if (intrp_a == corr_max) { + lags1[k] -= 0.5; + lags2[k] += 0.5; + IntrepolFilter(&CorrSurfPtr1[peak - 2 * (PITCH_LAG_SPAN2 + 5)], + &T[0][0]); + IntrepolFilter(&CorrSurfPtr1[peak - (2 * PITCH_LAG_SPAN2 + 9)], + &T[2][0]); + T[1][1] = intrp_a; + T[0][2] = intrp_b; + T[2][2] = intrp_c; + T[1][0] = CorrSurfPtr1[peak - (2 * PITCH_LAG_SPAN2 + 9)]; + T[0][1] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2 + 5)]; + T[2][1] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2 + 4)]; + T[1][2] = corr; + } else if (intrp_b == corr_max) { + lags1[k] -= 0.5; + lags2[k] -= 0.5; + IntrepolFilter(&CorrSurfPtr1[peak - (PITCH_LAG_SPAN2 + 6)], &T[0][0]); + T[2][0] = intrp_a; + T[1][1] = intrp_b; + IntrepolFilter(&CorrSurfPtr1[peak + (PITCH_LAG_SPAN2 + 3)], &T[0][2]); + T[2][2] = intrp_d; + T[1][0] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2 + 5)]; + T[0][1] = CorrSurfPtr1[peak - 1]; + T[2][1] = corr; + T[1][2] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2 + 4)]; + } else if (intrp_c == corr_max) { + lags1[k] += 0.5; + lags2[k] += 0.5; + T[0][0] = intrp_a; + IntrepolFilter(&CorrSurfPtr1[peak - (PITCH_LAG_SPAN2 + 4)], &T[2][0]); + T[1][1] = intrp_c; + T[0][2] = intrp_d; + IntrepolFilter(&CorrSurfPtr1[peak + (PITCH_LAG_SPAN2 + 5)], &T[2][2]); + T[1][0] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2 + 4)]; + T[0][1] = corr; + T[2][1] = CorrSurfPtr1[peak + 1]; + T[1][2] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2 + 5)]; + } else { + lags1[k] += 0.5; + lags2[k] -= 0.5; + T[0][0] = intrp_b; + T[2][0] = intrp_c; + T[1][1] = intrp_d; + IntrepolFilter(&CorrSurfPtr1[peak + 2 * (PITCH_LAG_SPAN2 + 4)], + &T[0][2]); + IntrepolFilter(&CorrSurfPtr1[peak + (2 * PITCH_LAG_SPAN2 + 9)], + &T[2][2]); + T[1][0] = corr; + T[0][1] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2 + 4)]; + T[2][1] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2 + 5)]; + T[1][2] = CorrSurfPtr1[peak + (2 * PITCH_LAG_SPAN2 + 9)]; + } + } + + /* 2D parabolic interpolation gives more accurate lags and peak value */ + Intrpol2D(T, &lags1[k], &lags2[k], &peak_vals[k]); + } + + /* determine the highest peak, after applying a bias towards short lags */ + corr_max = 0.0; + for (k = 0; k < peaks_ind; k++) { + corr = peak_vals[k] * pow(PITCH_PEAK_DECAY, log(lags1[k] + lags2[k])); + if (corr > corr_max) { + corr_max = corr; + peak = k; + } + } + + lags1[peak] *= 2.0; + lags2[peak] *= 2.0; + + if (lags1[peak] < (double)PITCH_MIN_LAG) + lags1[peak] = (double)PITCH_MIN_LAG; + if (lags2[peak] < (double)PITCH_MIN_LAG) + lags2[peak] = (double)PITCH_MIN_LAG; + if (lags1[peak] > (double)PITCH_MAX_LAG) + lags1[peak] = (double)PITCH_MAX_LAG; + if (lags2[peak] > (double)PITCH_MAX_LAG) + lags2[peak] = (double)PITCH_MAX_LAG; + + /* store lags of highest peak in output array */ + lags[0] = lags1[peak]; + lags[1] = lags1[peak]; + lags[2] = lags2[peak]; + lags[3] = lags2[peak]; + } else { + row = max_ind / (PITCH_LAG_SPAN2 + 4); + lags1[0] = (double)((max_ind - row * (PITCH_LAG_SPAN2 + 4)) + + PITCH_MIN_LAG / 2 - 4); + lags2[0] = (double)(lags1[0] + PITCH_BW - row); + + if (lags1[0] < (double)PITCH_MIN_LAG) + lags1[0] = (double)PITCH_MIN_LAG; + if (lags2[0] < (double)PITCH_MIN_LAG) + lags2[0] = (double)PITCH_MIN_LAG; + if (lags1[0] > (double)PITCH_MAX_LAG) + lags1[0] = (double)PITCH_MAX_LAG; + if (lags2[0] > (double)PITCH_MAX_LAG) + lags2[0] = (double)PITCH_MAX_LAG; + + /* store lags of highest peak in output array */ + lags[0] = lags1[0]; + lags[1] = lags1[0]; + lags[2] = lags2[0]; + lags[3] = lags2[0]; + } +} + +RTC_POP_IGNORING_WFRAME_LARGER_THAN() + +/* create weighting matrix by orthogonalizing a basis of polynomials of + * increasing order t = (0:4)'; A = [t.^0, t.^1, t.^2, t.^3, t.^4]; [Q, dummy] = + * qr(A); P.Weight = Q * diag([0, .1, .5, 1, 1]) * Q'; */ +static const double kWeight[5][5] = { + {0.29714285714286, -0.30857142857143, -0.05714285714286, 0.05142857142857, + 0.01714285714286}, + {-0.30857142857143, 0.67428571428571, -0.27142857142857, -0.14571428571429, + 0.05142857142857}, + {-0.05714285714286, -0.27142857142857, 0.65714285714286, -0.27142857142857, + -0.05714285714286}, + {0.05142857142857, -0.14571428571429, -0.27142857142857, 0.67428571428571, + -0.30857142857143}, + {0.01714285714286, 0.05142857142857, -0.05714285714286, -0.30857142857143, + 0.29714285714286}}; + +/* second order high-pass filter */ +static void WebRtcIsac_Highpass(const double* in, + double* out, + double* state, + size_t N) { + /* create high-pass filter ocefficients + * z = 0.998 * exp(j*2*pi*35/8000); + * p = 0.94 * exp(j*2*pi*140/8000); + * HP_b = [1, -2*real(z), abs(z)^2]; + * HP_a = [1, -2*real(p), abs(p)^2]; */ + static const double a_coef[2] = {1.86864659625574, -0.88360000000000}; + static const double b_coef[2] = {-1.99524591718270, 0.99600400000000}; + + size_t k; + + for (k = 0; k < N; k++) { + *out = *in + state[1]; + state[1] = state[0] + b_coef[0] * *in + a_coef[0] * *out; + state[0] = b_coef[1] * *in++ + a_coef[1] * *out++; + } +} + +RTC_PUSH_IGNORING_WFRAME_LARGER_THAN() + +void WebRtcIsac_PitchAnalysis( + const double* in, /* PITCH_FRAME_LEN samples */ + double* out, /* PITCH_FRAME_LEN+QLOOKAHEAD samples */ + PitchAnalysisStruct* State, + double* lags, + double* gains) { + double HPin[PITCH_FRAME_LEN]; + double Weighted[PITCH_FRAME_LEN]; + double Whitened[PITCH_FRAME_LEN + QLOOKAHEAD]; + double inbuf[PITCH_FRAME_LEN + QLOOKAHEAD]; + double out_G[PITCH_FRAME_LEN + + QLOOKAHEAD]; // could be removed by using out instead + double out_dG[4][PITCH_FRAME_LEN + QLOOKAHEAD]; + double old_lag, old_gain; + double nrg_wht, tmp; + double Wnrg, Wfluct, Wgain; + double H[4][4]; + double grad[4]; + double dG[4]; + int k, m, n, iter; + + /* high pass filtering using second order pole-zero filter */ + WebRtcIsac_Highpass(in, HPin, State->hp_state, PITCH_FRAME_LEN); + + /* copy from state into buffer */ + memcpy(Whitened, State->whitened_buf, sizeof(double) * QLOOKAHEAD); + + /* compute weighted and whitened signals */ + WebRtcIsac_WeightingFilter(HPin, &Weighted[0], &Whitened[QLOOKAHEAD], + &(State->Wghtstr)); + + /* copy from buffer into state */ + memcpy(State->whitened_buf, Whitened + PITCH_FRAME_LEN, + sizeof(double) * QLOOKAHEAD); + + old_lag = State->PFstr_wght.oldlagp[0]; + old_gain = State->PFstr_wght.oldgainp[0]; + + /* inital pitch estimate */ + WebRtcIsac_InitializePitch(Weighted, old_lag, old_gain, State, lags); + + /* Iterative optimization of lags - to be done */ + + /* compute energy of whitened signal */ + nrg_wht = 0.0; + for (k = 0; k < PITCH_FRAME_LEN + QLOOKAHEAD; k++) + nrg_wht += Whitened[k] * Whitened[k]; + + /* Iterative optimization of gains */ + + /* set weights for energy, gain fluctiation, and spectral gain penalty + * functions */ + Wnrg = 1.0 / nrg_wht; + Wgain = 0.005; + Wfluct = 3.0; + + /* set initial gains */ + for (k = 0; k < 4; k++) + gains[k] = PITCH_MAX_GAIN_06; + + /* two iterations should be enough */ + for (iter = 0; iter < 2; iter++) { + /* compute Jacobian of pre-filter output towards gains */ + WebRtcIsac_PitchfilterPre_gains(Whitened, out_G, out_dG, + &(State->PFstr_wght), lags, gains); + + /* gradient and approximate Hessian (lower triangle) for minimizing the + * filter's output power */ + for (k = 0; k < 4; k++) { + tmp = 0.0; + for (n = 0; n < PITCH_FRAME_LEN + QLOOKAHEAD; n++) + tmp += out_G[n] * out_dG[k][n]; + grad[k] = tmp * Wnrg; + } + for (k = 0; k < 4; k++) { + for (m = 0; m <= k; m++) { + tmp = 0.0; + for (n = 0; n < PITCH_FRAME_LEN + QLOOKAHEAD; n++) + tmp += out_dG[m][n] * out_dG[k][n]; + H[k][m] = tmp * Wnrg; + } + } + + /* add gradient and Hessian (lower triangle) for dampening fast gain changes + */ + for (k = 0; k < 4; k++) { + tmp = kWeight[k + 1][0] * old_gain; + for (m = 0; m < 4; m++) + tmp += kWeight[k + 1][m + 1] * gains[m]; + grad[k] += tmp * Wfluct; + } + for (k = 0; k < 4; k++) { + for (m = 0; m <= k; m++) { + H[k][m] += kWeight[k + 1][m + 1] * Wfluct; + } + } + + /* add gradient and Hessian for dampening gain */ + for (k = 0; k < 3; k++) { + tmp = 1.0 / (1 - gains[k]); + grad[k] += tmp * tmp * Wgain; + H[k][k] += 2.0 * tmp * (tmp * tmp * Wgain); + } + tmp = 1.0 / (1 - gains[3]); + grad[3] += 1.33 * (tmp * tmp * Wgain); + H[3][3] += 2.66 * tmp * (tmp * tmp * Wgain); + + /* compute Cholesky factorization of Hessian + * by overwritting the upper triangle; scale factors on diagonal + * (for non pc-platforms store the inverse of the diagonals seperately to + * minimize divisions) */ + H[0][1] = H[1][0] / H[0][0]; + H[0][2] = H[2][0] / H[0][0]; + H[0][3] = H[3][0] / H[0][0]; + H[1][1] -= H[0][0] * H[0][1] * H[0][1]; + H[1][2] = (H[2][1] - H[0][1] * H[2][0]) / H[1][1]; + H[1][3] = (H[3][1] - H[0][1] * H[3][0]) / H[1][1]; + H[2][2] -= H[0][0] * H[0][2] * H[0][2] + H[1][1] * H[1][2] * H[1][2]; + H[2][3] = + (H[3][2] - H[0][2] * H[3][0] - H[1][2] * H[1][1] * H[1][3]) / H[2][2]; + H[3][3] -= H[0][0] * H[0][3] * H[0][3] + H[1][1] * H[1][3] * H[1][3] + + H[2][2] * H[2][3] * H[2][3]; + + /* Compute update as delta_gains = -inv(H) * grad */ + /* copy and negate */ + for (k = 0; k < 4; k++) + dG[k] = -grad[k]; + /* back substitution */ + dG[1] -= dG[0] * H[0][1]; + dG[2] -= dG[0] * H[0][2] + dG[1] * H[1][2]; + dG[3] -= dG[0] * H[0][3] + dG[1] * H[1][3] + dG[2] * H[2][3]; + /* scale */ + for (k = 0; k < 4; k++) + dG[k] /= H[k][k]; + /* back substitution */ + dG[2] -= dG[3] * H[2][3]; + dG[1] -= dG[3] * H[1][3] + dG[2] * H[1][2]; + dG[0] -= dG[3] * H[0][3] + dG[2] * H[0][2] + dG[1] * H[0][1]; + + /* update gains and check range */ + for (k = 0; k < 4; k++) { + gains[k] += dG[k]; + if (gains[k] > PITCH_MAX_GAIN) + gains[k] = PITCH_MAX_GAIN; + else if (gains[k] < 0.0) + gains[k] = 0.0; + } + } + + /* update state for next frame */ + WebRtcIsac_PitchfilterPre(Whitened, out, &(State->PFstr_wght), lags, gains); + + /* concatenate previous input's end and current input */ + memcpy(inbuf, State->inbuf, sizeof(double) * QLOOKAHEAD); + memcpy(inbuf + QLOOKAHEAD, in, sizeof(double) * PITCH_FRAME_LEN); + + /* lookahead pitch filtering for masking analysis */ + WebRtcIsac_PitchfilterPre_la(inbuf, out, &(State->PFstr), lags, gains); + + /* store last part of input */ + for (k = 0; k < QLOOKAHEAD; k++) + State->inbuf[k] = inbuf[k + PITCH_FRAME_LEN]; +} + +RTC_POP_IGNORING_WFRAME_LARGER_THAN() diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h new file mode 100644 index 00000000..4ab78c20 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * pitch_estimator.h + * + * Pitch functions + * + */ + +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ + +#include + +#include "modules/audio_coding/codecs/isac/main/source/structs.h" + +void WebRtcIsac_PitchAnalysis( + const double* in, /* PITCH_FRAME_LEN samples */ + double* out, /* PITCH_FRAME_LEN+QLOOKAHEAD samples */ + PitchAnalysisStruct* State, + double* lags, + double* gains); + +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ */ diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.c b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.c new file mode 100644 index 00000000..494b5b7b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.c @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include + +#include "modules/audio_coding/codecs/isac/main/source/os_specific_inline.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" +#include "rtc_base/compile_assert_c.h" + +/* + * We are implementing the following filters; + * + * Pre-filtering: + * y(z) = x(z) + damper(z) * gain * (x(z) + y(z)) * z ^ (-lag); + * + * Post-filtering: + * y(z) = x(z) - damper(z) * gain * (x(z) + y(z)) * z ^ (-lag); + * + * Note that `lag` is a floating number so we perform an interpolation to + * obtain the correct `lag`. + * + */ + +static const double kDampFilter[PITCH_DAMPORDER] = {-0.07, 0.25, 0.64, 0.25, + -0.07}; + +/* interpolation coefficients; generated by design_pitch_filter.m */ +static const double kIntrpCoef[PITCH_FRACS][PITCH_FRACORDER] = { + {-0.02239172458614, 0.06653315052934, -0.16515880017569, 0.60701333734125, + 0.64671399919202, -0.20249000396417, 0.09926548334755, -0.04765933793109, + 0.01754159521746}, + {-0.01985640750434, 0.05816126837866, -0.13991265473714, 0.44560418147643, + 0.79117042386876, -0.20266133815188, 0.09585268418555, -0.04533310458084, + 0.01654127246314}, + {-0.01463300534216, 0.04229888475060, -0.09897034715253, 0.28284326017787, + 0.90385267956632, -0.16976950138649, 0.07704272393639, -0.03584218578311, + 0.01295781500709}, + {-0.00764851320885, 0.02184035544377, -0.04985561057281, 0.13083306574393, + 0.97545011664662, -0.10177807997561, 0.04400901776474, -0.02010737175166, + 0.00719783432422}, + {-0.00000000000000, 0.00000000000000, -0.00000000000001, 0.00000000000001, + 0.99999999999999, 0.00000000000001, -0.00000000000001, 0.00000000000000, + -0.00000000000000}, + {0.00719783432422, -0.02010737175166, 0.04400901776474, -0.10177807997562, + 0.97545011664663, 0.13083306574393, -0.04985561057280, 0.02184035544377, + -0.00764851320885}, + {0.01295781500710, -0.03584218578312, 0.07704272393640, -0.16976950138650, + 0.90385267956634, 0.28284326017785, -0.09897034715252, 0.04229888475059, + -0.01463300534216}, + {0.01654127246315, -0.04533310458085, 0.09585268418557, -0.20266133815190, + 0.79117042386878, 0.44560418147640, -0.13991265473712, 0.05816126837865, + -0.01985640750433}}; + +/* + * Enumerating the operation of the filter. + * iSAC has 4 different pitch-filter which are very similar in their structure. + * + * kPitchFilterPre : In this mode the filter is operating as pitch + * pre-filter. This is used at the encoder. + * kPitchFilterPost : In this mode the filter is operating as pitch + * post-filter. This is the inverse of pre-filter and used + * in the decoder. + * kPitchFilterPreLa : This is, in structure, similar to pre-filtering but + * utilizing 3 millisecond lookahead. It is used to + * obtain the signal for LPC analysis. + * kPitchFilterPreGain : This is, in structure, similar to pre-filtering but + * differential changes in gain is considered. This is + * used to find the optimal gain. + */ +typedef enum { + kPitchFilterPre, + kPitchFilterPost, + kPitchFilterPreLa, + kPitchFilterPreGain +} PitchFilterOperation; + +/* + * Structure with parameters used for pitch-filtering. + * buffer : a buffer where the sum of previous inputs and outputs + * are stored. + * damper_state : the state of the damping filter. The filter is defined by + * `kDampFilter`. + * interpol_coeff : pointer to a set of coefficient which are used to utilize + * fractional pitch by interpolation. + * gain : pitch-gain to be applied to the current segment of input. + * lag : pitch-lag for the current segment of input. + * lag_offset : the offset of lag w.r.t. current sample. + * sub_frame : sub-frame index, there are 4 pitch sub-frames in an iSAC + * frame. + * This specifies the usage of the filter. See + * 'PitchFilterOperation' for operational modes. + * num_samples : number of samples to be processed in each segment. + * index : index of the input and output sample. + * damper_state_dg : state of damping filter for different trial gains. + * gain_mult : differential changes to gain. + */ +typedef struct { + double buffer[PITCH_INTBUFFSIZE + QLOOKAHEAD]; + double damper_state[PITCH_DAMPORDER]; + const double* interpol_coeff; + double gain; + double lag; + int lag_offset; + + int sub_frame; + PitchFilterOperation mode; + int num_samples; + int index; + + double damper_state_dg[4][PITCH_DAMPORDER]; + double gain_mult[4]; +} PitchFilterParam; + +/********************************************************************** + * FilterSegment() + * Filter one segment, a quarter of a frame. + * + * Inputs + * in_data : pointer to the input signal of 30 ms at 8 kHz sample-rate. + * filter_param : pitch filter parameters. + * + * Outputs + * out_data : pointer to a buffer where the filtered signal is written to. + * out_dg : [only used in kPitchFilterPreGain] pointer to a buffer + * where the output of different gain values (differential + * change to gain) is written. + */ +static void FilterSegment(const double* in_data, + PitchFilterParam* parameters, + double* out_data, + double out_dg[][PITCH_FRAME_LEN + QLOOKAHEAD]) { + int n; + int m; + int j; + double sum; + double sum2; + /* Index of `parameters->buffer` where the output is written to. */ + int pos = parameters->index + PITCH_BUFFSIZE; + /* Index of `parameters->buffer` where samples are read for fractional-lag + * computation. */ + int pos_lag = pos - parameters->lag_offset; + + for (n = 0; n < parameters->num_samples; ++n) { + /* Shift low pass filter states. */ + for (m = PITCH_DAMPORDER - 1; m > 0; --m) { + parameters->damper_state[m] = parameters->damper_state[m - 1]; + } + /* Filter to get fractional pitch. */ + sum = 0.0; + for (m = 0; m < PITCH_FRACORDER; ++m) { + sum += parameters->buffer[pos_lag + m] * parameters->interpol_coeff[m]; + } + /* Multiply with gain. */ + parameters->damper_state[0] = parameters->gain * sum; + + if (parameters->mode == kPitchFilterPreGain) { + int lag_index = parameters->index - parameters->lag_offset; + int m_tmp = (lag_index < 0) ? -lag_index : 0; + /* Update the damper state for the new sample. */ + for (m = PITCH_DAMPORDER - 1; m > 0; --m) { + for (j = 0; j < 4; ++j) { + parameters->damper_state_dg[j][m] = + parameters->damper_state_dg[j][m - 1]; + } + } + + for (j = 0; j < parameters->sub_frame + 1; ++j) { + /* Filter for fractional pitch. */ + sum2 = 0.0; + for (m = PITCH_FRACORDER - 1; m >= m_tmp; --m) { + /* `lag_index + m` is always larger than or equal to zero, see how + * m_tmp is computed. This is equivalent to assume samples outside + * `out_dg[j]` are zero. */ + sum2 += out_dg[j][lag_index + m] * parameters->interpol_coeff[m]; + } + /* Add the contribution of differential gain change. */ + parameters->damper_state_dg[j][0] = + parameters->gain_mult[j] * sum + parameters->gain * sum2; + } + + /* Filter with damping filter, and store the results. */ + for (j = 0; j < parameters->sub_frame + 1; ++j) { + sum = 0.0; + for (m = 0; m < PITCH_DAMPORDER; ++m) { + sum -= parameters->damper_state_dg[j][m] * kDampFilter[m]; + } + out_dg[j][parameters->index] = sum; + } + } + /* Filter with damping filter. */ + sum = 0.0; + for (m = 0; m < PITCH_DAMPORDER; ++m) { + sum += parameters->damper_state[m] * kDampFilter[m]; + } + + /* Subtract from input and update buffer. */ + out_data[parameters->index] = in_data[parameters->index] - sum; + parameters->buffer[pos] = + in_data[parameters->index] + out_data[parameters->index]; + + ++parameters->index; + ++pos; + ++pos_lag; + } + return; +} + +/* Update filter parameters based on the pitch-gains and pitch-lags. */ +static void Update(PitchFilterParam* parameters) { + double fraction; + int fraction_index; + /* Compute integer lag-offset. */ + parameters->lag_offset = + WebRtcIsac_lrint(parameters->lag + PITCH_FILTDELAY + 0.5); + /* Find correct set of coefficients for computing fractional pitch. */ + fraction = parameters->lag_offset - (parameters->lag + PITCH_FILTDELAY); + fraction_index = WebRtcIsac_lrint(PITCH_FRACS * fraction - 0.5); + parameters->interpol_coeff = kIntrpCoef[fraction_index]; + + if (parameters->mode == kPitchFilterPreGain) { + /* If in this mode make a differential change to pitch gain. */ + parameters->gain_mult[parameters->sub_frame] += 0.2; + if (parameters->gain_mult[parameters->sub_frame] > 1.0) { + parameters->gain_mult[parameters->sub_frame] = 1.0; + } + if (parameters->sub_frame > 0) { + parameters->gain_mult[parameters->sub_frame - 1] -= 0.2; + } + } +} + +/****************************************************************************** + * FilterFrame() + * Filter a frame of 30 millisecond, given pitch-lags and pitch-gains. + * + * Inputs + * in_data : pointer to the input signal of 30 ms at 8 kHz sample-rate. + * lags : pointer to pitch-lags, 4 lags per frame. + * gains : pointer to pitch-gians, 4 gains per frame. + * mode : defining the functionality of the filter. It takes the + * following values. + * kPitchFilterPre: Pitch pre-filter, used at encoder. + * kPitchFilterPost: Pitch post-filter, used at decoder. + * kPitchFilterPreLa: Pitch pre-filter with lookahead. + * kPitchFilterPreGain: Pitch pre-filter used to otain optimal + * pitch-gains. + * + * Outputs + * out_data : pointer to a buffer where the filtered signal is written to. + * out_dg : [only used in kPitchFilterPreGain] pointer to a buffer + * where the output of different gain values (differential + * change to gain) is written. + */ +static void FilterFrame(const double* in_data, + PitchFiltstr* filter_state, + double* lags, + double* gains, + PitchFilterOperation mode, + double* out_data, + double out_dg[][PITCH_FRAME_LEN + QLOOKAHEAD]) { + PitchFilterParam filter_parameters; + double gain_delta, lag_delta; + double old_lag, old_gain; + int n; + int m; + const double kEnhancer = 1.3; + + /* Set up buffer and states. */ + filter_parameters.index = 0; + filter_parameters.lag_offset = 0; + filter_parameters.mode = mode; + /* Copy states to local variables. */ + memcpy(filter_parameters.buffer, filter_state->ubuf, + sizeof(filter_state->ubuf)); + RTC_COMPILE_ASSERT(sizeof(filter_parameters.buffer) >= + sizeof(filter_state->ubuf)); + memset(filter_parameters.buffer + + sizeof(filter_state->ubuf) / sizeof(filter_state->ubuf[0]), + 0, sizeof(filter_parameters.buffer) - sizeof(filter_state->ubuf)); + memcpy(filter_parameters.damper_state, filter_state->ystate, + sizeof(filter_state->ystate)); + + if (mode == kPitchFilterPreGain) { + /* Clear buffers. */ + memset(filter_parameters.gain_mult, 0, sizeof(filter_parameters.gain_mult)); + memset(filter_parameters.damper_state_dg, 0, + sizeof(filter_parameters.damper_state_dg)); + for (n = 0; n < PITCH_SUBFRAMES; ++n) { + // memset(out_dg[n], 0, sizeof(double) * (PITCH_FRAME_LEN + QLOOKAHEAD)); + memset(out_dg[n], 0, sizeof(out_dg[n])); + } + } else if (mode == kPitchFilterPost) { + /* Make output more periodic. Negative sign is to change the structure + * of the filter. */ + for (n = 0; n < PITCH_SUBFRAMES; ++n) { + gains[n] *= -kEnhancer; + } + } + + old_lag = *filter_state->oldlagp; + old_gain = *filter_state->oldgainp; + + /* No interpolation if pitch lag step is big. */ + if ((lags[0] > (PITCH_UPSTEP * old_lag)) || + (lags[0] < (PITCH_DOWNSTEP * old_lag))) { + old_lag = lags[0]; + old_gain = gains[0]; + + if (mode == kPitchFilterPreGain) { + filter_parameters.gain_mult[0] = 1.0; + } + } + + filter_parameters.num_samples = PITCH_UPDATE; + for (m = 0; m < PITCH_SUBFRAMES; ++m) { + /* Set the sub-frame value. */ + filter_parameters.sub_frame = m; + /* Calculate interpolation steps for pitch-lag and pitch-gain. */ + lag_delta = (lags[m] - old_lag) / PITCH_GRAN_PER_SUBFRAME; + filter_parameters.lag = old_lag; + gain_delta = (gains[m] - old_gain) / PITCH_GRAN_PER_SUBFRAME; + filter_parameters.gain = old_gain; + /* Store for the next sub-frame. */ + old_lag = lags[m]; + old_gain = gains[m]; + + for (n = 0; n < PITCH_GRAN_PER_SUBFRAME; ++n) { + /* Step-wise interpolation of pitch gains and lags. As pitch-lag changes, + * some parameters of filter need to be update. */ + filter_parameters.gain += gain_delta; + filter_parameters.lag += lag_delta; + /* Update parameters according to new lag value. */ + Update(&filter_parameters); + /* Filter a segment of input. */ + FilterSegment(in_data, &filter_parameters, out_data, out_dg); + } + } + + if (mode != kPitchFilterPreGain) { + /* Export buffer and states. */ + memcpy(filter_state->ubuf, &filter_parameters.buffer[PITCH_FRAME_LEN], + sizeof(filter_state->ubuf)); + memcpy(filter_state->ystate, filter_parameters.damper_state, + sizeof(filter_state->ystate)); + + /* Store for the next frame. */ + *filter_state->oldlagp = old_lag; + *filter_state->oldgainp = old_gain; + } + + if ((mode == kPitchFilterPreGain) || (mode == kPitchFilterPreLa)) { + /* Filter the lookahead segment, this is treated as the last sub-frame. So + * set `pf_param` to last sub-frame. */ + filter_parameters.sub_frame = PITCH_SUBFRAMES - 1; + filter_parameters.num_samples = QLOOKAHEAD; + FilterSegment(in_data, &filter_parameters, out_data, out_dg); + } +} + +void WebRtcIsac_PitchfilterPre(double* in_data, + double* out_data, + PitchFiltstr* pf_state, + double* lags, + double* gains) { + FilterFrame(in_data, pf_state, lags, gains, kPitchFilterPre, out_data, NULL); +} + +void WebRtcIsac_PitchfilterPre_la(double* in_data, + double* out_data, + PitchFiltstr* pf_state, + double* lags, + double* gains) { + FilterFrame(in_data, pf_state, lags, gains, kPitchFilterPreLa, out_data, + NULL); +} + +void WebRtcIsac_PitchfilterPre_gains( + double* in_data, + double* out_data, + double out_dg[][PITCH_FRAME_LEN + QLOOKAHEAD], + PitchFiltstr* pf_state, + double* lags, + double* gains) { + FilterFrame(in_data, pf_state, lags, gains, kPitchFilterPreGain, out_data, + out_dg); +} + +void WebRtcIsac_PitchfilterPost(double* in_data, + double* out_data, + PitchFiltstr* pf_state, + double* lags, + double* gains) { + FilterFrame(in_data, pf_state, lags, gains, kPitchFilterPost, out_data, NULL); +} diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.h b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.h new file mode 100644 index 00000000..9a232de8 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_FILTER_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_FILTER_H_ + +#include "modules/audio_coding/codecs/isac/main/source/structs.h" + +void WebRtcIsac_PitchfilterPre(double* indat, + double* outdat, + PitchFiltstr* pfp, + double* lags, + double* gains); + +void WebRtcIsac_PitchfilterPost(double* indat, + double* outdat, + PitchFiltstr* pfp, + double* lags, + double* gains); + +void WebRtcIsac_PitchfilterPre_la(double* indat, + double* outdat, + PitchFiltstr* pfp, + double* lags, + double* gains); + +void WebRtcIsac_PitchfilterPre_gains( + double* indat, + double* outdat, + double out_dG[][PITCH_FRAME_LEN + QLOOKAHEAD], + PitchFiltstr* pfp, + double* lags, + double* gains); + +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_FILTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/settings.h b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/settings.h new file mode 100644 index 00000000..abce90c4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/settings.h @@ -0,0 +1,196 @@ +/* + * 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. + */ + +/* + * settings.h + * + * Declaration of #defines used in the iSAC codec + * + */ + +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ + +/* sampling frequency (Hz) */ +#define FS 16000 + +/* number of samples per frame (either 320 (20ms), 480 (30ms) or 960 (60ms)) */ +#define INITIAL_FRAMESAMPLES 960 + +/* do not modify the following; this will have to be modified if we + * have a 20ms framesize option */ +/**********************************************************************/ +/* miliseconds */ +#define FRAMESIZE 30 +/* number of samples per frame processed in the encoder, 480 */ +#define FRAMESAMPLES 480 /* ((FRAMESIZE*FS)/1000) */ +#define FRAMESAMPLES_HALF 240 +#define FRAMESAMPLES_QUARTER 120 +/**********************************************************************/ + +/* max number of samples per frame (= 60 ms frame) */ +#define MAX_FRAMESAMPLES 960 +#define MAX_SWBFRAMESAMPLES (MAX_FRAMESAMPLES * 2) +/* number of samples per 10ms frame */ +#define FRAMESAMPLES_10ms ((10 * FS) / 1000) +#define SWBFRAMESAMPLES_10ms (FRAMESAMPLES_10ms * 2) +/* number of samples in 30 ms frame */ +#define FRAMESAMPLES_30ms 480 +/* number of subframes */ +#define SUBFRAMES 6 +/* length of a subframe */ +#define UPDATE 80 +/* length of half a subframe (low/high band) */ +#define HALF_SUBFRAMELEN (UPDATE / 2) +/* samples of look ahead (in a half-band, so actually + * half the samples of look ahead @ FS) */ +#define QLOOKAHEAD 24 /* 3 ms */ +/* order of AR model in spectral entropy coder */ +#define AR_ORDER 6 +/* order of LP model in spectral entropy coder */ +#define LP_ORDER 0 + +/* window length (masking analysis) */ +#define WINLEN 256 +/* order of low-band pole filter used to approximate masking curve */ +#define ORDERLO 12 +/* order of hi-band pole filter used to approximate masking curve */ +#define ORDERHI 6 + +#define UB_LPC_ORDER 4 +#define UB_LPC_VEC_PER_FRAME 2 +#define UB16_LPC_VEC_PER_FRAME 4 +#define UB_ACTIVE_SUBFRAMES 2 +#define UB_MAX_LPC_ORDER 6 +#define UB_INTERPOL_SEGMENTS 1 +#define UB16_INTERPOL_SEGMENTS 3 +#define LB_TOTAL_DELAY_SAMPLES 48 +enum ISACBandwidth { isac8kHz = 8, isac12kHz = 12, isac16kHz = 16 }; +enum ISACBand { + kIsacLowerBand = 0, + kIsacUpperBand12 = 1, + kIsacUpperBand16 = 2 +}; +enum IsacSamplingRate { kIsacWideband = 16, kIsacSuperWideband = 32 }; +#define UB_LPC_GAIN_DIM SUBFRAMES +#define FB_STATE_SIZE_WORD32 6 + +/* order for post_filter_bank */ +#define POSTQORDER 3 +/* order for pre-filterbank */ +#define QORDER 3 +/* another order */ +#define QORDER_ALL (POSTQORDER + QORDER - 1) +/* for decimator */ +#define ALLPASSSECTIONS 2 + +/* array size for byte stream in number of bytes. */ +/* The old maximum size still needed for the decoding */ +#define STREAM_SIZE_MAX 600 +#define STREAM_SIZE_MAX_30 200 /* 200 bytes=53.4 kbps @ 30 ms.framelength */ +#define STREAM_SIZE_MAX_60 400 /* 400 bytes=53.4 kbps @ 60 ms.framelength */ + +/* storage size for bit counts */ +#define BIT_COUNTER_SIZE 30 +/* maximum order of any AR model or filter */ +#define MAX_AR_MODEL_ORDER 12 // 50 + +/* For pitch analysis */ +#define PITCH_FRAME_LEN (FRAMESAMPLES_HALF) /* 30 ms */ +#define PITCH_MAX_LAG 140 /* 57 Hz */ +#define PITCH_MIN_LAG 20 /* 400 Hz */ +#define PITCH_MAX_GAIN 0.45 +#define PITCH_MAX_GAIN_06 0.27 /* PITCH_MAX_GAIN*0.6 */ +#define PITCH_MAX_GAIN_Q12 1843 +#define PITCH_LAG_SPAN2 (PITCH_MAX_LAG / 2 - PITCH_MIN_LAG / 2 + 5) +#define PITCH_CORR_LEN2 60 /* 15 ms */ +#define PITCH_CORR_STEP2 (PITCH_FRAME_LEN / 4) +#define PITCH_BW 11 /* half the band width of correlation surface */ +#define PITCH_SUBFRAMES 4 +#define PITCH_GRAN_PER_SUBFRAME 5 +#define PITCH_SUBFRAME_LEN (PITCH_FRAME_LEN / PITCH_SUBFRAMES) +#define PITCH_UPDATE (PITCH_SUBFRAME_LEN / PITCH_GRAN_PER_SUBFRAME) +/* maximum number of peaks to be examined in correlation surface */ +#define PITCH_MAX_NUM_PEAKS 10 +#define PITCH_PEAK_DECAY 0.85 +/* For weighting filter */ +#define PITCH_WLPCORDER 6 +#define PITCH_WLPCWINLEN PITCH_FRAME_LEN +#define PITCH_WLPCASYM 0.3 /* asymmetry parameter */ +#define PITCH_WLPCBUFLEN PITCH_WLPCWINLEN +/* For pitch filter */ +/* Extra 50 for fraction and LP filters */ +#define PITCH_BUFFSIZE (PITCH_MAX_LAG + 50) +#define PITCH_INTBUFFSIZE (PITCH_FRAME_LEN + PITCH_BUFFSIZE) +/* Max rel. step for interpolation */ +#define PITCH_UPSTEP 1.5 +/* Max rel. step for interpolation */ +#define PITCH_DOWNSTEP 0.67 +#define PITCH_FRACS 8 +#define PITCH_FRACORDER 9 +#define PITCH_DAMPORDER 5 +#define PITCH_FILTDELAY 1.5f +/* stepsize for quantization of the pitch Gain */ +#define PITCH_GAIN_STEPSIZE 0.125 + +/* Order of high pass filter */ +#define HPORDER 2 + +/* some mathematical constants */ +/* log2(exp) */ +#define LOG2EXP 1.44269504088896 +#define PI 3.14159265358979 + +/* Maximum number of iterations allowed to limit payload size */ +#define MAX_PAYLOAD_LIMIT_ITERATION 5 + +/* Redundant Coding */ +#define RCU_BOTTLENECK_BPS 16000 +#define RCU_TRANSCODING_SCALE 0.40f +#define RCU_TRANSCODING_SCALE_INVERSE 2.5f + +#define RCU_TRANSCODING_SCALE_UB 0.50f +#define RCU_TRANSCODING_SCALE_UB_INVERSE 2.0f + +/* Define Error codes */ +/* 6000 General */ +#define ISAC_MEMORY_ALLOCATION_FAILED 6010 +#define ISAC_MODE_MISMATCH 6020 +#define ISAC_DISALLOWED_BOTTLENECK 6030 +#define ISAC_DISALLOWED_FRAME_LENGTH 6040 +#define ISAC_UNSUPPORTED_SAMPLING_FREQUENCY 6050 + +/* 6200 Bandwidth estimator */ +#define ISAC_RANGE_ERROR_BW_ESTIMATOR 6240 +/* 6400 Encoder */ +#define ISAC_ENCODER_NOT_INITIATED 6410 +#define ISAC_DISALLOWED_CODING_MODE 6420 +#define ISAC_DISALLOWED_FRAME_MODE_ENCODER 6430 +#define ISAC_DISALLOWED_BITSTREAM_LENGTH 6440 +#define ISAC_PAYLOAD_LARGER_THAN_LIMIT 6450 +#define ISAC_DISALLOWED_ENCODER_BANDWIDTH 6460 +/* 6600 Decoder */ +#define ISAC_DECODER_NOT_INITIATED 6610 +#define ISAC_EMPTY_PACKET 6620 +#define ISAC_DISALLOWED_FRAME_MODE_DECODER 6630 +#define ISAC_RANGE_ERROR_DECODE_FRAME_LENGTH 6640 +#define ISAC_RANGE_ERROR_DECODE_BANDWIDTH 6650 +#define ISAC_RANGE_ERROR_DECODE_PITCH_GAIN 6660 +#define ISAC_RANGE_ERROR_DECODE_PITCH_LAG 6670 +#define ISAC_RANGE_ERROR_DECODE_LPC 6680 +#define ISAC_RANGE_ERROR_DECODE_SPECTRUM 6690 +#define ISAC_LENGTH_MISMATCH 6730 +#define ISAC_RANGE_ERROR_DECODE_BANDWITH 6740 +#define ISAC_DISALLOWED_BANDWIDTH_MODE_DECODER 6750 +#define ISAC_DISALLOWED_LPC_MODEL 6760 +/* 6800 Call setup formats */ +#define ISAC_INCOMPATIBLE_FORMATS 6810 + +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ */ diff --git a/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/structs.h b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/structs.h new file mode 100644 index 00000000..6861ca42 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_coding/codecs/isac/main/source/structs.h @@ -0,0 +1,448 @@ +/* + * 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. + */ + +/* + * structs.h + * + * This header file contains all the structs used in the ISAC codec + * + */ + +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ + +#include "modules/audio_coding/codecs/isac/bandwidth_info.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/third_party/fft/fft.h" + +typedef struct Bitstreamstruct { + uint8_t stream[STREAM_SIZE_MAX]; + uint32_t W_upper; + uint32_t streamval; + uint32_t stream_index; + +} Bitstr; + +typedef struct { + double DataBufferLo[WINLEN]; + double DataBufferHi[WINLEN]; + + double CorrBufLo[ORDERLO + 1]; + double CorrBufHi[ORDERHI + 1]; + + float PreStateLoF[ORDERLO + 1]; + float PreStateLoG[ORDERLO + 1]; + float PreStateHiF[ORDERHI + 1]; + float PreStateHiG[ORDERHI + 1]; + float PostStateLoF[ORDERLO + 1]; + float PostStateLoG[ORDERLO + 1]; + float PostStateHiF[ORDERHI + 1]; + float PostStateHiG[ORDERHI + 1]; + + double OldEnergy; + +} MaskFiltstr; + +typedef struct { + // state vectors for each of the two analysis filters + double INSTAT1[2 * (QORDER - 1)]; + double INSTAT2[2 * (QORDER - 1)]; + double INSTATLA1[2 * (QORDER - 1)]; + double INSTATLA2[2 * (QORDER - 1)]; + double INLABUF1[QLOOKAHEAD]; + double INLABUF2[QLOOKAHEAD]; + + float INSTAT1_float[2 * (QORDER - 1)]; + float INSTAT2_float[2 * (QORDER - 1)]; + float INSTATLA1_float[2 * (QORDER - 1)]; + float INSTATLA2_float[2 * (QORDER - 1)]; + float INLABUF1_float[QLOOKAHEAD]; + float INLABUF2_float[QLOOKAHEAD]; + + /* High pass filter */ + double HPstates[HPORDER]; + float HPstates_float[HPORDER]; + +} PreFiltBankstr; + +typedef struct { + // state vectors for each of the two analysis filters + double STATE_0_LOWER[2 * POSTQORDER]; + double STATE_0_UPPER[2 * POSTQORDER]; + + /* High pass filter */ + double HPstates1[HPORDER]; + double HPstates2[HPORDER]; + + float STATE_0_LOWER_float[2 * POSTQORDER]; + float STATE_0_UPPER_float[2 * POSTQORDER]; + + float HPstates1_float[HPORDER]; + float HPstates2_float[HPORDER]; + +} PostFiltBankstr; + +typedef struct { + // data buffer for pitch filter + double ubuf[PITCH_BUFFSIZE]; + + // low pass state vector + double ystate[PITCH_DAMPORDER]; + + // old lag and gain + double oldlagp[1]; + double oldgainp[1]; + +} PitchFiltstr; + +typedef struct { + // data buffer + double buffer[PITCH_WLPCBUFLEN]; + + // state vectors + double istate[PITCH_WLPCORDER]; + double weostate[PITCH_WLPCORDER]; + double whostate[PITCH_WLPCORDER]; + + // LPC window -> should be a global array because constant + double window[PITCH_WLPCWINLEN]; + +} WeightFiltstr; + +typedef struct { + // for inital estimator + double dec_buffer[PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + PITCH_MAX_LAG / 2 - + PITCH_FRAME_LEN / 2 + 2]; + double decimator_state[2 * ALLPASSSECTIONS + 1]; + double hp_state[2]; + + double whitened_buf[QLOOKAHEAD]; + + double inbuf[QLOOKAHEAD]; + + PitchFiltstr PFstr_wght; + PitchFiltstr PFstr; + WeightFiltstr Wghtstr; + +} PitchAnalysisStruct; + +/* Have instance of struct together with other iSAC structs */ +typedef struct { + /* Previous frame length (in ms) */ + int32_t prev_frame_length; + + /* Previous RTP timestamp from received + packet (in samples relative beginning) */ + int32_t prev_rec_rtp_number; + + /* Send timestamp for previous packet (in ms using timeGetTime()) */ + uint32_t prev_rec_send_ts; + + /* Arrival time for previous packet (in ms using timeGetTime()) */ + uint32_t prev_rec_arr_ts; + + /* rate of previous packet, derived from RTP timestamps (in bits/s) */ + float prev_rec_rtp_rate; + + /* Time sinse the last update of the BN estimate (in ms) */ + uint32_t last_update_ts; + + /* Time sinse the last reduction (in ms) */ + uint32_t last_reduction_ts; + + /* How many times the estimate was update in the beginning */ + int32_t count_tot_updates_rec; + + /* The estimated bottle neck rate from there to here (in bits/s) */ + int32_t rec_bw; + float rec_bw_inv; + float rec_bw_avg; + float rec_bw_avg_Q; + + /* The estimated mean absolute jitter value, + as seen on this side (in ms) */ + float rec_jitter; + float rec_jitter_short_term; + float rec_jitter_short_term_abs; + float rec_max_delay; + float rec_max_delay_avg_Q; + + /* (assumed) bitrate for headers (bps) */ + float rec_header_rate; + + /* The estimated bottle neck rate from here to there (in bits/s) */ + float send_bw_avg; + + /* The estimated mean absolute jitter value, as seen on + the other siee (in ms) */ + float send_max_delay_avg; + + // number of packets received since last update + int num_pkts_rec; + + int num_consec_rec_pkts_over_30k; + + // flag for marking that a high speed network has been + // detected downstream + int hsn_detect_rec; + + int num_consec_snt_pkts_over_30k; + + // flag for marking that a high speed network has + // been detected upstream + int hsn_detect_snd; + + uint32_t start_wait_period; + + int in_wait_period; + + int change_to_WB; + + uint32_t senderTimestamp; + uint32_t receiverTimestamp; + // enum IsacSamplingRate incomingStreamSampFreq; + uint16_t numConsecLatePkts; + float consecLatency; + int16_t inWaitLatePkts; + + IsacBandwidthInfo external_bw_info; +} BwEstimatorstr; + +typedef struct { + /* boolean, flags if previous packet exceeded B.N. */ + int PrevExceed; + /* ms */ + int ExceedAgo; + /* packets left to send in current burst */ + int BurstCounter; + /* packets */ + int InitCounter; + /* ms remaining in buffer when next packet will be sent */ + double StillBuffered; + +} RateModel; + +/* The following strutc is used to store data from encoding, to make it + fast and easy to construct a new bitstream with a different Bandwidth + estimate. All values (except framelength and minBytes) is double size to + handle 60 ms of data. +*/ +typedef struct { + /* Used to keep track of if it is first or second part of 60 msec packet */ + int startIdx; + + /* Frame length in samples */ + int16_t framelength; + + /* Pitch Gain */ + int pitchGain_index[2]; + + /* Pitch Lag */ + double meanGain[2]; + int pitchIndex[PITCH_SUBFRAMES * 2]; + + /* LPC */ + int LPCindex_s[108 * 2]; /* KLT_ORDER_SHAPE = 108 */ + int LPCindex_g[12 * 2]; /* KLT_ORDER_GAIN = 12 */ + double LPCcoeffs_lo[(ORDERLO + 1) * SUBFRAMES * 2]; + double LPCcoeffs_hi[(ORDERHI + 1) * SUBFRAMES * 2]; + + /* Encode Spec */ + int16_t fre[FRAMESAMPLES]; + int16_t fim[FRAMESAMPLES]; + int16_t AvgPitchGain[2]; + + /* Used in adaptive mode only */ + int minBytes; + +} IsacSaveEncoderData; + +typedef struct { + int indexLPCShape[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; + double lpcGain[SUBFRAMES << 1]; + int lpcGainIndex[SUBFRAMES << 1]; + + Bitstr bitStreamObj; + + int16_t realFFT[FRAMESAMPLES_HALF]; + int16_t imagFFT[FRAMESAMPLES_HALF]; +} ISACUBSaveEncDataStruct; + +typedef struct { + Bitstr bitstr_obj; + MaskFiltstr maskfiltstr_obj; + PreFiltBankstr prefiltbankstr_obj; + PitchFiltstr pitchfiltstr_obj; + PitchAnalysisStruct pitchanalysisstr_obj; + FFTstr fftstr_obj; + IsacSaveEncoderData SaveEnc_obj; + + int buffer_index; + int16_t current_framesamples; + + float data_buffer_float[FRAMESAMPLES_30ms]; + + int frame_nb; + double bottleneck; + int16_t new_framelength; + double s2nr; + + /* Maximum allowed number of bits for a 30 msec packet */ + int16_t payloadLimitBytes30; + /* Maximum allowed number of bits for a 30 msec packet */ + int16_t payloadLimitBytes60; + /* Maximum allowed number of bits for both 30 and 60 msec packet */ + int16_t maxPayloadBytes; + /* Maximum allowed rate in bytes per 30 msec packet */ + int16_t maxRateInBytes; + + /*--- + If set to 1 iSAC will not adapt the frame-size, if used in + channel-adaptive mode. The initial value will be used for all rates. + ---*/ + int16_t enforceFrameSize; + + /*----- + This records the BWE index the encoder injected into the bit-stream. + It will be used in RCU. The same BWE index of main payload will be in + the redundant payload. We can not retrieve it from BWE because it is + a recursive procedure (WebRtcIsac_GetDownlinkBwJitIndexImpl) and has to be + called only once per each encode. + -----*/ + int16_t lastBWIdx; +} ISACLBEncStruct; + +typedef struct { + Bitstr bitstr_obj; + MaskFiltstr maskfiltstr_obj; + PreFiltBankstr prefiltbankstr_obj; + FFTstr fftstr_obj; + ISACUBSaveEncDataStruct SaveEnc_obj; + + int buffer_index; + float data_buffer_float[MAX_FRAMESAMPLES + LB_TOTAL_DELAY_SAMPLES]; + double bottleneck; + /* Maximum allowed number of bits for a 30 msec packet */ + // int16_t payloadLimitBytes30; + /* Maximum allowed number of bits for both 30 and 60 msec packet */ + // int16_t maxPayloadBytes; + int16_t maxPayloadSizeBytes; + + double lastLPCVec[UB_LPC_ORDER]; + int16_t numBytesUsed; + int16_t lastJitterInfo; +} ISACUBEncStruct; + +typedef struct { + Bitstr bitstr_obj; + MaskFiltstr maskfiltstr_obj; + PostFiltBankstr postfiltbankstr_obj; + PitchFiltstr pitchfiltstr_obj; + FFTstr fftstr_obj; + +} ISACLBDecStruct; + +typedef struct { + Bitstr bitstr_obj; + MaskFiltstr maskfiltstr_obj; + PostFiltBankstr postfiltbankstr_obj; + FFTstr fftstr_obj; + +} ISACUBDecStruct; + +typedef struct { + ISACLBEncStruct ISACencLB_obj; + ISACLBDecStruct ISACdecLB_obj; +} ISACLBStruct; + +typedef struct { + ISACUBEncStruct ISACencUB_obj; + ISACUBDecStruct ISACdecUB_obj; +} ISACUBStruct; + +/* + This struct is used to take a snapshot of the entropy coder and LPC gains + right before encoding LPC gains. This allows us to go back to that state + if we like to limit the payload size. +*/ +typedef struct { + /* 6 lower-band & 6 upper-band */ + double loFiltGain[SUBFRAMES]; + double hiFiltGain[SUBFRAMES]; + /* Upper boundary of interval W */ + uint32_t W_upper; + uint32_t streamval; + /* Index to the current position in bytestream */ + uint32_t stream_index; + uint8_t stream[3]; +} transcode_obj; + +typedef struct { + // TODO(kwiberg): The size of these tables could be reduced by storing floats + // instead of doubles, and by making use of the identity cos(x) = + // sin(x+pi/2). They could also be made global constants that we fill in at + // compile time. + double costab1[FRAMESAMPLES_HALF]; + double sintab1[FRAMESAMPLES_HALF]; + double costab2[FRAMESAMPLES_QUARTER]; + double sintab2[FRAMESAMPLES_QUARTER]; +} TransformTables; + +typedef struct { + // lower-band codec instance + ISACLBStruct instLB; + // upper-band codec instance + ISACUBStruct instUB; + + // Bandwidth Estimator and model for the rate. + BwEstimatorstr bwestimator_obj; + RateModel rate_data_obj; + double MaxDelay; + + /* 0 = adaptive; 1 = instantaneous */ + int16_t codingMode; + + // overall bottleneck of the codec + int32_t bottleneck; + + // QMF Filter state + int32_t analysisFBState1[FB_STATE_SIZE_WORD32]; + int32_t analysisFBState2[FB_STATE_SIZE_WORD32]; + int32_t synthesisFBState1[FB_STATE_SIZE_WORD32]; + int32_t synthesisFBState2[FB_STATE_SIZE_WORD32]; + + // Error Code + int16_t errorCode; + + // bandwidth of the encoded audio 8, 12 or 16 kHz + enum ISACBandwidth bandwidthKHz; + // Sampling rate of audio, encoder and decode, 8 or 16 kHz + enum IsacSamplingRate encoderSamplingRateKHz; + enum IsacSamplingRate decoderSamplingRateKHz; + // Flag to keep track of initializations, lower & upper-band + // encoder and decoder. + int16_t initFlag; + + // Flag to to indicate signal bandwidth switch + int16_t resetFlag_8kHz; + + // Maximum allowed rate, measured in Bytes per 30 ms. + int16_t maxRateBytesPer30Ms; + // Maximum allowed payload-size, measured in Bytes. + int16_t maxPayloadSizeBytes; + /* The expected sampling rate of the input signal. Valid values are 16000 + * and 32000. This is not the operation sampling rate of the codec. */ + uint16_t in_sample_rate_hz; + + // Trig tables for WebRtcIsac_Time2Spec and WebRtcIsac_Spec2time. + TransformTables transform_tables; +} ISACMainStruct; + +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ */ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc new file mode 100644 index 00000000..22c3be2b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc @@ -0,0 +1,744 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/adaptive_fir_filter.h" + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_HAS_NEON) +#include +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif +#include + +#include +#include + +#include "modules/audio_processing/aec3/fft_data.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace aec3 { + +// Computes and stores the frequency response of the filter. +void ComputeFrequencyResponse( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2) { + for (auto& H2_ch : *H2) { + H2_ch.fill(0.f); + } + + const size_t num_render_channels = H[0].size(); + RTC_DCHECK_EQ(H.size(), H2->capacity()); + for (size_t p = 0; p < num_partitions; ++p) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, (*H2)[p].size()); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + for (size_t j = 0; j < kFftLengthBy2Plus1; ++j) { + float tmp = + H[p][ch].re[j] * H[p][ch].re[j] + H[p][ch].im[j] * H[p][ch].im[j]; + (*H2)[p][j] = std::max((*H2)[p][j], tmp); + } + } + } +} + +#if defined(WEBRTC_HAS_NEON) +// Computes and stores the frequency response of the filter. +void ComputeFrequencyResponse_Neon( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2) { + for (auto& H2_ch : *H2) { + H2_ch.fill(0.f); + } + + const size_t num_render_channels = H[0].size(); + RTC_DCHECK_EQ(H.size(), H2->capacity()); + for (size_t p = 0; p < num_partitions; ++p) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, (*H2)[p].size()); + auto& H2_p = (*H2)[p]; + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + for (size_t j = 0; j < kFftLengthBy2; j += 4) { + const float32x4_t re = vld1q_f32(&H_p_ch.re[j]); + const float32x4_t im = vld1q_f32(&H_p_ch.im[j]); + float32x4_t H2_new = vmulq_f32(re, re); + H2_new = vmlaq_f32(H2_new, im, im); + float32x4_t H2_p_j = vld1q_f32(&H2_p[j]); + H2_p_j = vmaxq_f32(H2_p_j, H2_new); + vst1q_f32(&H2_p[j], H2_p_j); + } + float H2_new = H_p_ch.re[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2] + + H_p_ch.im[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2]; + H2_p[kFftLengthBy2] = std::max(H2_p[kFftLengthBy2], H2_new); + } + } +} +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) +// Computes and stores the frequency response of the filter. +void ComputeFrequencyResponse_Sse2( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2) { + for (auto& H2_ch : *H2) { + H2_ch.fill(0.f); + } + + const size_t num_render_channels = H[0].size(); + RTC_DCHECK_EQ(H.size(), H2->capacity()); + // constexpr __mmmask8 kMaxMask = static_cast<__mmmask8>(256u); + for (size_t p = 0; p < num_partitions; ++p) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, (*H2)[p].size()); + auto& H2_p = (*H2)[p]; + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + for (size_t j = 0; j < kFftLengthBy2; j += 4) { + const __m128 re = _mm_loadu_ps(&H_p_ch.re[j]); + const __m128 re2 = _mm_mul_ps(re, re); + const __m128 im = _mm_loadu_ps(&H_p_ch.im[j]); + const __m128 im2 = _mm_mul_ps(im, im); + const __m128 H2_new = _mm_add_ps(re2, im2); + __m128 H2_k_j = _mm_loadu_ps(&H2_p[j]); + H2_k_j = _mm_max_ps(H2_k_j, H2_new); + _mm_storeu_ps(&H2_p[j], H2_k_j); + } + float H2_new = H_p_ch.re[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2] + + H_p_ch.im[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2]; + H2_p[kFftLengthBy2] = std::max(H2_p[kFftLengthBy2], H2_new); + } + } +} +#endif + +// Adapts the filter partitions as H(t+1)=H(t)+G(t)*conj(X(t)). +void AdaptPartitions(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H) { + ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + size_t index = render_buffer.Position(); + const size_t num_render_channels = render_buffer_data[index].size(); + for (size_t p = 0; p < num_partitions; ++p) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& X_p_ch = render_buffer_data[index][ch]; + FftData& H_p_ch = (*H)[p][ch]; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + H_p_ch.re[k] += X_p_ch.re[k] * G.re[k] + X_p_ch.im[k] * G.im[k]; + H_p_ch.im[k] += X_p_ch.re[k] * G.im[k] - X_p_ch.im[k] * G.re[k]; + } + } + index = index < (render_buffer_data.size() - 1) ? index + 1 : 0; + } +} + +#if defined(WEBRTC_HAS_NEON) +// Adapts the filter partitions. (Neon variant) +void AdaptPartitions_Neon(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H) { + webrtc::ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumFourBinBands = kFftLengthBy2 / 4; + + size_t X_partition = render_buffer.Position(); + size_t limit = lim1; + size_t p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + for (size_t k = 0, n = 0; n < kNumFourBinBands; ++n, k += 4) { + const float32x4_t G_re = vld1q_f32(&G.re[k]); + const float32x4_t G_im = vld1q_f32(&G.im[k]); + const float32x4_t X_re = vld1q_f32(&X.re[k]); + const float32x4_t X_im = vld1q_f32(&X.im[k]); + const float32x4_t H_re = vld1q_f32(&H_p_ch.re[k]); + const float32x4_t H_im = vld1q_f32(&H_p_ch.im[k]); + const float32x4_t a = vmulq_f32(X_re, G_re); + const float32x4_t e = vmlaq_f32(a, X_im, G_im); + const float32x4_t c = vmulq_f32(X_re, G_im); + const float32x4_t f = vmlsq_f32(c, X_im, G_re); + const float32x4_t g = vaddq_f32(H_re, e); + const float32x4_t h = vaddq_f32(H_im, f); + vst1q_f32(&H_p_ch.re[k], g); + vst1q_f32(&H_p_ch.im[k], h); + } + } + } + + X_partition = 0; + limit = lim2; + } while (p < lim2); + + X_partition = render_buffer.Position(); + limit = lim1; + p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + + H_p_ch.re[kFftLengthBy2] += X.re[kFftLengthBy2] * G.re[kFftLengthBy2] + + X.im[kFftLengthBy2] * G.im[kFftLengthBy2]; + H_p_ch.im[kFftLengthBy2] += X.re[kFftLengthBy2] * G.im[kFftLengthBy2] - + X.im[kFftLengthBy2] * G.re[kFftLengthBy2]; + } + } + X_partition = 0; + limit = lim2; + } while (p < lim2); +} +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) +// Adapts the filter partitions. (SSE2 variant) +void AdaptPartitions_Sse2(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H) { + ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumFourBinBands = kFftLengthBy2 / 4; + + size_t X_partition = render_buffer.Position(); + size_t limit = lim1; + size_t p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + + for (size_t k = 0, n = 0; n < kNumFourBinBands; ++n, k += 4) { + const __m128 G_re = _mm_loadu_ps(&G.re[k]); + const __m128 G_im = _mm_loadu_ps(&G.im[k]); + const __m128 X_re = _mm_loadu_ps(&X.re[k]); + const __m128 X_im = _mm_loadu_ps(&X.im[k]); + const __m128 H_re = _mm_loadu_ps(&H_p_ch.re[k]); + const __m128 H_im = _mm_loadu_ps(&H_p_ch.im[k]); + const __m128 a = _mm_mul_ps(X_re, G_re); + const __m128 b = _mm_mul_ps(X_im, G_im); + const __m128 c = _mm_mul_ps(X_re, G_im); + const __m128 d = _mm_mul_ps(X_im, G_re); + const __m128 e = _mm_add_ps(a, b); + const __m128 f = _mm_sub_ps(c, d); + const __m128 g = _mm_add_ps(H_re, e); + const __m128 h = _mm_add_ps(H_im, f); + _mm_storeu_ps(&H_p_ch.re[k], g); + _mm_storeu_ps(&H_p_ch.im[k], h); + } + } + } + X_partition = 0; + limit = lim2; + } while (p < lim2); + + X_partition = render_buffer.Position(); + limit = lim1; + p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + + H_p_ch.re[kFftLengthBy2] += X.re[kFftLengthBy2] * G.re[kFftLengthBy2] + + X.im[kFftLengthBy2] * G.im[kFftLengthBy2]; + H_p_ch.im[kFftLengthBy2] += X.re[kFftLengthBy2] * G.im[kFftLengthBy2] - + X.im[kFftLengthBy2] * G.re[kFftLengthBy2]; + } + } + + X_partition = 0; + limit = lim2; + } while (p < lim2); +} +#endif + +// Produces the filter output. +void ApplyFilter(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S) { + S->re.fill(0.f); + S->im.fill(0.f); + + ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + size_t index = render_buffer.Position(); + const size_t num_render_channels = render_buffer_data[index].size(); + for (size_t p = 0; p < num_partitions; ++p) { + RTC_DCHECK_EQ(num_render_channels, H[p].size()); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& X_p_ch = render_buffer_data[index][ch]; + const FftData& H_p_ch = H[p][ch]; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + S->re[k] += X_p_ch.re[k] * H_p_ch.re[k] - X_p_ch.im[k] * H_p_ch.im[k]; + S->im[k] += X_p_ch.re[k] * H_p_ch.im[k] + X_p_ch.im[k] * H_p_ch.re[k]; + } + } + index = index < (render_buffer_data.size() - 1) ? index + 1 : 0; + } +} + +#if defined(WEBRTC_HAS_NEON) +// Produces the filter output (Neon variant). +void ApplyFilter_Neon(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S) { + // const RenderBuffer& render_buffer, + // webrtc::ArrayView H, + // FftData* S) { + RTC_DCHECK_GE(H.size(), H.size() - 1); + S->Clear(); + + webrtc::ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumFourBinBands = kFftLengthBy2 / 4; + + size_t X_partition = render_buffer.Position(); + size_t p = 0; + size_t limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + for (size_t k = 0, n = 0; n < kNumFourBinBands; ++n, k += 4) { + const float32x4_t X_re = vld1q_f32(&X.re[k]); + const float32x4_t X_im = vld1q_f32(&X.im[k]); + const float32x4_t H_re = vld1q_f32(&H_p_ch.re[k]); + const float32x4_t H_im = vld1q_f32(&H_p_ch.im[k]); + const float32x4_t S_re = vld1q_f32(&S->re[k]); + const float32x4_t S_im = vld1q_f32(&S->im[k]); + const float32x4_t a = vmulq_f32(X_re, H_re); + const float32x4_t e = vmlsq_f32(a, X_im, H_im); + const float32x4_t c = vmulq_f32(X_re, H_im); + const float32x4_t f = vmlaq_f32(c, X_im, H_re); + const float32x4_t g = vaddq_f32(S_re, e); + const float32x4_t h = vaddq_f32(S_im, f); + vst1q_f32(&S->re[k], g); + vst1q_f32(&S->im[k], h); + } + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); + + X_partition = render_buffer.Position(); + p = 0; + limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + S->re[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2] - + X.im[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2]; + S->im[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2] + + X.im[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2]; + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); +} +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) +// Produces the filter output (SSE2 variant). +void ApplyFilter_Sse2(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S) { + // const RenderBuffer& render_buffer, + // webrtc::ArrayView H, + // FftData* S) { + RTC_DCHECK_GE(H.size(), H.size() - 1); + S->re.fill(0.f); + S->im.fill(0.f); + + ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumFourBinBands = kFftLengthBy2 / 4; + + size_t X_partition = render_buffer.Position(); + size_t p = 0; + size_t limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + for (size_t k = 0, n = 0; n < kNumFourBinBands; ++n, k += 4) { + const __m128 X_re = _mm_loadu_ps(&X.re[k]); + const __m128 X_im = _mm_loadu_ps(&X.im[k]); + const __m128 H_re = _mm_loadu_ps(&H_p_ch.re[k]); + const __m128 H_im = _mm_loadu_ps(&H_p_ch.im[k]); + const __m128 S_re = _mm_loadu_ps(&S->re[k]); + const __m128 S_im = _mm_loadu_ps(&S->im[k]); + const __m128 a = _mm_mul_ps(X_re, H_re); + const __m128 b = _mm_mul_ps(X_im, H_im); + const __m128 c = _mm_mul_ps(X_re, H_im); + const __m128 d = _mm_mul_ps(X_im, H_re); + const __m128 e = _mm_sub_ps(a, b); + const __m128 f = _mm_add_ps(c, d); + const __m128 g = _mm_add_ps(S_re, e); + const __m128 h = _mm_add_ps(S_im, f); + _mm_storeu_ps(&S->re[k], g); + _mm_storeu_ps(&S->im[k], h); + } + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); + + X_partition = render_buffer.Position(); + p = 0; + limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + S->re[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2] - + X.im[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2]; + S->im[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2] + + X.im[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2]; + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); +} +#endif + +} // namespace aec3 + +namespace { + +// Ensures that the newly added filter partitions after a size increase are set +// to zero. +void ZeroFilter(size_t old_size, + size_t new_size, + std::vector>* H) { + RTC_DCHECK_GE(H->size(), old_size); + RTC_DCHECK_GE(H->size(), new_size); + + for (size_t p = old_size; p < new_size; ++p) { + RTC_DCHECK_EQ((*H)[p].size(), (*H)[0].size()); + for (size_t ch = 0; ch < (*H)[0].size(); ++ch) { + (*H)[p][ch].Clear(); + } + } +} + +} // namespace + +AdaptiveFirFilter::AdaptiveFirFilter(size_t max_size_partitions, + size_t initial_size_partitions, + size_t size_change_duration_blocks, + size_t num_render_channels, + Aec3Optimization optimization, + ApmDataDumper* data_dumper) + : data_dumper_(data_dumper), + fft_(), + optimization_(optimization), + num_render_channels_(num_render_channels), + max_size_partitions_(max_size_partitions), + size_change_duration_blocks_( + static_cast(size_change_duration_blocks)), + current_size_partitions_(initial_size_partitions), + target_size_partitions_(initial_size_partitions), + old_target_size_partitions_(initial_size_partitions), + H_(max_size_partitions_, std::vector(num_render_channels_)) { + RTC_DCHECK(data_dumper_); + RTC_DCHECK_GE(max_size_partitions, initial_size_partitions); + + RTC_DCHECK_LT(0, size_change_duration_blocks_); + one_by_size_change_duration_blocks_ = 1.f / size_change_duration_blocks_; + + ZeroFilter(0, max_size_partitions_, &H_); + + SetSizePartitions(current_size_partitions_, true); +} + +AdaptiveFirFilter::~AdaptiveFirFilter() = default; + +void AdaptiveFirFilter::HandleEchoPathChange() { + // TODO(peah): Check the value and purpose of the code below. + ZeroFilter(current_size_partitions_, max_size_partitions_, &H_); +} + +void AdaptiveFirFilter::SetSizePartitions(size_t size, bool immediate_effect) { + RTC_DCHECK_EQ(max_size_partitions_, H_.capacity()); + RTC_DCHECK_LE(size, max_size_partitions_); + + target_size_partitions_ = std::min(max_size_partitions_, size); + if (immediate_effect) { + size_t old_size_partitions_ = current_size_partitions_; + current_size_partitions_ = old_target_size_partitions_ = + target_size_partitions_; + ZeroFilter(old_size_partitions_, current_size_partitions_, &H_); + + partition_to_constrain_ = + std::min(partition_to_constrain_, current_size_partitions_ - 1); + size_change_counter_ = 0; + } else { + size_change_counter_ = size_change_duration_blocks_; + } +} + +void AdaptiveFirFilter::UpdateSize() { + RTC_DCHECK_GE(size_change_duration_blocks_, size_change_counter_); + size_t old_size_partitions_ = current_size_partitions_; + if (size_change_counter_ > 0) { + --size_change_counter_; + + auto average = [](float from, float to, float from_weight) { + return from * from_weight + to * (1.f - from_weight); + }; + + float change_factor = + size_change_counter_ * one_by_size_change_duration_blocks_; + + current_size_partitions_ = average(old_target_size_partitions_, + target_size_partitions_, change_factor); + + partition_to_constrain_ = + std::min(partition_to_constrain_, current_size_partitions_ - 1); + } else { + current_size_partitions_ = old_target_size_partitions_ = + target_size_partitions_; + } + ZeroFilter(old_size_partitions_, current_size_partitions_, &H_); + RTC_DCHECK_LE(0, size_change_counter_); +} + +void AdaptiveFirFilter::Filter(const RenderBuffer& render_buffer, + FftData* S) const { + RTC_DCHECK(S); + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: + aec3::ApplyFilter_Sse2(render_buffer, current_size_partitions_, H_, S); + break; + case Aec3Optimization::kAvx2: + aec3::ApplyFilter_Avx2(render_buffer, current_size_partitions_, H_, S); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: + aec3::ApplyFilter_Neon(render_buffer, current_size_partitions_, H_, S); + break; +#endif + default: + aec3::ApplyFilter(render_buffer, current_size_partitions_, H_, S); + } +} + +void AdaptiveFirFilter::Adapt(const RenderBuffer& render_buffer, + const FftData& G) { + // Adapt the filter and update the filter size. + AdaptAndUpdateSize(render_buffer, G); + + // Constrain the filter partitions in a cyclic manner. + Constrain(); +} + +void AdaptiveFirFilter::Adapt(const RenderBuffer& render_buffer, + const FftData& G, + std::vector* impulse_response) { + // Adapt the filter and update the filter size. + AdaptAndUpdateSize(render_buffer, G); + + // Constrain the filter partitions in a cyclic manner. + ConstrainAndUpdateImpulseResponse(impulse_response); +} + +void AdaptiveFirFilter::ComputeFrequencyResponse( + std::vector>* H2) const { + RTC_DCHECK_GE(max_size_partitions_, H2->capacity()); + + H2->resize(current_size_partitions_); + + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: + aec3::ComputeFrequencyResponse_Sse2(current_size_partitions_, H_, H2); + break; + case Aec3Optimization::kAvx2: + aec3::ComputeFrequencyResponse_Avx2(current_size_partitions_, H_, H2); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: + aec3::ComputeFrequencyResponse_Neon(current_size_partitions_, H_, H2); + break; +#endif + default: + aec3::ComputeFrequencyResponse(current_size_partitions_, H_, H2); + } +} + +void AdaptiveFirFilter::AdaptAndUpdateSize(const RenderBuffer& render_buffer, + const FftData& G) { + // Update the filter size if needed. + UpdateSize(); + + // Adapt the filter. + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: + aec3::AdaptPartitions_Sse2(render_buffer, G, current_size_partitions_, + &H_); + break; + case Aec3Optimization::kAvx2: + aec3::AdaptPartitions_Avx2(render_buffer, G, current_size_partitions_, + &H_); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: + aec3::AdaptPartitions_Neon(render_buffer, G, current_size_partitions_, + &H_); + break; +#endif + default: + aec3::AdaptPartitions(render_buffer, G, current_size_partitions_, &H_); + } +} + +// Constrains the partition of the frequency domain filter to be limited in +// time via setting the relevant time-domain coefficients to zero and updates +// the corresponding values in an externally stored impulse response estimate. +void AdaptiveFirFilter::ConstrainAndUpdateImpulseResponse( + std::vector* impulse_response) { + RTC_DCHECK_EQ(GetTimeDomainLength(max_size_partitions_), + impulse_response->capacity()); + impulse_response->resize(GetTimeDomainLength(current_size_partitions_)); + std::array h; + impulse_response->resize(GetTimeDomainLength(current_size_partitions_)); + std::fill( + impulse_response->begin() + partition_to_constrain_ * kFftLengthBy2, + impulse_response->begin() + (partition_to_constrain_ + 1) * kFftLengthBy2, + 0.f); + + for (size_t ch = 0; ch < num_render_channels_; ++ch) { + fft_.Ifft(H_[partition_to_constrain_][ch], &h); + + static constexpr float kScale = 1.0f / kFftLengthBy2; + std::for_each(h.begin(), h.begin() + kFftLengthBy2, + [](float& a) { a *= kScale; }); + std::fill(h.begin() + kFftLengthBy2, h.end(), 0.f); + + if (ch == 0) { + std::copy( + h.begin(), h.begin() + kFftLengthBy2, + impulse_response->begin() + partition_to_constrain_ * kFftLengthBy2); + } else { + for (size_t k = 0, j = partition_to_constrain_ * kFftLengthBy2; + k < kFftLengthBy2; ++k, ++j) { + if (fabsf((*impulse_response)[j]) < fabsf(h[k])) { + (*impulse_response)[j] = h[k]; + } + } + } + + fft_.Fft(&h, &H_[partition_to_constrain_][ch]); + } + + partition_to_constrain_ = + partition_to_constrain_ < (current_size_partitions_ - 1) + ? partition_to_constrain_ + 1 + : 0; +} + +// Constrains the a partiton of the frequency domain filter to be limited in +// time via setting the relevant time-domain coefficients to zero. +void AdaptiveFirFilter::Constrain() { + std::array h; + for (size_t ch = 0; ch < num_render_channels_; ++ch) { + fft_.Ifft(H_[partition_to_constrain_][ch], &h); + + static constexpr float kScale = 1.0f / kFftLengthBy2; + std::for_each(h.begin(), h.begin() + kFftLengthBy2, + [](float& a) { a *= kScale; }); + std::fill(h.begin() + kFftLengthBy2, h.end(), 0.f); + + fft_.Fft(&h, &H_[partition_to_constrain_][ch]); + } + + partition_to_constrain_ = + partition_to_constrain_ < (current_size_partitions_ - 1) + ? partition_to_constrain_ + 1 + : 0; +} + +void AdaptiveFirFilter::ScaleFilter(float factor) { + for (auto& H_p : H_) { + for (auto& H_p_ch : H_p) { + for (auto& re : H_p_ch.re) { + re *= factor; + } + for (auto& im : H_p_ch.im) { + im *= factor; + } + } + } +} + +// Set the filter coefficients. +void AdaptiveFirFilter::SetFilter(size_t num_partitions, + const std::vector>& H) { + const size_t min_num_partitions = + std::min(current_size_partitions_, num_partitions); + for (size_t p = 0; p < min_num_partitions; ++p) { + RTC_DCHECK_EQ(H_[p].size(), H[p].size()); + RTC_DCHECK_EQ(num_render_channels_, H_[p].size()); + + for (size_t ch = 0; ch < num_render_channels_; ++ch) { + std::copy(H[p][ch].re.begin(), H[p][ch].re.end(), H_[p][ch].re.begin()); + std::copy(H[p][ch].im.begin(), H[p][ch].im.end(), H_[p][ch].im.begin()); + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h b/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h new file mode 100644 index 00000000..34c06f43 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_H_ + +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec3_fft.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace aec3 { +// Computes and stores the frequency response of the filter. +void ComputeFrequencyResponse( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2); +#if defined(WEBRTC_HAS_NEON) +void ComputeFrequencyResponse_Neon( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2); +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +void ComputeFrequencyResponse_Sse2( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2); + +void ComputeFrequencyResponse_Avx2( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2); +#endif + +// Adapts the filter partitions. +void AdaptPartitions(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H); +#if defined(WEBRTC_HAS_NEON) +void AdaptPartitions_Neon(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H); +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +void AdaptPartitions_Sse2(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H); + +void AdaptPartitions_Avx2(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H); +#endif + +// Produces the filter output. +void ApplyFilter(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S); +#if defined(WEBRTC_HAS_NEON) +void ApplyFilter_Neon(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S); +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +void ApplyFilter_Sse2(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S); + +void ApplyFilter_Avx2(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S); +#endif + +} // namespace aec3 + +// Provides a frequency domain adaptive filter functionality. +class AdaptiveFirFilter { + public: + AdaptiveFirFilter(size_t max_size_partitions, + size_t initial_size_partitions, + size_t size_change_duration_blocks, + size_t num_render_channels, + Aec3Optimization optimization, + ApmDataDumper* data_dumper); + + ~AdaptiveFirFilter(); + + AdaptiveFirFilter(const AdaptiveFirFilter&) = delete; + AdaptiveFirFilter& operator=(const AdaptiveFirFilter&) = delete; + + // Produces the output of the filter. + void Filter(const RenderBuffer& render_buffer, FftData* S) const; + + // Adapts the filter and updates an externally stored impulse response + // estimate. + void Adapt(const RenderBuffer& render_buffer, + const FftData& G, + std::vector* impulse_response); + + // Adapts the filter. + void Adapt(const RenderBuffer& render_buffer, const FftData& G); + + // Receives reports that known echo path changes have occured and adjusts + // the filter adaptation accordingly. + void HandleEchoPathChange(); + + // Returns the filter size. + size_t SizePartitions() const { return current_size_partitions_; } + + // Sets the filter size. + void SetSizePartitions(size_t size, bool immediate_effect); + + // Computes the frequency responses for the filter partitions. + void ComputeFrequencyResponse( + std::vector>* H2) const; + + // Returns the maximum number of partitions for the filter. + size_t max_filter_size_partitions() const { return max_size_partitions_; } + + void DumpFilter(absl::string_view name_frequency_domain) { + for (size_t p = 0; p < max_size_partitions_; ++p) { + data_dumper_->DumpRaw(name_frequency_domain, H_[p][0].re); + data_dumper_->DumpRaw(name_frequency_domain, H_[p][0].im); + } + } + + // Scale the filter impulse response and spectrum by a factor. + void ScaleFilter(float factor); + + // Set the filter coefficients. + void SetFilter(size_t num_partitions, + const std::vector>& H); + + // Gets the filter coefficients. + const std::vector>& GetFilter() const { return H_; } + + private: + // Adapts the filter and updates the filter size. + void AdaptAndUpdateSize(const RenderBuffer& render_buffer, const FftData& G); + + // Constrain the filter partitions in a cyclic manner. + void Constrain(); + // Constrains the filter in a cyclic manner and updates the corresponding + // values in the supplied impulse response. + void ConstrainAndUpdateImpulseResponse(std::vector* impulse_response); + + // Gradually Updates the current filter size towards the target size. + void UpdateSize(); + + ApmDataDumper* const data_dumper_; + const Aec3Fft fft_; + const Aec3Optimization optimization_; + const size_t num_render_channels_; + const size_t max_size_partitions_; + const int size_change_duration_blocks_; + float one_by_size_change_duration_blocks_; + size_t current_size_partitions_; + size_t target_size_partitions_; + size_t old_target_size_partitions_; + int size_change_counter_ = 0; + std::vector> H_; + size_t partition_to_constrain_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc new file mode 100644 index 00000000..9bf7bc7d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/adaptive_fir_filter_erl.h" + +#include +#include + +#if defined(WEBRTC_HAS_NEON) +#include +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif + +namespace webrtc { + +namespace aec3 { + +// Computes and stores the echo return loss estimate of the filter, which is the +// sum of the partition frequency responses. +void ErlComputer(const std::vector>& H2, + ArrayView erl) { + std::fill(erl.begin(), erl.end(), 0.f); + for (auto& H2_j : H2) { + std::transform(H2_j.begin(), H2_j.end(), erl.begin(), erl.begin(), + std::plus()); + } +} + +#if defined(WEBRTC_HAS_NEON) +// Computes and stores the echo return loss estimate of the filter, which is the +// sum of the partition frequency responses. +void ErlComputer_NEON( + const std::vector>& H2, + webrtc::ArrayView erl) { + std::fill(erl.begin(), erl.end(), 0.f); + for (auto& H2_j : H2) { + for (size_t k = 0; k < kFftLengthBy2; k += 4) { + const float32x4_t H2_j_k = vld1q_f32(&H2_j[k]); + float32x4_t erl_k = vld1q_f32(&erl[k]); + erl_k = vaddq_f32(erl_k, H2_j_k); + vst1q_f32(&erl[k], erl_k); + } + erl[kFftLengthBy2] += H2_j[kFftLengthBy2]; + } +} +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) +// Computes and stores the echo return loss estimate of the filter, which is the +// sum of the partition frequency responses. +void ErlComputer_SSE2( + const std::vector>& H2, + ArrayView erl) { + std::fill(erl.begin(), erl.end(), 0.f); + for (auto& H2_j : H2) { + for (size_t k = 0; k < kFftLengthBy2; k += 4) { + const __m128 H2_j_k = _mm_loadu_ps(&H2_j[k]); + __m128 erl_k = _mm_loadu_ps(&erl[k]); + erl_k = _mm_add_ps(erl_k, H2_j_k); + _mm_storeu_ps(&erl[k], erl_k); + } + erl[kFftLengthBy2] += H2_j[kFftLengthBy2]; + } +} +#endif + +} // namespace aec3 + +void ComputeErl(const Aec3Optimization& optimization, + const std::vector>& H2, + ArrayView erl) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, erl.size()); + // Update the frequency response and echo return loss for the filter. + switch (optimization) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: + aec3::ErlComputer_SSE2(H2, erl); + break; + case Aec3Optimization::kAvx2: + aec3::ErlComputer_AVX2(H2, erl); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: + aec3::ErlComputer_NEON(H2, erl); + break; +#endif + default: + aec3::ErlComputer(H2, erl); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.h b/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.h new file mode 100644 index 00000000..68da2d2d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_ERL_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_ERL_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace aec3 { + +// Computes and stores the echo return loss estimate of the filter, which is the +// sum of the partition frequency responses. +void ErlComputer(const std::vector>& H2, + ArrayView erl); +#if defined(WEBRTC_HAS_NEON) +void ErlComputer_NEON( + const std::vector>& H2, + webrtc::ArrayView erl); +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +void ErlComputer_SSE2( + const std::vector>& H2, + ArrayView erl); + +void ErlComputer_AVX2( + const std::vector>& H2, + ArrayView erl); +#endif + +} // namespace aec3 + +// Computes the echo return loss based on a frequency response. +void ComputeErl(const Aec3Optimization& optimization, + const std::vector>& H2, + ArrayView erl); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_ERL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/aec3.go b/pkg/apm/webrtc/modules/audio_processing/aec3/aec3.go new file mode 100644 index 00000000..1c450b9e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/aec3.go @@ -0,0 +1,10 @@ +//go:build console + +package aec3 + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_common.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_common.cc new file mode 100644 index 00000000..3ba10d5b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_common.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/aec3_common.h" + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" + +namespace webrtc { + +Aec3Optimization DetectOptimization() { +#if defined(WEBRTC_ARCH_X86_FAMILY) + if (GetCPUInfo(kAVX2) != 0) { + return Aec3Optimization::kAvx2; + } else if (GetCPUInfo(kSSE2) != 0) { + return Aec3Optimization::kSse2; + } +#endif + +#if defined(WEBRTC_HAS_NEON) + return Aec3Optimization::kNeon; +#else + return Aec3Optimization::kNone; +#endif +} + +float FastApproxLog2f(const float in) { + RTC_DCHECK_GT(in, .0f); + // Read and interpret float as uint32_t and then cast to float. + // This is done to extract the exponent (bits 30 - 23). + // "Right shift" of the exponent is then performed by multiplying + // with the constant (1/2^23). Finally, we subtract a constant to + // remove the bias (https://en.wikipedia.org/wiki/Exponent_bias). + union { + float dummy; + uint32_t a; + } x = {in}; + float out = x.a; + out *= 1.1920929e-7f; // 1/2^23 + out -= 126.942695f; // Remove bias. + return out; +} + +float Log2TodB(const float in_log2) { + return 3.0102999566398121 * in_log2; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_common.h b/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_common.h new file mode 100644 index 00000000..32b564f1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_common.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_AEC3_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_AEC3_COMMON_H_ + +#include + +namespace webrtc { + +#ifdef _MSC_VER /* visual c++ */ +#define ALIGN16_BEG __declspec(align(16)) +#define ALIGN16_END +#else /* gcc or icc */ +#define ALIGN16_BEG +#define ALIGN16_END __attribute__((aligned(16))) +#endif + +enum class Aec3Optimization { kNone, kSse2, kAvx2, kNeon }; + +constexpr int kNumBlocksPerSecond = 250; + +constexpr int kMetricsReportingIntervalBlocks = 10 * kNumBlocksPerSecond; +constexpr int kMetricsComputationBlocks = 3; +constexpr int kMetricsCollectionBlocks = + kMetricsReportingIntervalBlocks - kMetricsComputationBlocks; + +constexpr size_t kFftLengthBy2 = 64; +constexpr size_t kFftLengthBy2Plus1 = kFftLengthBy2 + 1; +constexpr size_t kFftLengthBy2Minus1 = kFftLengthBy2 - 1; +constexpr size_t kFftLength = 2 * kFftLengthBy2; +constexpr size_t kFftLengthBy2Log2 = 6; + +constexpr int kRenderTransferQueueSizeFrames = 100; + +constexpr size_t kMaxNumBands = 3; +constexpr size_t kFrameSize = 160; +constexpr size_t kSubFrameLength = kFrameSize / 2; + +constexpr size_t kBlockSize = kFftLengthBy2; +constexpr size_t kBlockSizeLog2 = kFftLengthBy2Log2; + +constexpr size_t kExtendedBlockSize = 2 * kFftLengthBy2; +constexpr size_t kMatchedFilterWindowSizeSubBlocks = 32; +constexpr size_t kMatchedFilterAlignmentShiftSizeSubBlocks = + kMatchedFilterWindowSizeSubBlocks * 3 / 4; + +// TODO(peah): Integrate this with how it is done inside audio_processing_impl. +constexpr size_t NumBandsForRate(int sample_rate_hz) { + return static_cast(sample_rate_hz / 16000); +} + +constexpr bool ValidFullBandRate(int sample_rate_hz) { + return sample_rate_hz == 16000 || sample_rate_hz == 32000 || + sample_rate_hz == 48000; +} + +constexpr int GetTimeDomainLength(int filter_length_blocks) { + return filter_length_blocks * kFftLengthBy2; +} + +constexpr size_t GetDownSampledBufferSize(size_t down_sampling_factor, + size_t num_matched_filters) { + return kBlockSize / down_sampling_factor * + (kMatchedFilterAlignmentShiftSizeSubBlocks * num_matched_filters + + kMatchedFilterWindowSizeSubBlocks + 1); +} + +constexpr size_t GetRenderDelayBufferSize(size_t down_sampling_factor, + size_t num_matched_filters, + size_t filter_length_blocks) { + return GetDownSampledBufferSize(down_sampling_factor, num_matched_filters) / + (kBlockSize / down_sampling_factor) + + filter_length_blocks + 1; +} + +// Detects what kind of optimizations to use for the code. +Aec3Optimization DetectOptimization(); + +// Computes the log2 of the input in a fast an approximate manner. +float FastApproxLog2f(float in); + +// Returns dB from a power quantity expressed in log2. +float Log2TodB(float in_log2); + +static_assert(1 << kBlockSizeLog2 == kBlockSize, + "Proper number of shifts for blocksize"); + +static_assert(1 << kFftLengthBy2Log2 == kFftLengthBy2, + "Proper number of shifts for the fft length"); + +static_assert(1 == NumBandsForRate(16000), "Number of bands for 16 kHz"); +static_assert(2 == NumBandsForRate(32000), "Number of bands for 32 kHz"); +static_assert(3 == NumBandsForRate(48000), "Number of bands for 48 kHz"); + +static_assert(ValidFullBandRate(16000), + "Test that 16 kHz is a valid sample rate"); +static_assert(ValidFullBandRate(32000), + "Test that 32 kHz is a valid sample rate"); +static_assert(ValidFullBandRate(48000), + "Test that 48 kHz is a valid sample rate"); +static_assert(!ValidFullBandRate(8001), + "Test that 8001 Hz is not a valid sample rate"); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_AEC3_COMMON_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_fft.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_fft.cc new file mode 100644 index 00000000..1c4cf33c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_fft.cc @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/aec3_fft.h" + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "system_wrappers/include/cpu_features_wrapper.h" + +namespace webrtc { + +namespace { + +const float kHanning64[kFftLengthBy2] = { + 0.f, 0.00248461f, 0.00991376f, 0.0222136f, 0.03926189f, + 0.06088921f, 0.08688061f, 0.11697778f, 0.15088159f, 0.1882551f, + 0.22872687f, 0.27189467f, 0.31732949f, 0.36457977f, 0.41317591f, + 0.46263495f, 0.51246535f, 0.56217185f, 0.61126047f, 0.65924333f, + 0.70564355f, 0.75f, 0.79187184f, 0.83084292f, 0.86652594f, + 0.89856625f, 0.92664544f, 0.95048443f, 0.96984631f, 0.98453864f, + 0.99441541f, 0.99937846f, 0.99937846f, 0.99441541f, 0.98453864f, + 0.96984631f, 0.95048443f, 0.92664544f, 0.89856625f, 0.86652594f, + 0.83084292f, 0.79187184f, 0.75f, 0.70564355f, 0.65924333f, + 0.61126047f, 0.56217185f, 0.51246535f, 0.46263495f, 0.41317591f, + 0.36457977f, 0.31732949f, 0.27189467f, 0.22872687f, 0.1882551f, + 0.15088159f, 0.11697778f, 0.08688061f, 0.06088921f, 0.03926189f, + 0.0222136f, 0.00991376f, 0.00248461f, 0.f}; + +// Hanning window from Matlab command win = sqrt(hanning(128)). +const float kSqrtHanning128[kFftLength] = { + 0.00000000000000f, 0.02454122852291f, 0.04906767432742f, 0.07356456359967f, + 0.09801714032956f, 0.12241067519922f, 0.14673047445536f, 0.17096188876030f, + 0.19509032201613f, 0.21910124015687f, 0.24298017990326f, 0.26671275747490f, + 0.29028467725446f, 0.31368174039889f, 0.33688985339222f, 0.35989503653499f, + 0.38268343236509f, 0.40524131400499f, 0.42755509343028f, 0.44961132965461f, + 0.47139673682600f, 0.49289819222978f, 0.51410274419322f, 0.53499761988710f, + 0.55557023301960f, 0.57580819141785f, 0.59569930449243f, 0.61523159058063f, + 0.63439328416365f, 0.65317284295378f, 0.67155895484702f, 0.68954054473707f, + 0.70710678118655f, 0.72424708295147f, 0.74095112535496f, 0.75720884650648f, + 0.77301045336274f, 0.78834642762661f, 0.80320753148064f, 0.81758481315158f, + 0.83146961230255f, 0.84485356524971f, 0.85772861000027f, 0.87008699110871f, + 0.88192126434835f, 0.89322430119552f, 0.90398929312344f, 0.91420975570353f, + 0.92387953251129f, 0.93299279883474f, 0.94154406518302f, 0.94952818059304f, + 0.95694033573221f, 0.96377606579544f, 0.97003125319454f, 0.97570213003853f, + 0.98078528040323f, 0.98527764238894f, 0.98917650996478f, 0.99247953459871f, + 0.99518472667220f, 0.99729045667869f, 0.99879545620517f, 0.99969881869620f, + 1.00000000000000f, 0.99969881869620f, 0.99879545620517f, 0.99729045667869f, + 0.99518472667220f, 0.99247953459871f, 0.98917650996478f, 0.98527764238894f, + 0.98078528040323f, 0.97570213003853f, 0.97003125319454f, 0.96377606579544f, + 0.95694033573221f, 0.94952818059304f, 0.94154406518302f, 0.93299279883474f, + 0.92387953251129f, 0.91420975570353f, 0.90398929312344f, 0.89322430119552f, + 0.88192126434835f, 0.87008699110871f, 0.85772861000027f, 0.84485356524971f, + 0.83146961230255f, 0.81758481315158f, 0.80320753148064f, 0.78834642762661f, + 0.77301045336274f, 0.75720884650648f, 0.74095112535496f, 0.72424708295147f, + 0.70710678118655f, 0.68954054473707f, 0.67155895484702f, 0.65317284295378f, + 0.63439328416365f, 0.61523159058063f, 0.59569930449243f, 0.57580819141785f, + 0.55557023301960f, 0.53499761988710f, 0.51410274419322f, 0.49289819222978f, + 0.47139673682600f, 0.44961132965461f, 0.42755509343028f, 0.40524131400499f, + 0.38268343236509f, 0.35989503653499f, 0.33688985339222f, 0.31368174039889f, + 0.29028467725446f, 0.26671275747490f, 0.24298017990326f, 0.21910124015687f, + 0.19509032201613f, 0.17096188876030f, 0.14673047445536f, 0.12241067519922f, + 0.09801714032956f, 0.07356456359967f, 0.04906767432742f, 0.02454122852291f}; + +bool IsSse2Available() { +#if defined(WEBRTC_ARCH_X86_FAMILY) + return GetCPUInfo(kSSE2) != 0; +#else + return false; +#endif +} + +} // namespace + +Aec3Fft::Aec3Fft() : ooura_fft_(IsSse2Available()) {} + +// TODO(peah): Change x to be std::array once the rest of the code allows this. +void Aec3Fft::ZeroPaddedFft(ArrayView x, + Window window, + FftData* X) const { + RTC_DCHECK(X); + RTC_DCHECK_EQ(kFftLengthBy2, x.size()); + std::array fft; + std::fill(fft.begin(), fft.begin() + kFftLengthBy2, 0.f); + switch (window) { + case Window::kRectangular: + std::copy(x.begin(), x.end(), fft.begin() + kFftLengthBy2); + break; + case Window::kHanning: + std::transform(x.begin(), x.end(), std::begin(kHanning64), + fft.begin() + kFftLengthBy2, + [](float a, float b) { return a * b; }); + break; + case Window::kSqrtHanning: + RTC_DCHECK_NOTREACHED(); + break; + default: + RTC_DCHECK_NOTREACHED(); + } + + Fft(&fft, X); +} + +void Aec3Fft::PaddedFft(ArrayView x, + ArrayView x_old, + Window window, + FftData* X) const { + RTC_DCHECK(X); + RTC_DCHECK_EQ(kFftLengthBy2, x.size()); + RTC_DCHECK_EQ(kFftLengthBy2, x_old.size()); + std::array fft; + + switch (window) { + case Window::kRectangular: + std::copy(x_old.begin(), x_old.end(), fft.begin()); + std::copy(x.begin(), x.end(), fft.begin() + x_old.size()); + break; + case Window::kHanning: + RTC_DCHECK_NOTREACHED(); + break; + case Window::kSqrtHanning: + std::transform(x_old.begin(), x_old.end(), std::begin(kSqrtHanning128), + fft.begin(), std::multiplies()); + std::transform(x.begin(), x.end(), + std::begin(kSqrtHanning128) + x_old.size(), + fft.begin() + x_old.size(), std::multiplies()); + break; + default: + RTC_DCHECK_NOTREACHED(); + } + + Fft(&fft, X); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_fft.h b/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_fft.h new file mode 100644 index 00000000..83d2a2e9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/aec3_fft.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_AEC3_FFT_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_AEC3_FFT_H_ + +#include + +#include "api/array_view.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Wrapper class that provides 128 point real valued FFT functionality with the +// FftData type. +class Aec3Fft { + public: + enum class Window { kRectangular, kHanning, kSqrtHanning }; + + Aec3Fft(); + + Aec3Fft(const Aec3Fft&) = delete; + Aec3Fft& operator=(const Aec3Fft&) = delete; + + // Computes the FFT. Note that both the input and output are modified. + void Fft(std::array* x, FftData* X) const { + RTC_DCHECK(x); + RTC_DCHECK(X); + ooura_fft_.Fft(x->data()); + X->CopyFromPackedArray(*x); + } + // Computes the inverse Fft. + void Ifft(const FftData& X, std::array* x) const { + RTC_DCHECK(x); + X.CopyToPackedArray(x); + ooura_fft_.InverseFft(x->data()); + } + + // Windows the input using a Hanning window, and then adds padding of + // kFftLengthBy2 initial zeros before computing the Fft. + void ZeroPaddedFft(ArrayView x, Window window, FftData* X) const; + + // Concatenates the kFftLengthBy2 values long x and x_old before computing the + // Fft. After that, x is copied to x_old. + void PaddedFft(ArrayView x, + ArrayView x_old, + FftData* X) const { + PaddedFft(x, x_old, Window::kRectangular, X); + } + + // Padded Fft using a time-domain window. + void PaddedFft(ArrayView x, + ArrayView x_old, + Window window, + FftData* X) const; + + private: + const OouraFft ooura_fft_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_AEC3_FFT_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/aec_state.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/aec_state.cc new file mode 100644 index 00000000..cc8642dc --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/aec_state.cc @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/aec_state.h" + +#include + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +bool DeactivateInitialStateResetAtEchoPathChange( + const FieldTrialsView& field_trials) { + return field_trials.IsEnabled( + "WebRTC-Aec3DeactivateInitialStateResetKillSwitch"); +} + +bool FullResetAtEchoPathChange(const FieldTrialsView& field_trials) { + return !field_trials.IsEnabled("WebRTC-Aec3AecStateFullResetKillSwitch"); +} + +bool SubtractorAnalyzerResetAtEchoPathChange( + const FieldTrialsView& field_trials) { + return !field_trials.IsEnabled( + "WebRTC-Aec3AecStateSubtractorAnalyzerResetKillSwitch"); +} + +void ComputeAvgRenderReverb( + const SpectrumBuffer& spectrum_buffer, + int delay_blocks, + float reverb_decay, + ReverbModel* reverb_model, + ArrayView reverb_power_spectrum) { + RTC_DCHECK(reverb_model); + const size_t num_render_channels = spectrum_buffer.buffer[0].size(); + int idx_at_delay = + spectrum_buffer.OffsetIndex(spectrum_buffer.read, delay_blocks); + int idx_past = spectrum_buffer.IncIndex(idx_at_delay); + + std::array X2_data; + ArrayView X2; + if (num_render_channels > 1) { + auto average_channels = + [](size_t num_render_channels, + ArrayView> + spectrum_band_0, + ArrayView render_power) { + std::fill(render_power.begin(), render_power.end(), 0.f); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + render_power[k] += spectrum_band_0[ch][k]; + } + } + const float normalizer = 1.f / num_render_channels; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + render_power[k] *= normalizer; + } + }; + average_channels(num_render_channels, spectrum_buffer.buffer[idx_past], + X2_data); + reverb_model->UpdateReverbNoFreqShaping( + X2_data, /*power_spectrum_scaling=*/1.0f, reverb_decay); + + average_channels(num_render_channels, spectrum_buffer.buffer[idx_at_delay], + X2_data); + X2 = X2_data; + } else { + reverb_model->UpdateReverbNoFreqShaping( + spectrum_buffer.buffer[idx_past][/*channel=*/0], + /*power_spectrum_scaling=*/1.0f, reverb_decay); + + X2 = spectrum_buffer.buffer[idx_at_delay][/*channel=*/0]; + } + + ArrayView reverb_power = + reverb_model->reverb(); + for (size_t k = 0; k < X2.size(); ++k) { + reverb_power_spectrum[k] = X2[k] + reverb_power[k]; + } +} + +} // namespace + +std::atomic AecState::instance_count_(0); + +void AecState::GetResidualEchoScaling(ArrayView residual_scaling) const { + bool filter_has_had_time_to_converge; + if (config_.filter.conservative_initial_phase) { + filter_has_had_time_to_converge = + strong_not_saturated_render_blocks_ >= 1.5f * kNumBlocksPerSecond; + } else { + filter_has_had_time_to_converge = + strong_not_saturated_render_blocks_ >= 0.8f * kNumBlocksPerSecond; + } + echo_audibility_.GetResidualEchoScaling(filter_has_had_time_to_converge, + residual_scaling); +} + +AecState::AecState(const Environment& env, + const EchoCanceller3Config& config, + size_t num_capture_channels) + : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), + config_(config), + num_capture_channels_(num_capture_channels), + deactivate_initial_state_reset_at_echo_path_change_( + DeactivateInitialStateResetAtEchoPathChange(env.field_trials())), + full_reset_at_echo_path_change_( + FullResetAtEchoPathChange(env.field_trials())), + subtractor_analyzer_reset_at_echo_path_change_( + SubtractorAnalyzerResetAtEchoPathChange(env.field_trials())), + initial_state_(config_), + delay_state_(config_, num_capture_channels_), + transparent_state_(TransparentMode::Create(env, config_)), + filter_quality_state_(config_, num_capture_channels_), + erl_estimator_(2 * kNumBlocksPerSecond), + erle_estimator_(env, + 2 * kNumBlocksPerSecond, + config_, + num_capture_channels_), + filter_analyzer_(config_, num_capture_channels_), + echo_audibility_( + config_.echo_audibility.use_stationarity_properties_at_init), + reverb_model_estimator_(config_, num_capture_channels_), + subtractor_output_analyzer_(num_capture_channels_) {} + +AecState::~AecState() = default; + +void AecState::HandleEchoPathChange( + const EchoPathVariability& echo_path_variability) { + const auto full_reset = [&]() { + filter_analyzer_.Reset(); + capture_signal_saturation_ = false; + strong_not_saturated_render_blocks_ = 0; + blocks_with_active_render_ = 0; + if (!deactivate_initial_state_reset_at_echo_path_change_) { + initial_state_.Reset(); + } + if (transparent_state_) { + transparent_state_->Reset(); + } + erle_estimator_.Reset(true); + erl_estimator_.Reset(); + filter_quality_state_.Reset(); + }; + + // TODO(peah): Refine the reset scheme according to the type of gain and + // delay adjustment. + + if (full_reset_at_echo_path_change_ && + echo_path_variability.delay_change != + EchoPathVariability::DelayAdjustment::kNone) { + full_reset(); + } else if (echo_path_variability.gain_change) { + erle_estimator_.Reset(false); + } + if (subtractor_analyzer_reset_at_echo_path_change_) { + subtractor_output_analyzer_.HandleEchoPathChange(); + } +} + +void AecState::Update( + const std::optional& external_delay, + ArrayView>> + adaptive_filter_frequency_responses, + ArrayView> adaptive_filter_impulse_responses, + const RenderBuffer& render_buffer, + ArrayView> E2_refined, + ArrayView> Y2, + ArrayView subtractor_output) { + RTC_DCHECK_EQ(num_capture_channels_, Y2.size()); + RTC_DCHECK_EQ(num_capture_channels_, subtractor_output.size()); + RTC_DCHECK_EQ(num_capture_channels_, + adaptive_filter_frequency_responses.size()); + RTC_DCHECK_EQ(num_capture_channels_, + adaptive_filter_impulse_responses.size()); + + // Analyze the filter outputs and filters. + bool any_filter_converged; + bool any_coarse_filter_converged; + bool all_filters_diverged; + subtractor_output_analyzer_.Update(subtractor_output, &any_filter_converged, + &any_coarse_filter_converged, + &all_filters_diverged); + + bool any_filter_consistent; + float max_echo_path_gain; + filter_analyzer_.Update(adaptive_filter_impulse_responses, render_buffer, + &any_filter_consistent, &max_echo_path_gain); + + // Estimate the direct path delay of the filter. + if (config_.filter.use_linear_filter) { + delay_state_.Update(filter_analyzer_.FilterDelaysBlocks(), external_delay, + strong_not_saturated_render_blocks_); + } + + const Block& aligned_render_block = + render_buffer.GetBlock(-delay_state_.MinDirectPathFilterDelay()); + + // Update render counters. + bool active_render = false; + for (int ch = 0; ch < aligned_render_block.NumChannels(); ++ch) { + const float render_energy = + std::inner_product(aligned_render_block.begin(/*block=*/0, ch), + aligned_render_block.end(/*block=*/0, ch), + aligned_render_block.begin(/*block=*/0, ch), 0.f); + if (render_energy > (config_.render_levels.active_render_limit * + config_.render_levels.active_render_limit) * + kFftLengthBy2) { + active_render = true; + break; + } + } + blocks_with_active_render_ += active_render ? 1 : 0; + strong_not_saturated_render_blocks_ += + active_render && !SaturatedCapture() ? 1 : 0; + + std::array avg_render_spectrum_with_reverb; + + ComputeAvgRenderReverb(render_buffer.GetSpectrumBuffer(), + delay_state_.MinDirectPathFilterDelay(), + ReverbDecay(/*mild=*/false), &avg_render_reverb_, + avg_render_spectrum_with_reverb); + + if (config_.echo_audibility.use_stationarity_properties) { + // Update the echo audibility evaluator. + echo_audibility_.Update(render_buffer, avg_render_reverb_.reverb(), + delay_state_.MinDirectPathFilterDelay(), + delay_state_.ExternalDelayReported()); + } + + // Update the ERL and ERLE measures. + if (initial_state_.TransitionTriggered()) { + erle_estimator_.Reset(false); + } + + erle_estimator_.Update(render_buffer, adaptive_filter_frequency_responses, + avg_render_spectrum_with_reverb, Y2, E2_refined, + subtractor_output_analyzer_.ConvergedFilters()); + + erl_estimator_.Update( + subtractor_output_analyzer_.ConvergedFilters(), + render_buffer.Spectrum(delay_state_.MinDirectPathFilterDelay()), Y2); + + // Detect and flag echo saturation. + if (config_.ep_strength.echo_can_saturate) { + saturation_detector_.Update(aligned_render_block, SaturatedCapture(), + UsableLinearEstimate(), subtractor_output, + max_echo_path_gain); + } else { + RTC_DCHECK(!saturation_detector_.SaturatedEcho()); + } + + // Update the decision on whether to use the initial state parameter set. + initial_state_.Update(active_render, SaturatedCapture()); + + // Detect whether the transparent mode should be activated. + if (transparent_state_) { + transparent_state_->Update( + delay_state_.MinDirectPathFilterDelay(), any_filter_consistent, + any_filter_converged, any_coarse_filter_converged, all_filters_diverged, + active_render, SaturatedCapture()); + } + + // Analyze the quality of the filter. + filter_quality_state_.Update(active_render, TransparentModeActive(), + SaturatedCapture(), external_delay, + any_filter_converged); + + // Update the reverb estimate. + const bool stationary_block = + config_.echo_audibility.use_stationarity_properties && + echo_audibility_.IsBlockStationary(); + + reverb_model_estimator_.Update( + filter_analyzer_.GetAdjustedFilters(), + adaptive_filter_frequency_responses, + erle_estimator_.GetInstLinearQualityEstimates(), + delay_state_.DirectPathFilterDelays(), + filter_quality_state_.UsableLinearFilterOutputs(), stationary_block); + + erle_estimator_.Dump(data_dumper_); + reverb_model_estimator_.Dump(data_dumper_.get()); + data_dumper_->DumpRaw("aec3_active_render", active_render); + data_dumper_->DumpRaw("aec3_erl", Erl()); + data_dumper_->DumpRaw("aec3_erl_time_domain", ErlTimeDomain()); + data_dumper_->DumpRaw("aec3_erle", Erle(/*onset_compensated=*/false)[0]); + data_dumper_->DumpRaw("aec3_erle_onset_compensated", + Erle(/*onset_compensated=*/true)[0]); + data_dumper_->DumpRaw("aec3_usable_linear_estimate", UsableLinearEstimate()); + data_dumper_->DumpRaw("aec3_transparent_mode", TransparentModeActive()); + data_dumper_->DumpRaw("aec3_filter_delay", + filter_analyzer_.MinFilterDelayBlocks()); + + data_dumper_->DumpRaw("aec3_any_filter_consistent", any_filter_consistent); + data_dumper_->DumpRaw("aec3_initial_state", + initial_state_.InitialStateActive()); + data_dumper_->DumpRaw("aec3_capture_saturation", SaturatedCapture()); + data_dumper_->DumpRaw("aec3_echo_saturation", SaturatedEcho()); + data_dumper_->DumpRaw("aec3_any_filter_converged", any_filter_converged); + data_dumper_->DumpRaw("aec3_any_coarse_filter_converged", + any_coarse_filter_converged); + data_dumper_->DumpRaw("aec3_all_filters_diverged", all_filters_diverged); + + data_dumper_->DumpRaw("aec3_external_delay_avaliable", + external_delay ? 1 : 0); + data_dumper_->DumpRaw("aec3_filter_tail_freq_resp_est", + GetReverbFrequencyResponse()); + data_dumper_->DumpRaw("aec3_subtractor_y2", subtractor_output[0].y2); + data_dumper_->DumpRaw("aec3_subtractor_e2_coarse", + subtractor_output[0].e2_coarse); + data_dumper_->DumpRaw("aec3_subtractor_e2_refined", + subtractor_output[0].e2_refined); +} + +AecState::InitialState::InitialState(const EchoCanceller3Config& config) + : conservative_initial_phase_(config.filter.conservative_initial_phase), + initial_state_seconds_(config.filter.initial_state_seconds) { + Reset(); +} +void AecState::InitialState::InitialState::Reset() { + initial_state_ = true; + strong_not_saturated_render_blocks_ = 0; +} +void AecState::InitialState::InitialState::Update(bool active_render, + bool saturated_capture) { + strong_not_saturated_render_blocks_ += + active_render && !saturated_capture ? 1 : 0; + + // Flag whether the initial state is still active. + bool prev_initial_state = initial_state_; + if (conservative_initial_phase_) { + initial_state_ = + strong_not_saturated_render_blocks_ < 5 * kNumBlocksPerSecond; + } else { + initial_state_ = strong_not_saturated_render_blocks_ < + initial_state_seconds_ * kNumBlocksPerSecond; + } + + // Flag whether the transition from the initial state has started. + transition_triggered_ = !initial_state_ && prev_initial_state; +} + +AecState::FilterDelay::FilterDelay(const EchoCanceller3Config& config, + size_t num_capture_channels) + : delay_headroom_blocks_(config.delay.delay_headroom_samples / kBlockSize), + filter_delays_blocks_(num_capture_channels, delay_headroom_blocks_), + min_filter_delay_(delay_headroom_blocks_) {} + +void AecState::FilterDelay::Update( + ArrayView analyzer_filter_delay_estimates_blocks, + const std::optional& external_delay, + size_t blocks_with_proper_filter_adaptation) { + // Update the delay based on the external delay. + if (external_delay && + (!external_delay_ || external_delay_->delay != external_delay->delay)) { + external_delay_ = external_delay; + external_delay_reported_ = true; + } + + // Override the estimated delay if it is not certain that the filter has had + // time to converge. + const bool delay_estimator_may_not_have_converged = + blocks_with_proper_filter_adaptation < 2 * kNumBlocksPerSecond; + if (delay_estimator_may_not_have_converged && external_delay_) { + const int delay_guess = delay_headroom_blocks_; + std::fill(filter_delays_blocks_.begin(), filter_delays_blocks_.end(), + delay_guess); + } else { + RTC_DCHECK_EQ(filter_delays_blocks_.size(), + analyzer_filter_delay_estimates_blocks.size()); + std::copy(analyzer_filter_delay_estimates_blocks.begin(), + analyzer_filter_delay_estimates_blocks.end(), + filter_delays_blocks_.begin()); + } + + min_filter_delay_ = *std::min_element(filter_delays_blocks_.begin(), + filter_delays_blocks_.end()); +} + +AecState::FilteringQualityAnalyzer::FilteringQualityAnalyzer( + const EchoCanceller3Config& config, + size_t num_capture_channels) + : use_linear_filter_(config.filter.use_linear_filter), + usable_linear_filter_estimates_(num_capture_channels, false) {} + +void AecState::FilteringQualityAnalyzer::Reset() { + std::fill(usable_linear_filter_estimates_.begin(), + usable_linear_filter_estimates_.end(), false); + overall_usable_linear_estimates_ = false; + filter_update_blocks_since_reset_ = 0; +} + +void AecState::FilteringQualityAnalyzer::Update( + bool active_render, + bool transparent_mode, + bool saturated_capture, + const std::optional& external_delay, + bool any_filter_converged) { + // Update blocks counter. + const bool filter_update = active_render && !saturated_capture; + filter_update_blocks_since_reset_ += filter_update ? 1 : 0; + filter_update_blocks_since_start_ += filter_update ? 1 : 0; + + // Store convergence flag when observed. + convergence_seen_ = convergence_seen_ || any_filter_converged; + + // Verify requirements for achieving a decent filter. The requirements for + // filter adaptation at call startup are more restrictive than after an + // in-call reset. + const bool sufficient_data_to_converge_at_startup = + filter_update_blocks_since_start_ > kNumBlocksPerSecond * 0.4f; + const bool sufficient_data_to_converge_at_reset = + sufficient_data_to_converge_at_startup && + filter_update_blocks_since_reset_ > kNumBlocksPerSecond * 0.2f; + + // The linear filter can only be used if it has had time to converge. + overall_usable_linear_estimates_ = sufficient_data_to_converge_at_startup && + sufficient_data_to_converge_at_reset; + + // The linear filter can only be used if an external delay or convergence have + // been identified + overall_usable_linear_estimates_ = + overall_usable_linear_estimates_ && (external_delay || convergence_seen_); + + // If transparent mode is on, deactivate usign the linear filter. + overall_usable_linear_estimates_ = + overall_usable_linear_estimates_ && !transparent_mode; + + if (use_linear_filter_) { + std::fill(usable_linear_filter_estimates_.begin(), + usable_linear_filter_estimates_.end(), + overall_usable_linear_estimates_); + } +} + +void AecState::SaturationDetector::Update( + const Block& x, + bool saturated_capture, + bool usable_linear_estimate, + ArrayView subtractor_output, + float echo_path_gain) { + saturated_echo_ = false; + if (!saturated_capture) { + return; + } + + if (usable_linear_estimate) { + constexpr float kSaturationThreshold = 20000.f; + for (size_t ch = 0; ch < subtractor_output.size(); ++ch) { + saturated_echo_ = + saturated_echo_ || + (subtractor_output[ch].s_refined_max_abs > kSaturationThreshold || + subtractor_output[ch].s_coarse_max_abs > kSaturationThreshold); + } + } else { + float max_sample = 0.f; + for (int ch = 0; ch < x.NumChannels(); ++ch) { + ArrayView x_ch = x.View(/*band=*/0, ch); + for (float sample : x_ch) { + max_sample = std::max(max_sample, fabsf(sample)); + } + } + + const float kMargin = 10.f; + float peak_echo_amplitude = max_sample * echo_path_gain * kMargin; + saturated_echo_ = saturated_echo_ || peak_echo_amplitude > 32000; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/aec_state.h b/pkg/apm/webrtc/modules/audio_processing/aec3/aec_state.h new file mode 100644 index 00000000..bd7050c3 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/aec_state.h @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_AEC_STATE_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_AEC_STATE_H_ + +#include + +#include +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "api/environment/environment.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/echo_audibility.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/erl_estimator.h" +#include "modules/audio_processing/aec3/erle_estimator.h" +#include "modules/audio_processing/aec3/filter_analyzer.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/reverb_model_estimator.h" +#include "modules/audio_processing/aec3/subtractor_output.h" +#include "modules/audio_processing/aec3/subtractor_output_analyzer.h" +#include "modules/audio_processing/aec3/transparent_mode.h" + +namespace webrtc { + +class ApmDataDumper; + +// Handles the state and the conditions for the echo removal functionality. +class AecState { + public: + AecState(const Environment& env, + const EchoCanceller3Config& config, + size_t num_capture_channels); + ~AecState(); + + // Returns whether the echo subtractor can be used to determine the residual + // echo. + bool UsableLinearEstimate() const { + return filter_quality_state_.LinearFilterUsable() && + config_.filter.use_linear_filter; + } + + // Returns whether the echo subtractor output should be used as output. + bool UseLinearFilterOutput() const { + return filter_quality_state_.LinearFilterUsable() && + config_.filter.use_linear_filter; + } + + // Returns whether the render signal is currently active. + bool ActiveRender() const { return blocks_with_active_render_ > 200; } + + // Returns the appropriate scaling of the residual echo to match the + // audibility. + void GetResidualEchoScaling(ArrayView residual_scaling) const; + + // Returns whether the stationary properties of the signals are used in the + // aec. + bool UseStationarityProperties() const { + return config_.echo_audibility.use_stationarity_properties; + } + + // Returns the ERLE. + ArrayView> Erle( + bool onset_compensated) const { + return erle_estimator_.Erle(onset_compensated); + } + + // Returns the non-capped ERLE. + ArrayView> ErleUnbounded() const { + return erle_estimator_.ErleUnbounded(); + } + + // Returns the fullband ERLE estimate in log2 units. + float FullBandErleLog2() const { return erle_estimator_.FullbandErleLog2(); } + + // Returns the ERL. + const std::array& Erl() const { + return erl_estimator_.Erl(); + } + + // Returns the time-domain ERL. + float ErlTimeDomain() const { return erl_estimator_.ErlTimeDomain(); } + + // Returns the delay estimate based on the linear filter. + int MinDirectPathFilterDelay() const { + return delay_state_.MinDirectPathFilterDelay(); + } + + // Returns whether the capture signal is saturated. + bool SaturatedCapture() const { return capture_signal_saturation_; } + + // Returns whether the echo signal is saturated. + bool SaturatedEcho() const { return saturation_detector_.SaturatedEcho(); } + + // Updates the capture signal saturation. + void UpdateCaptureSaturation(bool capture_signal_saturation) { + capture_signal_saturation_ = capture_signal_saturation; + } + + // Returns whether the transparent mode is active + bool TransparentModeActive() const { + return transparent_state_ && transparent_state_->Active(); + } + + // Takes appropriate action at an echo path change. + void HandleEchoPathChange(const EchoPathVariability& echo_path_variability); + + // Returns the decay factor for the echo reverberation. The parameter `mild` + // indicates which exponential decay to return. The default one or a milder + // one that can be used during nearend regions. + float ReverbDecay(bool mild) const { + return reverb_model_estimator_.ReverbDecay(mild); + } + + // Return the frequency response of the reverberant echo. + ArrayView GetReverbFrequencyResponse() const { + return reverb_model_estimator_.GetReverbFrequencyResponse(); + } + + // Returns whether the transition for going out of the initial stated has + // been triggered. + bool TransitionTriggered() const { + return initial_state_.TransitionTriggered(); + } + + // Updates the aec state. + // TODO(bugs.webrtc.org/10913): Compute multi-channel ERL. + void Update( + const std::optional& external_delay, + ArrayView>> + adaptive_filter_frequency_responses, + ArrayView> adaptive_filter_impulse_responses, + const RenderBuffer& render_buffer, + ArrayView> E2_refined, + ArrayView> Y2, + ArrayView subtractor_output); + + // Returns filter length in blocks. + int FilterLengthBlocks() const { + // All filters have the same length, so arbitrarily return channel 0 length. + return filter_analyzer_.FilterLengthBlocks(); + } + + private: + static std::atomic instance_count_; + std::unique_ptr data_dumper_; + const EchoCanceller3Config config_; + const size_t num_capture_channels_; + const bool deactivate_initial_state_reset_at_echo_path_change_; + const bool full_reset_at_echo_path_change_; + const bool subtractor_analyzer_reset_at_echo_path_change_; + + // Class for controlling the transition from the intial state, which in turn + // controls when the filter parameters for the initial state should be used. + class InitialState { + public: + explicit InitialState(const EchoCanceller3Config& config); + // Resets the state to again begin in the initial state. + void Reset(); + + // Updates the state based on new data. + void Update(bool active_render, bool saturated_capture); + + // Returns whether the initial state is active or not. + bool InitialStateActive() const { return initial_state_; } + + // Returns that the transition from the initial state has was started. + bool TransitionTriggered() const { return transition_triggered_; } + + private: + const bool conservative_initial_phase_; + const float initial_state_seconds_; + bool transition_triggered_ = false; + bool initial_state_ = true; + size_t strong_not_saturated_render_blocks_ = 0; + } initial_state_; + + // Class for choosing the direct-path delay relative to the beginning of the + // filter, as well as any other data related to the delay used within + // AecState. + class FilterDelay { + public: + FilterDelay(const EchoCanceller3Config& config, + size_t num_capture_channels); + + // Returns whether an external delay has been reported to the AecState (from + // the delay estimator). + bool ExternalDelayReported() const { return external_delay_reported_; } + + // Returns the delay in blocks relative to the beginning of the filter that + // corresponds to the direct path of the echo. + ArrayView DirectPathFilterDelays() const { + return filter_delays_blocks_; + } + + // Returns the minimum delay among the direct path delays relative to the + // beginning of the filter + int MinDirectPathFilterDelay() const { return min_filter_delay_; } + + // Updates the delay estimates based on new data. + void Update(ArrayView analyzer_filter_delay_estimates_blocks, + const std::optional& external_delay, + size_t blocks_with_proper_filter_adaptation); + + private: + const int delay_headroom_blocks_; + bool external_delay_reported_ = false; + std::vector filter_delays_blocks_; + int min_filter_delay_; + std::optional external_delay_; + } delay_state_; + + // Classifier for toggling transparent mode when there is no echo. + std::unique_ptr transparent_state_; + + // Class for analyzing how well the linear filter is, and can be expected to, + // perform on the current signals. The purpose of this is for using to + // select the echo suppression functionality as well as the input to the echo + // suppressor. + class FilteringQualityAnalyzer { + public: + FilteringQualityAnalyzer(const EchoCanceller3Config& config, + size_t num_capture_channels); + + // Returns whether the linear filter can be used for the echo + // canceller output. + bool LinearFilterUsable() const { return overall_usable_linear_estimates_; } + + // Returns whether an individual filter output can be used for the echo + // canceller output. + const std::vector& UsableLinearFilterOutputs() const { + return usable_linear_filter_estimates_; + } + + // Resets the state of the analyzer. + void Reset(); + + // Updates the analysis based on new data. + void Update(bool active_render, + bool transparent_mode, + bool saturated_capture, + const std::optional& external_delay, + bool any_filter_converged); + + private: + const bool use_linear_filter_; + bool overall_usable_linear_estimates_ = false; + size_t filter_update_blocks_since_reset_ = 0; + size_t filter_update_blocks_since_start_ = 0; + bool convergence_seen_ = false; + std::vector usable_linear_filter_estimates_; + } filter_quality_state_; + + // Class for detecting whether the echo is to be considered to be + // saturated. + class SaturationDetector { + public: + // Returns whether the echo is to be considered saturated. + bool SaturatedEcho() const { return saturated_echo_; } + + // Updates the detection decision based on new data. + void Update(const Block& x, + bool saturated_capture, + bool usable_linear_estimate, + ArrayView subtractor_output, + float echo_path_gain); + + private: + bool saturated_echo_ = false; + } saturation_detector_; + + ErlEstimator erl_estimator_; + ErleEstimator erle_estimator_; + size_t strong_not_saturated_render_blocks_ = 0; + size_t blocks_with_active_render_ = 0; + bool capture_signal_saturation_ = false; + FilterAnalyzer filter_analyzer_; + EchoAudibility echo_audibility_; + ReverbModelEstimator reverb_model_estimator_; + ReverbModel avg_render_reverb_; + SubtractorOutputAnalyzer subtractor_output_analyzer_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_AEC_STATE_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/alignment_mixer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/alignment_mixer.cc new file mode 100644 index 00000000..728e0f7b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/alignment_mixer.cc @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/aec3/alignment_mixer.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +AlignmentMixer::MixingVariant ChooseMixingVariant(bool downmix, + bool adaptive_selection, + int num_channels) { + RTC_DCHECK(!(adaptive_selection && downmix)); + RTC_DCHECK_LT(0, num_channels); + + if (num_channels == 1) { + return AlignmentMixer::MixingVariant::kFixed; + } + if (downmix) { + return AlignmentMixer::MixingVariant::kDownmix; + } + if (adaptive_selection) { + return AlignmentMixer::MixingVariant::kAdaptive; + } + return AlignmentMixer::MixingVariant::kFixed; +} + +} // namespace + +AlignmentMixer::AlignmentMixer( + size_t num_channels, + const EchoCanceller3Config::Delay::AlignmentMixing& config) + : AlignmentMixer(num_channels, + config.downmix, + config.adaptive_selection, + config.activity_power_threshold, + config.prefer_first_two_channels) {} + +AlignmentMixer::AlignmentMixer(size_t num_channels, + bool downmix, + bool adaptive_selection, + float activity_power_threshold, + bool prefer_first_two_channels) + : num_channels_(num_channels), + one_by_num_channels_(1.f / num_channels_), + excitation_energy_threshold_(kBlockSize * activity_power_threshold), + prefer_first_two_channels_(prefer_first_two_channels), + selection_variant_( + ChooseMixingVariant(downmix, adaptive_selection, num_channels_)) { + if (selection_variant_ == MixingVariant::kAdaptive) { + std::fill(strong_block_counters_.begin(), strong_block_counters_.end(), 0); + cumulative_energies_.resize(num_channels_); + std::fill(cumulative_energies_.begin(), cumulative_energies_.end(), 0.f); + } +} + +void AlignmentMixer::ProduceOutput(const Block& x, + ArrayView y) { + RTC_DCHECK_EQ(x.NumChannels(), num_channels_); + + if (selection_variant_ == MixingVariant::kDownmix) { + Downmix(x, y); + return; + } + + int ch = selection_variant_ == MixingVariant::kFixed ? 0 : SelectChannel(x); + + RTC_DCHECK_GT(x.NumChannels(), ch); + std::copy(x.begin(/*band=*/0, ch), x.end(/*band=*/0, ch), y.begin()); +} + +void AlignmentMixer::Downmix(const Block& x, + ArrayView y) const { + RTC_DCHECK_EQ(x.NumChannels(), num_channels_); + RTC_DCHECK_GE(num_channels_, 2); + std::memcpy(&y[0], x.View(/*band=*/0, /*channel=*/0).data(), + kBlockSize * sizeof(y[0])); + for (size_t ch = 1; ch < num_channels_; ++ch) { + const auto x_ch = x.View(/*band=*/0, ch); + for (size_t i = 0; i < kBlockSize; ++i) { + y[i] += x_ch[i]; + } + } + + for (size_t i = 0; i < kBlockSize; ++i) { + y[i] *= one_by_num_channels_; + } +} + +int AlignmentMixer::SelectChannel(const Block& x) { + RTC_DCHECK_EQ(x.NumChannels(), num_channels_); + RTC_DCHECK_GE(num_channels_, 2); + RTC_DCHECK_EQ(cumulative_energies_.size(), num_channels_); + + constexpr size_t kBlocksToChooseLeftOrRight = + static_cast(0.5f * kNumBlocksPerSecond); + const bool good_signal_in_left_or_right = + prefer_first_two_channels_ && + (strong_block_counters_[0] > kBlocksToChooseLeftOrRight || + strong_block_counters_[1] > kBlocksToChooseLeftOrRight); + + const int num_ch_to_analyze = + good_signal_in_left_or_right ? 2 : num_channels_; + + constexpr int kNumBlocksBeforeEnergySmoothing = 60 * kNumBlocksPerSecond; + ++block_counter_; + + for (int ch = 0; ch < num_ch_to_analyze; ++ch) { + float x2_sum = 0.f; + ArrayView x_ch = x.View(/*band=*/0, ch); + for (size_t i = 0; i < kBlockSize; ++i) { + x2_sum += x_ch[i] * x_ch[i]; + } + + if (ch < 2 && x2_sum > excitation_energy_threshold_) { + ++strong_block_counters_[ch]; + } + + if (block_counter_ <= kNumBlocksBeforeEnergySmoothing) { + cumulative_energies_[ch] += x2_sum; + } else { + constexpr float kSmoothing = 1.f / (10 * kNumBlocksPerSecond); + cumulative_energies_[ch] += + kSmoothing * (x2_sum - cumulative_energies_[ch]); + } + } + + // Normalize the energies to allow the energy computations to from now be + // based on smoothing. + if (block_counter_ == kNumBlocksBeforeEnergySmoothing) { + constexpr float kOneByNumBlocksBeforeEnergySmoothing = + 1.f / kNumBlocksBeforeEnergySmoothing; + for (int ch = 0; ch < num_ch_to_analyze; ++ch) { + cumulative_energies_[ch] *= kOneByNumBlocksBeforeEnergySmoothing; + } + } + + int strongest_ch = 0; + for (int ch = 0; ch < num_ch_to_analyze; ++ch) { + if (cumulative_energies_[ch] > cumulative_energies_[strongest_ch]) { + strongest_ch = ch; + } + } + + if ((good_signal_in_left_or_right && selected_channel_ > 1) || + cumulative_energies_[strongest_ch] > + 2.f * cumulative_energies_[selected_channel_]) { + selected_channel_ = strongest_ch; + } + + return selected_channel_; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/alignment_mixer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/alignment_mixer.h new file mode 100644 index 00000000..238f9a0e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/alignment_mixer.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ALIGNMENT_MIXER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ALIGNMENT_MIXER_H_ + +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/block.h" + +namespace webrtc { + +// Performs channel conversion to mono for the purpose of providing a decent +// mono input for the delay estimation. This is achieved by analyzing all +// incoming channels and produce one single channel output. +class AlignmentMixer { + public: + AlignmentMixer(size_t num_channels, + const EchoCanceller3Config::Delay::AlignmentMixing& config); + + AlignmentMixer(size_t num_channels, + bool downmix, + bool adaptive_selection, + float excitation_limit, + bool prefer_first_two_channels); + + void ProduceOutput(const Block& x, ArrayView y); + + enum class MixingVariant { kDownmix, kAdaptive, kFixed }; + + private: + const size_t num_channels_; + const float one_by_num_channels_; + const float excitation_energy_threshold_; + const bool prefer_first_two_channels_; + const MixingVariant selection_variant_; + std::array strong_block_counters_; + std::vector cumulative_energies_; + int selected_channel_ = 0; + size_t block_counter_ = 0; + + void Downmix(const Block& x, ArrayView y) const; + int SelectChannel(const Block& x); +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ALIGNMENT_MIXER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.cc new file mode 100644 index 00000000..45f56a5d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.cc @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/api_call_jitter_metrics.h" + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { +namespace { + +bool TimeToReportMetrics(int frames_since_last_report) { + constexpr int kNumFramesPerSecond = 100; + constexpr int kReportingIntervalFrames = 10 * kNumFramesPerSecond; + return frames_since_last_report == kReportingIntervalFrames; +} + +} // namespace + +ApiCallJitterMetrics::Jitter::Jitter() + : max_(0), min_(std::numeric_limits::max()) {} + +void ApiCallJitterMetrics::Jitter::Update(int num_api_calls_in_a_row) { + min_ = std::min(min_, num_api_calls_in_a_row); + max_ = std::max(max_, num_api_calls_in_a_row); +} + +void ApiCallJitterMetrics::Jitter::Reset() { + min_ = std::numeric_limits::max(); + max_ = 0; +} + +void ApiCallJitterMetrics::Reset() { + render_jitter_.Reset(); + capture_jitter_.Reset(); + num_api_calls_in_a_row_ = 0; + frames_since_last_report_ = 0; + last_call_was_render_ = false; + proper_call_observed_ = false; +} + +void ApiCallJitterMetrics::ReportRenderCall() { + if (!last_call_was_render_) { + // If the previous call was a capture and a proper call has been observed + // (containing both render and capture data), storing the last number of + // capture calls into the metrics. + if (proper_call_observed_) { + capture_jitter_.Update(num_api_calls_in_a_row_); + } + + // Reset the call counter to start counting render calls. + num_api_calls_in_a_row_ = 0; + } + ++num_api_calls_in_a_row_; + last_call_was_render_ = true; +} + +void ApiCallJitterMetrics::ReportCaptureCall() { + if (last_call_was_render_) { + // If the previous call was a render and a proper call has been observed + // (containing both render and capture data), storing the last number of + // render calls into the metrics. + if (proper_call_observed_) { + render_jitter_.Update(num_api_calls_in_a_row_); + } + // Reset the call counter to start counting capture calls. + num_api_calls_in_a_row_ = 0; + + // If this statement is reached, at least one render and one capture call + // have been observed. + proper_call_observed_ = true; + } + ++num_api_calls_in_a_row_; + last_call_was_render_ = false; + + // Only report and update jitter metrics for when a proper call, containing + // both render and capture data, has been observed. + if (proper_call_observed_ && + TimeToReportMetrics(++frames_since_last_report_)) { + // Report jitter, where the base basic unit is frames. + constexpr int kMaxJitterToReport = 50; + + // Report max and min jitter for render and capture, in units of 20 ms. + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.MaxRenderJitter", + std::min(kMaxJitterToReport, render_jitter().max()), 1, + kMaxJitterToReport, kMaxJitterToReport); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.MinRenderJitter", + std::min(kMaxJitterToReport, render_jitter().min()), 1, + kMaxJitterToReport, kMaxJitterToReport); + + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.MaxCaptureJitter", + std::min(kMaxJitterToReport, capture_jitter().max()), 1, + kMaxJitterToReport, kMaxJitterToReport); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.MinCaptureJitter", + std::min(kMaxJitterToReport, capture_jitter().min()), 1, + kMaxJitterToReport, kMaxJitterToReport); + + frames_since_last_report_ = 0; + Reset(); + } +} + +bool ApiCallJitterMetrics::WillReportMetricsAtNextCapture() const { + return TimeToReportMetrics(frames_since_last_report_ + 1); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.h b/pkg/apm/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.h new file mode 100644 index 00000000..dd1fa82e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_API_CALL_JITTER_METRICS_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_API_CALL_JITTER_METRICS_H_ + +namespace webrtc { + +// Stores data for reporting metrics on the API call jitter. +class ApiCallJitterMetrics { + public: + class Jitter { + public: + Jitter(); + void Update(int num_api_calls_in_a_row); + void Reset(); + + int min() const { return min_; } + int max() const { return max_; } + + private: + int max_; + int min_; + }; + + ApiCallJitterMetrics() { Reset(); } + + // Update metrics for render API call. + void ReportRenderCall(); + + // Update and periodically report metrics for capture API call. + void ReportCaptureCall(); + + // Methods used only for testing. + const Jitter& render_jitter() const { return render_jitter_; } + const Jitter& capture_jitter() const { return capture_jitter_; } + bool WillReportMetricsAtNextCapture() const; + + private: + void Reset(); + + Jitter render_jitter_; + Jitter capture_jitter_; + + int num_api_calls_in_a_row_ = 0; + int frames_since_last_report_ = 0; + bool last_call_was_render_ = false; + bool proper_call_observed_ = false; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_API_CALL_JITTER_METRICS_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/adaptive_fir_filter_avx2.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/adaptive_fir_filter_avx2.cc new file mode 100644 index 00000000..9f720a5a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/adaptive_fir_filter_avx2.cc @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "modules/audio_processing/aec3/adaptive_fir_filter.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace aec3 { + +// Computes and stores the frequency response of the filter. +void ComputeFrequencyResponse_Avx2( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2) { + for (auto& H2_ch : *H2) { + H2_ch.fill(0.f); + } + + const size_t num_render_channels = H[0].size(); + RTC_DCHECK_EQ(H.size(), H2->capacity()); + for (size_t p = 0; p < num_partitions; ++p) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, (*H2)[p].size()); + auto& H2_p = (*H2)[p]; + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + for (size_t j = 0; j < kFftLengthBy2; j += 8) { + __m256 re = _mm256_loadu_ps(&H_p_ch.re[j]); + __m256 re2 = _mm256_mul_ps(re, re); + __m256 im = _mm256_loadu_ps(&H_p_ch.im[j]); + re2 = _mm256_fmadd_ps(im, im, re2); + __m256 H2_k_j = _mm256_loadu_ps(&H2_p[j]); + H2_k_j = _mm256_max_ps(H2_k_j, re2); + _mm256_storeu_ps(&H2_p[j], H2_k_j); + } + float H2_new = H_p_ch.re[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2] + + H_p_ch.im[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2]; + H2_p[kFftLengthBy2] = std::max(H2_p[kFftLengthBy2], H2_new); + } + } +} + +// Adapts the filter partitions. +void AdaptPartitions_Avx2(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H) { + ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumEightBinBands = kFftLengthBy2 / 8; + + size_t X_partition = render_buffer.Position(); + size_t limit = lim1; + size_t p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + + for (size_t k = 0, n = 0; n < kNumEightBinBands; ++n, k += 8) { + const __m256 G_re = _mm256_loadu_ps(&G.re[k]); + const __m256 G_im = _mm256_loadu_ps(&G.im[k]); + const __m256 X_re = _mm256_loadu_ps(&X.re[k]); + const __m256 X_im = _mm256_loadu_ps(&X.im[k]); + const __m256 H_re = _mm256_loadu_ps(&H_p_ch.re[k]); + const __m256 H_im = _mm256_loadu_ps(&H_p_ch.im[k]); + const __m256 a = _mm256_mul_ps(X_re, G_re); + const __m256 b = _mm256_mul_ps(X_im, G_im); + const __m256 c = _mm256_mul_ps(X_re, G_im); + const __m256 d = _mm256_mul_ps(X_im, G_re); + const __m256 e = _mm256_add_ps(a, b); + const __m256 f = _mm256_sub_ps(c, d); + const __m256 g = _mm256_add_ps(H_re, e); + const __m256 h = _mm256_add_ps(H_im, f); + _mm256_storeu_ps(&H_p_ch.re[k], g); + _mm256_storeu_ps(&H_p_ch.im[k], h); + } + } + } + X_partition = 0; + limit = lim2; + } while (p < lim2); + + X_partition = render_buffer.Position(); + limit = lim1; + p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + + H_p_ch.re[kFftLengthBy2] += X.re[kFftLengthBy2] * G.re[kFftLengthBy2] + + X.im[kFftLengthBy2] * G.im[kFftLengthBy2]; + H_p_ch.im[kFftLengthBy2] += X.re[kFftLengthBy2] * G.im[kFftLengthBy2] - + X.im[kFftLengthBy2] * G.re[kFftLengthBy2]; + } + } + + X_partition = 0; + limit = lim2; + } while (p < lim2); +} + +// Produces the filter output (AVX2 variant). +void ApplyFilter_Avx2(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S) { + RTC_DCHECK_GE(H.size(), H.size() - 1); + S->re.fill(0.f); + S->im.fill(0.f); + + ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumEightBinBands = kFftLengthBy2 / 8; + + size_t X_partition = render_buffer.Position(); + size_t p = 0; + size_t limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + for (size_t k = 0, n = 0; n < kNumEightBinBands; ++n, k += 8) { + const __m256 X_re = _mm256_loadu_ps(&X.re[k]); + const __m256 X_im = _mm256_loadu_ps(&X.im[k]); + const __m256 H_re = _mm256_loadu_ps(&H_p_ch.re[k]); + const __m256 H_im = _mm256_loadu_ps(&H_p_ch.im[k]); + const __m256 S_re = _mm256_loadu_ps(&S->re[k]); + const __m256 S_im = _mm256_loadu_ps(&S->im[k]); + const __m256 a = _mm256_mul_ps(X_re, H_re); + const __m256 b = _mm256_mul_ps(X_im, H_im); + const __m256 c = _mm256_mul_ps(X_re, H_im); + const __m256 d = _mm256_mul_ps(X_im, H_re); + const __m256 e = _mm256_sub_ps(a, b); + const __m256 f = _mm256_add_ps(c, d); + const __m256 g = _mm256_add_ps(S_re, e); + const __m256 h = _mm256_add_ps(S_im, f); + _mm256_storeu_ps(&S->re[k], g); + _mm256_storeu_ps(&S->im[k], h); + } + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); + + X_partition = render_buffer.Position(); + p = 0; + limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + S->re[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2] - + X.im[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2]; + S->im[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2] + + X.im[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2]; + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); +} + +} // namespace aec3 +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/adaptive_fir_filter_erl_avx2.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/adaptive_fir_filter_erl_avx2.cc new file mode 100644 index 00000000..dbcaa9ac --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/adaptive_fir_filter_erl_avx2.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "modules/audio_processing/aec3/adaptive_fir_filter_erl.h" + +namespace webrtc { + +namespace aec3 { + +// Computes and stores the echo return loss estimate of the filter, which is the +// sum of the partition frequency responses. +void ErlComputer_AVX2( + const std::vector>& H2, + ArrayView erl) { + std::fill(erl.begin(), erl.end(), 0.f); + for (auto& H2_j : H2) { + for (size_t k = 0; k < kFftLengthBy2; k += 8) { + const __m256 H2_j_k = _mm256_loadu_ps(&H2_j[k]); + __m256 erl_k = _mm256_loadu_ps(&erl[k]); + erl_k = _mm256_add_ps(erl_k, H2_j_k); + _mm256_storeu_ps(&erl[k], erl_k); + } + erl[kFftLengthBy2] += H2_j[kFftLengthBy2]; + } +} + +} // namespace aec3 +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/avx2.go b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/avx2.go new file mode 100644 index 00000000..9bc38772 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/avx2.go @@ -0,0 +1,8 @@ +//go:build console && amd64 + +package avx2 + +// #cgo CXXFLAGS: -I${SRCDIR}/../../../.. -I${SRCDIR}/../../../../third_party/abseil-cpp -std=c++17 -fno-rtti -march=haswell -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/fft_data_avx2.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/fft_data_avx2.cc new file mode 100644 index 00000000..c604b56a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/fft_data_avx2.cc @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/fft_data.h" + +namespace webrtc { + +// Computes the power spectrum of the data. +void FftData::SpectrumAVX2(ArrayView power_spectrum) const { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, power_spectrum.size()); + for (size_t k = 0; k < kFftLengthBy2; k += 8) { + __m256 r = _mm256_loadu_ps(&re[k]); + __m256 i = _mm256_loadu_ps(&im[k]); + __m256 ii = _mm256_mul_ps(i, i); + ii = _mm256_fmadd_ps(r, r, ii); + _mm256_storeu_ps(&power_spectrum[k], ii); + } + power_spectrum[kFftLengthBy2] = re[kFftLengthBy2] * re[kFftLengthBy2] + + im[kFftLengthBy2] * im[kFftLengthBy2]; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/matched_filter_avx2.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/matched_filter_avx2.cc new file mode 100644 index 00000000..830ca96f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/matched_filter_avx2.cc @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/matched_filter.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace aec3 { + +// Let ha denote the horizontal of a, and hb the horizontal sum of b +// returns [ha, hb, ha, hb] +inline __m128 hsum_ab(__m256 a, __m256 b) { + __m256 s_256 = _mm256_hadd_ps(a, b); + const __m256i mask = _mm256_set_epi32(7, 6, 3, 2, 5, 4, 1, 0); + s_256 = _mm256_permutevar8x32_ps(s_256, mask); + __m128 s = _mm_hadd_ps(_mm256_extractf128_ps(s_256, 0), + _mm256_extractf128_ps(s_256, 1)); + s = _mm_hadd_ps(s, s); + return s; +} + +void MatchedFilterCore_AccumulatedError_AVX2(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + ArrayView x, + ArrayView y, + ArrayView h, + bool* filters_updated, + float* error_sum, + ArrayView accumulated_error, + ArrayView scratch_memory) { + const int h_size = static_cast(h.size()); + const int x_size = static_cast(x.size()); + RTC_DCHECK_EQ(0, h_size % 16); + std::fill(accumulated_error.begin(), accumulated_error.end(), 0.0f); + + // Process for all samples in the sub-block. + for (size_t i = 0; i < y.size(); ++i) { + // Apply the matched filter as filter * x, and compute x * x. + RTC_DCHECK_GT(x_size, x_start_index); + const int chunk1 = + std::min(h_size, static_cast(x_size - x_start_index)); + if (chunk1 != h_size) { + const int chunk2 = h_size - chunk1; + std::copy(x.begin() + x_start_index, x.end(), scratch_memory.begin()); + std::copy(x.begin(), x.begin() + chunk2, scratch_memory.begin() + chunk1); + } + const float* x_p = + chunk1 != h_size ? scratch_memory.data() : &x[x_start_index]; + const float* h_p = &h[0]; + float* a_p = &accumulated_error[0]; + __m256 s_inst_hadd_256; + __m256 s_inst_256; + __m256 s_inst_256_8; + __m256 x2_sum_256 = _mm256_set1_ps(0); + __m256 x2_sum_256_8 = _mm256_set1_ps(0); + __m128 e_128; + float x2_sum = 0.0f; + float s_acum = 0; + const int limit_by_16 = h_size >> 4; + for (int k = limit_by_16; k > 0; --k, h_p += 16, x_p += 16, a_p += 4) { + // Load the data into 256 bit vectors. + __m256 x_k = _mm256_loadu_ps(x_p); + __m256 h_k = _mm256_loadu_ps(h_p); + __m256 x_k_8 = _mm256_loadu_ps(x_p + 8); + __m256 h_k_8 = _mm256_loadu_ps(h_p + 8); + // Compute and accumulate x * x and h * x. + x2_sum_256 = _mm256_fmadd_ps(x_k, x_k, x2_sum_256); + x2_sum_256_8 = _mm256_fmadd_ps(x_k_8, x_k_8, x2_sum_256_8); + s_inst_256 = _mm256_mul_ps(h_k, x_k); + 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]; + + __m128 acum_error = _mm_load_ps(a_p); + acum_error = _mm_fmadd_ps(e_128, e_128, acum_error); + _mm_storeu_ps(a_p, acum_error); + } + // Sum components together. + x2_sum_256 = _mm256_add_ps(x2_sum_256, x2_sum_256_8); + __m128 x2_sum_128 = _mm_add_ps(_mm256_extractf128_ps(x2_sum_256, 0), + _mm256_extractf128_ps(x2_sum_256, 1)); + // Combine the accumulated vector and scalar values. + float* v = reinterpret_cast(&x2_sum_128); + x2_sum += v[0] + v[1] + v[2] + v[3]; + + // Compute the matched filter error. + float e = y[i] - s_acum; + const bool saturation = y[i] >= 32000.f || y[i] <= -32000.f; + (*error_sum) += e * e; + + // Update the matched filter estimate in an NLMS manner. + if (x2_sum > x2_sum_threshold && !saturation) { + RTC_DCHECK_LT(0.f, x2_sum); + const float alpha = smoothing * e / x2_sum; + const __m256 alpha_256 = _mm256_set1_ps(alpha); + + // filter = filter + smoothing * (y - filter * x) * x / x * x. + float* h_p2 = &h[0]; + const float* x_p2 = + chunk1 != h_size ? scratch_memory.data() : &x[x_start_index]; + // Perform 256 bit vector operations. + const int limit_by_8 = h_size >> 3; + for (int k = limit_by_8; k > 0; --k, h_p2 += 8, x_p2 += 8) { + // Load the data into 256 bit vectors. + __m256 h_k = _mm256_loadu_ps(h_p2); + __m256 x_k = _mm256_loadu_ps(x_p2); + // Compute h = h + alpha * x. + h_k = _mm256_fmadd_ps(x_k, alpha_256, h_k); + + // Store the result. + _mm256_storeu_ps(h_p2, h_k); + } + *filters_updated = true; + } + + x_start_index = x_start_index > 0 ? x_start_index - 1 : x_size - 1; + } +} + +void MatchedFilterCore_AVX2(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + ArrayView x, + ArrayView y, + ArrayView h, + bool* filters_updated, + float* error_sum, + bool compute_accumulated_error, + ArrayView accumulated_error, + ArrayView scratch_memory) { + if (compute_accumulated_error) { + return MatchedFilterCore_AccumulatedError_AVX2( + x_start_index, x2_sum_threshold, smoothing, x, y, h, filters_updated, + error_sum, accumulated_error, scratch_memory); + } + const int h_size = static_cast(h.size()); + const int x_size = static_cast(x.size()); + RTC_DCHECK_EQ(0, h_size % 8); + + // Process for all samples in the sub-block. + for (size_t i = 0; i < y.size(); ++i) { + // Apply the matched filter as filter * x, and compute x * x. + + RTC_DCHECK_GT(x_size, x_start_index); + const float* x_p = &x[x_start_index]; + const float* h_p = &h[0]; + + // Initialize values for the accumulation. + __m256 s_256 = _mm256_set1_ps(0); + __m256 s_256_8 = _mm256_set1_ps(0); + __m256 x2_sum_256 = _mm256_set1_ps(0); + __m256 x2_sum_256_8 = _mm256_set1_ps(0); + float x2_sum = 0.f; + float s = 0; + + // Compute loop chunk sizes until, and after, the wraparound of the circular + // buffer for x. + const int chunk1 = + std::min(h_size, static_cast(x_size - x_start_index)); + + // Perform the loop in two chunks. + const int chunk2 = h_size - chunk1; + for (int limit : {chunk1, chunk2}) { + // Perform 256 bit vector operations. + const int limit_by_16 = limit >> 4; + for (int k = limit_by_16; k > 0; --k, h_p += 16, x_p += 16) { + // Load the data into 256 bit vectors. + __m256 x_k = _mm256_loadu_ps(x_p); + __m256 h_k = _mm256_loadu_ps(h_p); + __m256 x_k_8 = _mm256_loadu_ps(x_p + 8); + __m256 h_k_8 = _mm256_loadu_ps(h_p + 8); + // Compute and accumulate x * x and h * x. + x2_sum_256 = _mm256_fmadd_ps(x_k, x_k, x2_sum_256); + x2_sum_256_8 = _mm256_fmadd_ps(x_k_8, x_k_8, x2_sum_256_8); + s_256 = _mm256_fmadd_ps(h_k, x_k, s_256); + s_256_8 = _mm256_fmadd_ps(h_k_8, x_k_8, s_256_8); + } + + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_16 * 16; k > 0; --k, ++h_p, ++x_p) { + const float x_k = *x_p; + x2_sum += x_k * x_k; + s += *h_p * x_k; + } + + x_p = &x[0]; + } + + // Sum components together. + 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]; + + // Compute the matched filter error. + float e = y[i] - s; + const bool saturation = y[i] >= 32000.f || y[i] <= -32000.f; + (*error_sum) += e * e; + + // Update the matched filter estimate in an NLMS manner. + if (x2_sum > x2_sum_threshold && !saturation) { + RTC_DCHECK_LT(0.f, x2_sum); + const float alpha = smoothing * e / x2_sum; + const __m256 alpha_256 = _mm256_set1_ps(alpha); + + // filter = filter + smoothing * (y - filter * x) * x / x * x. + float* h_p2 = &h[0]; + x_p = &x[x_start_index]; + + // Perform the loop in two chunks. + for (int limit : {chunk1, chunk2}) { + // Perform 256 bit vector operations. + const int limit_by_8 = limit >> 3; + for (int k = limit_by_8; k > 0; --k, h_p2 += 8, x_p += 8) { + // Load the data into 256 bit vectors. + __m256 h_k = _mm256_loadu_ps(h_p2); + __m256 x_k = _mm256_loadu_ps(x_p); + // Compute h = h + alpha * x. + h_k = _mm256_fmadd_ps(x_k, alpha_256, h_k); + + // Store the result. + _mm256_storeu_ps(h_p2, h_k); + } + + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_8 * 8; k > 0; --k, ++h_p2, ++x_p) { + *h_p2 += alpha * *x_p; + } + + x_p = &x[0]; + } + + *filters_updated = true; + } + + x_start_index = x_start_index > 0 ? x_start_index - 1 : x_size - 1; + } +} + +} // namespace aec3 +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/vector_math_avx2.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/vector_math_avx2.cc new file mode 100644 index 00000000..63b9c1e4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/avx2/vector_math_avx2.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/vector_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace aec3 { + +// Elementwise square root. +void VectorMath::SqrtAVX2(ArrayView x) { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 3; + + int j = 0; + for (; j < vector_limit * 8; j += 8) { + __m256 g = _mm256_loadu_ps(&x[j]); + g = _mm256_sqrt_ps(g); + _mm256_storeu_ps(&x[j], g); + } + + for (; j < x_size; ++j) { + x[j] = sqrtf(x[j]); + } +} + +// Elementwise vector multiplication z = x * y. +void VectorMath::MultiplyAVX2(ArrayView x, + ArrayView y, + ArrayView z) { + RTC_DCHECK_EQ(z.size(), x.size()); + RTC_DCHECK_EQ(z.size(), y.size()); + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 3; + + int j = 0; + for (; j < vector_limit * 8; j += 8) { + const __m256 x_j = _mm256_loadu_ps(&x[j]); + const __m256 y_j = _mm256_loadu_ps(&y[j]); + const __m256 z_j = _mm256_mul_ps(x_j, y_j); + _mm256_storeu_ps(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] = x[j] * y[j]; + } +} + +// Elementwise vector accumulation z += x. +void VectorMath::AccumulateAVX2(ArrayView x, ArrayView z) { + RTC_DCHECK_EQ(z.size(), x.size()); + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 3; + + int j = 0; + for (; j < vector_limit * 8; j += 8) { + const __m256 x_j = _mm256_loadu_ps(&x[j]); + __m256 z_j = _mm256_loadu_ps(&z[j]); + z_j = _mm256_add_ps(x_j, z_j); + _mm256_storeu_ps(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] += x[j]; + } +} + +} // namespace aec3 +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/block.h b/pkg/apm/webrtc/modules/audio_processing/aec3/block.h new file mode 100644 index 00000000..3f0f4f6b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/block.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// Contains one or more channels of 4 milliseconds of audio data. +// The audio is split in one or more frequency bands, each with a sampling +// rate of 16 kHz. +class Block { + public: + Block(int num_bands, int num_channels, float default_value = 0.0f) + : num_bands_(num_bands), + num_channels_(num_channels), + data_(num_bands * num_channels * kBlockSize, default_value) {} + + // Returns the number of bands. + int NumBands() const { return num_bands_; } + + // Returns the number of channels. + int NumChannels() const { return num_channels_; } + + // Modifies the number of channels and sets all samples to zero. + void SetNumChannels(int num_channels) { + num_channels_ = num_channels; + data_.resize(num_bands_ * num_channels_ * kBlockSize); + std::fill(data_.begin(), data_.end(), 0.0f); + } + + // Iterators for accessing the data. + auto begin(int band, int channel) { + return data_.begin() + GetIndex(band, channel); + } + + auto begin(int band, int channel) const { + return data_.begin() + GetIndex(band, channel); + } + + auto end(int band, int channel) { return begin(band, channel) + kBlockSize; } + + auto end(int band, int channel) const { + return begin(band, channel) + kBlockSize; + } + + // Access data via ArrayView. + ArrayView View(int band, int channel) { + return ArrayView(&data_[GetIndex(band, channel)], + kBlockSize); + } + + ArrayView View(int band, int channel) const { + return ArrayView(&data_[GetIndex(band, channel)], + kBlockSize); + } + + // Lets two Blocks swap audio data. + void Swap(Block& b) { + std::swap(num_bands_, b.num_bands_); + std::swap(num_channels_, b.num_channels_); + data_.swap(b.data_); + } + + private: + // Returns the index of the first sample of the requested |band| and + // |channel|. + int GetIndex(int band, int channel) const { + return (band * num_channels_ + channel) * kBlockSize; + } + + int num_bands_; + int num_channels_; + std::vector data_; +}; + +} // namespace webrtc +#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/block_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/block_buffer.cc new file mode 100644 index 00000000..289c3f0d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/block_buffer.cc @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/block_buffer.h" + +#include + +namespace webrtc { + +BlockBuffer::BlockBuffer(size_t size, size_t num_bands, size_t num_channels) + : size(static_cast(size)), + buffer(size, Block(num_bands, num_channels)) {} + +BlockBuffer::~BlockBuffer() = default; + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/block_buffer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/block_buffer.h new file mode 100644 index 00000000..3489d516 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/block_buffer.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_BUFFER_H_ + +#include + +#include + +#include "modules/audio_processing/aec3/block.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Struct for bundling a circular buffer of two dimensional vector objects +// together with the read and write indices. +struct BlockBuffer { + BlockBuffer(size_t size, size_t num_bands, size_t num_channels); + ~BlockBuffer(); + + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; + } + + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index > 0 ? index - 1 : size - 1; + } + + int OffsetIndex(int index, int offset) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + RTC_DCHECK_GE(size, offset); + return (size + index + offset) % size; + } + + void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } + void IncWriteIndex() { write = IncIndex(write); } + void DecWriteIndex() { write = DecIndex(write); } + void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); } + void IncReadIndex() { read = IncIndex(read); } + void DecReadIndex() { read = DecIndex(read); } + + const int size; + std::vector buffer; + int write = 0; + int read = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/block_delay_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/block_delay_buffer.cc new file mode 100644 index 00000000..c599b392 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/block_delay_buffer.cc @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/aec3/block_delay_buffer.h" + +#include "api/array_view.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +BlockDelayBuffer::BlockDelayBuffer(size_t num_channels, + size_t num_bands, + size_t frame_length, + size_t delay_samples) + : frame_length_(frame_length), + delay_(delay_samples), + buf_(num_channels, + std::vector>(num_bands, + std::vector(delay_, 0.f))) {} + +BlockDelayBuffer::~BlockDelayBuffer() = default; + +void BlockDelayBuffer::DelaySignal(AudioBuffer* frame) { + RTC_DCHECK_EQ(buf_.size(), frame->num_channels()); + if (delay_ == 0) { + return; + } + + const size_t num_bands = buf_[0].size(); + const size_t num_channels = buf_.size(); + + const size_t i_start = last_insert_; + size_t i = 0; + for (size_t ch = 0; ch < num_channels; ++ch) { + RTC_DCHECK_EQ(buf_[ch].size(), frame->num_bands()); + RTC_DCHECK_EQ(buf_[ch].size(), num_bands); + ArrayView frame_ch(frame->split_bands(ch), num_bands); + const size_t delay = delay_; + + for (size_t band = 0; band < num_bands; ++band) { + RTC_DCHECK_EQ(delay_, buf_[ch][band].size()); + i = i_start; + + // Offloading these pointers and class variables to local variables allows + // the compiler to optimize the below loop when compiling with + // '-fno-strict-aliasing'. + float* buf_ch_band = buf_[ch][band].data(); + float* frame_ch_band = frame_ch[band]; + + for (size_t k = 0, frame_length = frame_length_; k < frame_length; ++k) { + const float tmp = buf_ch_band[i]; + buf_ch_band[i] = frame_ch_band[k]; + frame_ch_band[k] = tmp; + + i = i < delay - 1 ? i + 1 : 0; + } + } + } + + last_insert_ = i; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/block_delay_buffer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/block_delay_buffer.h new file mode 100644 index 00000000..711a790b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/block_delay_buffer.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_DELAY_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_DELAY_BUFFER_H_ + +#include + +#include + +#include "modules/audio_processing/audio_buffer.h" + +namespace webrtc { + +// Class for applying a fixed delay to the samples in a signal partitioned using +// the audiobuffer band-splitting scheme. +class BlockDelayBuffer { + public: + BlockDelayBuffer(size_t num_channels, + size_t num_bands, + size_t frame_length, + size_t delay_samples); + ~BlockDelayBuffer(); + + // Delays the samples by the specified delay. + void DelaySignal(AudioBuffer* frame); + + private: + const size_t frame_length_; + const size_t delay_; + std::vector>> buf_; + size_t last_insert_ = 0; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_DELAY_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/block_framer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/block_framer.cc new file mode 100644 index 00000000..6ce39c1a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/block_framer.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/block_framer.h" + +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +BlockFramer::BlockFramer(size_t num_bands, size_t num_channels) + : num_bands_(num_bands), + num_channels_(num_channels), + buffer_(num_bands_, + std::vector>( + num_channels, + std::vector(kBlockSize, 0.f))) { + RTC_DCHECK_LT(0, num_bands); + RTC_DCHECK_LT(0, num_channels); +} + +BlockFramer::~BlockFramer() = default; + +// All the constants are chosen so that the buffer is either empty or has enough +// samples for InsertBlockAndExtractSubFrame to produce a frame. In order to +// achieve this, the InsertBlockAndExtractSubFrame and InsertBlock methods need +// to be called in the correct order. +void BlockFramer::InsertBlock(const Block& block) { + RTC_DCHECK_EQ(num_bands_, block.NumBands()); + RTC_DCHECK_EQ(num_channels_, block.NumChannels()); + for (size_t band = 0; band < num_bands_; ++band) { + for (size_t channel = 0; channel < num_channels_; ++channel) { + RTC_DCHECK_EQ(0, buffer_[band][channel].size()); + + buffer_[band][channel].insert(buffer_[band][channel].begin(), + block.begin(band, channel), + block.end(band, channel)); + } + } +} + +void BlockFramer::InsertBlockAndExtractSubFrame( + const Block& block, + std::vector>>* sub_frame) { + RTC_DCHECK(sub_frame); + RTC_DCHECK_EQ(num_bands_, block.NumBands()); + RTC_DCHECK_EQ(num_channels_, block.NumChannels()); + RTC_DCHECK_EQ(num_bands_, sub_frame->size()); + for (size_t band = 0; band < num_bands_; ++band) { + RTC_DCHECK_EQ(num_channels_, (*sub_frame)[0].size()); + for (size_t channel = 0; channel < num_channels_; ++channel) { + RTC_DCHECK_LE(kSubFrameLength, + buffer_[band][channel].size() + kBlockSize); + RTC_DCHECK_GE(kBlockSize, buffer_[band][channel].size()); + RTC_DCHECK_EQ(kSubFrameLength, (*sub_frame)[band][channel].size()); + + const int samples_to_frame = + kSubFrameLength - buffer_[band][channel].size(); + std::copy(buffer_[band][channel].begin(), buffer_[band][channel].end(), + (*sub_frame)[band][channel].begin()); + std::copy( + block.begin(band, channel), + block.begin(band, channel) + samples_to_frame, + (*sub_frame)[band][channel].begin() + buffer_[band][channel].size()); + buffer_[band][channel].clear(); + buffer_[band][channel].insert( + buffer_[band][channel].begin(), + block.begin(band, channel) + samples_to_frame, + block.end(band, channel)); + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/block_framer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/block_framer.h new file mode 100644 index 00000000..e45a8384 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/block_framer.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_FRAMER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_FRAMER_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/block.h" + +namespace webrtc { + +// Class for producing frames consisting of 2 subframes of 80 samples each +// from 64 sample blocks. The class is designed to work together with the +// FrameBlocker class which performs the reverse conversion. Used together with +// that, this class produces output frames are the same rate as frames are +// received by the FrameBlocker class. Note that the internal buffers will +// overrun if any other rate of packets insertion is used. +class BlockFramer { + public: + BlockFramer(size_t num_bands, size_t num_channels); + ~BlockFramer(); + BlockFramer(const BlockFramer&) = delete; + BlockFramer& operator=(const BlockFramer&) = delete; + + // Adds a 64 sample block into the data that will form the next output frame. + void InsertBlock(const Block& block); + // Adds a 64 sample block and extracts an 80 sample subframe. + void InsertBlockAndExtractSubFrame( + const Block& block, + std::vector>>* sub_frame); + + private: + const size_t num_bands_; + const size_t num_channels_; + std::vector>> buffer_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_FRAMER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor.cc new file mode 100644 index 00000000..c7f198e7 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor.cc @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/aec3/block_processor.h" + +#include + +#include +#include +#include +#include +#include + +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/block_processor_metrics.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/echo_remover.h" +#include "modules/audio_processing/aec3/render_delay_buffer.h" +#include "modules/audio_processing/aec3/render_delay_controller.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace { + +enum class BlockProcessorApiCall { kCapture, kRender }; + +class BlockProcessorImpl final : public BlockProcessor { + public: + BlockProcessorImpl(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr render_buffer, + std::unique_ptr delay_controller, + std::unique_ptr echo_remover); + + BlockProcessorImpl() = delete; + + ~BlockProcessorImpl() override; + + void ProcessCapture(bool echo_path_gain_change, + bool capture_signal_saturation, + Block* linear_output, + Block* capture_block) override; + + void BufferRender(const Block& block) override; + + void UpdateEchoLeakageStatus(bool leakage_detected) override; + + void GetMetrics(EchoControl::Metrics* metrics) const override; + + void SetAudioBufferDelay(int delay_ms) override; + void SetCaptureOutputUsage(bool capture_output_used) override; + + private: + static std::atomic instance_count_; + std::unique_ptr data_dumper_; + const EchoCanceller3Config config_; + bool capture_properly_started_ = false; + bool render_properly_started_ = false; + const size_t sample_rate_hz_; + std::unique_ptr render_buffer_; + std::unique_ptr delay_controller_; + std::unique_ptr echo_remover_; + BlockProcessorMetrics metrics_; + RenderDelayBuffer::BufferingEvent render_event_; + size_t capture_call_counter_ = 0; + std::optional estimated_delay_; +}; + +std::atomic BlockProcessorImpl::instance_count_(0); + +BlockProcessorImpl::BlockProcessorImpl( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t /* num_render_channels */, + size_t /* num_capture_channels */, + std::unique_ptr render_buffer, + std::unique_ptr delay_controller, + std::unique_ptr echo_remover) + : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), + config_(config), + sample_rate_hz_(sample_rate_hz), + render_buffer_(std::move(render_buffer)), + delay_controller_(std::move(delay_controller)), + echo_remover_(std::move(echo_remover)), + render_event_(RenderDelayBuffer::BufferingEvent::kNone) { + RTC_DCHECK(ValidFullBandRate(sample_rate_hz_)); +} + +BlockProcessorImpl::~BlockProcessorImpl() = default; + +void BlockProcessorImpl::ProcessCapture(bool echo_path_gain_change, + bool capture_signal_saturation, + Block* linear_output, + Block* capture_block) { + RTC_DCHECK(capture_block); + RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), capture_block->NumBands()); + + capture_call_counter_++; + + data_dumper_->DumpRaw("aec3_processblock_call_order", + static_cast(BlockProcessorApiCall::kCapture)); + data_dumper_->DumpWav("aec3_processblock_capture_input", + capture_block->View(/*band=*/0, /*channel=*/0), 16000, + 1); + + if (render_properly_started_) { + if (!capture_properly_started_) { + capture_properly_started_ = true; + render_buffer_->Reset(); + if (delay_controller_) + delay_controller_->Reset(true); + } + } else { + // If no render data has yet arrived, do not process the capture signal. + render_buffer_->HandleSkippedCaptureProcessing(); + return; + } + + EchoPathVariability echo_path_variability( + echo_path_gain_change, EchoPathVariability::DelayAdjustment::kNone, + false); + + if (render_event_ == RenderDelayBuffer::BufferingEvent::kRenderOverrun && + render_properly_started_) { + echo_path_variability.delay_change = + EchoPathVariability::DelayAdjustment::kBufferFlush; + if (delay_controller_) + delay_controller_->Reset(true); + RTC_LOG(LS_WARNING) << "Reset due to render buffer overrun at block " + << capture_call_counter_; + } + render_event_ = RenderDelayBuffer::BufferingEvent::kNone; + + // Update the render buffers with any newly arrived render blocks and prepare + // the render buffers for reading the render data corresponding to the current + // capture block. + RenderDelayBuffer::BufferingEvent buffer_event = + render_buffer_->PrepareCaptureProcessing(); + // Reset the delay controller at render buffer underrun. + if (buffer_event == RenderDelayBuffer::BufferingEvent::kRenderUnderrun) { + if (delay_controller_) + delay_controller_->Reset(false); + } + + data_dumper_->DumpWav("aec3_processblock_capture_input2", + capture_block->View(/*band=*/0, /*channel=*/0), 16000, + 1); + + bool has_delay_estimator = !config_.delay.use_external_delay_estimator; + if (has_delay_estimator) { + RTC_DCHECK(delay_controller_); + // Compute and apply the render delay required to achieve proper signal + // alignment. + estimated_delay_ = delay_controller_->GetDelay( + render_buffer_->GetDownsampledRenderBuffer(), render_buffer_->Delay(), + *capture_block); + + if (estimated_delay_) { + bool delay_change = + render_buffer_->AlignFromDelay(estimated_delay_->delay); + if (delay_change) { + LoggingSeverity log_level = + config_.delay.log_warning_on_delay_changes ? LS_WARNING : LS_INFO; + RTC_LOG_V(log_level) << "Delay changed to " << estimated_delay_->delay + << " at block " << capture_call_counter_; + echo_path_variability.delay_change = + EchoPathVariability::DelayAdjustment::kNewDetectedDelay; + } + } + + echo_path_variability.clock_drift = delay_controller_->HasClockdrift(); + + } else { + render_buffer_->AlignFromExternalDelay(); + } + + // Remove the echo from the capture signal. + if (has_delay_estimator || render_buffer_->HasReceivedBufferDelay()) { + echo_remover_->ProcessCapture( + echo_path_variability, capture_signal_saturation, estimated_delay_, + render_buffer_->GetRenderBuffer(), linear_output, capture_block); + } + + // Update the metrics. + metrics_.UpdateCapture(false); +} + +void BlockProcessorImpl::BufferRender(const Block& block) { + RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), block.NumBands()); + data_dumper_->DumpRaw("aec3_processblock_call_order", + static_cast(BlockProcessorApiCall::kRender)); + data_dumper_->DumpWav("aec3_processblock_render_input", + block.View(/*band=*/0, /*channel=*/0), 16000, 1); + + render_event_ = render_buffer_->Insert(block); + + metrics_.UpdateRender(render_event_ != + RenderDelayBuffer::BufferingEvent::kNone); + + render_properly_started_ = true; + if (delay_controller_) + delay_controller_->LogRenderCall(); +} + +void BlockProcessorImpl::UpdateEchoLeakageStatus(bool leakage_detected) { + echo_remover_->UpdateEchoLeakageStatus(leakage_detected); +} + +void BlockProcessorImpl::GetMetrics(EchoControl::Metrics* metrics) const { + echo_remover_->GetMetrics(metrics); + constexpr int block_size_ms = 4; + std::optional delay = render_buffer_->Delay(); + metrics->delay_ms = delay ? static_cast(*delay) * block_size_ms : 0; +} + +void BlockProcessorImpl::SetAudioBufferDelay(int delay_ms) { + render_buffer_->SetAudioBufferDelay(delay_ms); +} + +void BlockProcessorImpl::SetCaptureOutputUsage(bool capture_output_used) { + echo_remover_->SetCaptureOutputUsage(capture_output_used); +} + +} // namespace + +std::unique_ptr BlockProcessor::Create( + const Environment& env, + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels) { + std::unique_ptr render_buffer( + RenderDelayBuffer::Create(config, sample_rate_hz, num_render_channels)); + std::unique_ptr delay_controller; + if (!config.delay.use_external_delay_estimator) { + delay_controller.reset(RenderDelayController::Create(config, sample_rate_hz, + num_capture_channels)); + } + std::unique_ptr echo_remover = EchoRemover::Create( + env, config, sample_rate_hz, num_render_channels, num_capture_channels); + return Create(config, sample_rate_hz, num_render_channels, + num_capture_channels, std::move(render_buffer), + std::move(delay_controller), std::move(echo_remover)); +} + +std::unique_ptr BlockProcessor::Create( + const Environment& env, + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr render_buffer) { + std::unique_ptr delay_controller; + if (!config.delay.use_external_delay_estimator) { + delay_controller.reset(RenderDelayController::Create(config, sample_rate_hz, + num_capture_channels)); + } + std::unique_ptr echo_remover = EchoRemover::Create( + env, config, sample_rate_hz, num_render_channels, num_capture_channels); + return Create(config, sample_rate_hz, num_render_channels, + num_capture_channels, std::move(render_buffer), + std::move(delay_controller), std::move(echo_remover)); +} + +std::unique_ptr BlockProcessor::Create( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr render_buffer, + std::unique_ptr delay_controller, + std::unique_ptr echo_remover) { + return std::make_unique( + config, sample_rate_hz, num_render_channels, num_capture_channels, + std::move(render_buffer), std::move(delay_controller), + std::move(echo_remover)); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor.h b/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor.h new file mode 100644 index 00000000..06669ab9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor.h @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_H_ + +#include + +#include +#include + +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "api/environment/environment.h" +#include "modules/audio_processing/aec3/block.h" +#include "modules/audio_processing/aec3/echo_remover.h" +#include "modules/audio_processing/aec3/render_delay_buffer.h" +#include "modules/audio_processing/aec3/render_delay_controller.h" + +namespace webrtc { + +// Class for performing echo cancellation on 64 sample blocks of audio data. +class BlockProcessor { + public: + static std::unique_ptr Create( + const Environment& env, + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels); + // Only used for testing purposes. + static std::unique_ptr Create( + const Environment& env, + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr render_buffer); + static std::unique_ptr Create( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr render_buffer, + std::unique_ptr delay_controller, + std::unique_ptr echo_remover); + + virtual ~BlockProcessor() = default; + + // Get current metrics. + virtual void GetMetrics(EchoControl::Metrics* metrics) const = 0; + + // Provides an optional external estimate of the audio buffer delay. + virtual void SetAudioBufferDelay(int delay_ms) = 0; + + // Processes a block of capture data. + virtual void ProcessCapture(bool echo_path_gain_change, + bool capture_signal_saturation, + Block* linear_output, + Block* capture_block) = 0; + + // Buffers a block of render data supplied by a FrameBlocker object. + virtual void BufferRender(const Block& render_block) = 0; + + // Reports whether echo leakage has been detected in the echo canceller + // output. + virtual void UpdateEchoLeakageStatus(bool leakage_detected) = 0; + + // Specifies whether the capture output will be used. The purpose of this is + // to allow the block processor to deactivate some of the processing when the + // resulting output is anyway not used, for instance when the endpoint is + // muted. + virtual void SetCaptureOutputUsage(bool capture_output_used) = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor_metrics.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor_metrics.cc new file mode 100644 index 00000000..deac1fcd --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor_metrics.cc @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/block_processor_metrics.h" + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +namespace { + +enum class RenderUnderrunCategory { + kNone, + kFew, + kSeveral, + kMany, + kConstant, + kNumCategories +}; + +enum class RenderOverrunCategory { + kNone, + kFew, + kSeveral, + kMany, + kConstant, + kNumCategories +}; + +} // namespace + +void BlockProcessorMetrics::UpdateCapture(bool underrun) { + ++capture_block_counter_; + if (underrun) { + ++render_buffer_underruns_; + } + + if (capture_block_counter_ == kMetricsReportingIntervalBlocks) { + metrics_reported_ = true; + + RenderUnderrunCategory underrun_category; + if (render_buffer_underruns_ == 0) { + underrun_category = RenderUnderrunCategory::kNone; + } else if (render_buffer_underruns_ > (capture_block_counter_ >> 1)) { + underrun_category = RenderUnderrunCategory::kConstant; + } else if (render_buffer_underruns_ > 100) { + underrun_category = RenderUnderrunCategory::kMany; + } else if (render_buffer_underruns_ > 10) { + underrun_category = RenderUnderrunCategory::kSeveral; + } else { + underrun_category = RenderUnderrunCategory::kFew; + } + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.EchoCanceller.RenderUnderruns", + static_cast(underrun_category), + static_cast(RenderUnderrunCategory::kNumCategories)); + + RenderOverrunCategory overrun_category; + if (render_buffer_overruns_ == 0) { + overrun_category = RenderOverrunCategory::kNone; + } else if (render_buffer_overruns_ > (buffer_render_calls_ >> 1)) { + overrun_category = RenderOverrunCategory::kConstant; + } else if (render_buffer_overruns_ > 100) { + overrun_category = RenderOverrunCategory::kMany; + } else if (render_buffer_overruns_ > 10) { + overrun_category = RenderOverrunCategory::kSeveral; + } else { + overrun_category = RenderOverrunCategory::kFew; + } + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.EchoCanceller.RenderOverruns", + static_cast(overrun_category), + static_cast(RenderOverrunCategory::kNumCategories)); + + ResetMetrics(); + capture_block_counter_ = 0; + } else { + metrics_reported_ = false; + } +} + +void BlockProcessorMetrics::UpdateRender(bool overrun) { + ++buffer_render_calls_; + if (overrun) { + ++render_buffer_overruns_; + } +} + +void BlockProcessorMetrics::ResetMetrics() { + render_buffer_underruns_ = 0; + render_buffer_overruns_ = 0; + buffer_render_calls_ = 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor_metrics.h b/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor_metrics.h new file mode 100644 index 00000000..a70d0dac --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/block_processor_metrics.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_METRICS_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_METRICS_H_ + +namespace webrtc { + +// Handles the reporting of metrics for the block_processor. +class BlockProcessorMetrics { + public: + BlockProcessorMetrics() = default; + + BlockProcessorMetrics(const BlockProcessorMetrics&) = delete; + BlockProcessorMetrics& operator=(const BlockProcessorMetrics&) = delete; + + // Updates the metric with new capture data. + void UpdateCapture(bool underrun); + + // Updates the metric with new render data. + void UpdateRender(bool overrun); + + // Returns true if the metrics have just been reported, otherwise false. + bool MetricsReported() { return metrics_reported_; } + + private: + // Resets the metrics. + void ResetMetrics(); + + int capture_block_counter_ = 0; + bool metrics_reported_ = false; + int render_buffer_underruns_ = 0; + int render_buffer_overruns_ = 0; + int buffer_render_calls_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_METRICS_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/clockdrift_detector.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/clockdrift_detector.cc new file mode 100644 index 00000000..2c49b795 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/clockdrift_detector.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/aec3/clockdrift_detector.h" + +namespace webrtc { + +ClockdriftDetector::ClockdriftDetector() + : level_(Level::kNone), stability_counter_(0) { + delay_history_.fill(0); +} + +ClockdriftDetector::~ClockdriftDetector() = default; + +void ClockdriftDetector::Update(int delay_estimate) { + if (delay_estimate == delay_history_[0]) { + // Reset clockdrift level if delay estimate is stable for 7500 blocks (30 + // seconds). + if (++stability_counter_ > 7500) + level_ = Level::kNone; + return; + } + + stability_counter_ = 0; + const int d1 = delay_history_[0] - delay_estimate; + const int d2 = delay_history_[1] - delay_estimate; + const int d3 = delay_history_[2] - delay_estimate; + + // Patterns recognized as positive clockdrift: + // [x-3], x-2, x-1, x. + // [x-3], x-1, x-2, x. + const bool probable_drift_up = + (d1 == -1 && d2 == -2) || (d1 == -2 && d2 == -1); + const bool drift_up = probable_drift_up && d3 == -3; + + // Patterns recognized as negative clockdrift: + // [x+3], x+2, x+1, x. + // [x+3], x+1, x+2, x. + const bool probable_drift_down = (d1 == 1 && d2 == 2) || (d1 == 2 && d2 == 1); + const bool drift_down = probable_drift_down && d3 == 3; + + // Set clockdrift level. + if (drift_up || drift_down) { + level_ = Level::kVerified; + } else if ((probable_drift_up || probable_drift_down) && + level_ == Level::kNone) { + level_ = Level::kProbable; + } + + // Shift delay history one step. + delay_history_[2] = delay_history_[1]; + delay_history_[1] = delay_history_[0]; + delay_history_[0] = delay_estimate; +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/clockdrift_detector.h b/pkg/apm/webrtc/modules/audio_processing/aec3/clockdrift_detector.h new file mode 100644 index 00000000..2ba90bb8 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/clockdrift_detector.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_CLOCKDRIFT_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_CLOCKDRIFT_DETECTOR_H_ + +#include + +#include + +namespace webrtc { + +class ApmDataDumper; +struct DownsampledRenderBuffer; +struct EchoCanceller3Config; + +// Detects clockdrift by analyzing the estimated delay. +class ClockdriftDetector { + public: + enum class Level { kNone, kProbable, kVerified, kNumCategories }; + ClockdriftDetector(); + ~ClockdriftDetector(); + void Update(int delay_estimate); + Level ClockdriftLevel() const { return level_; } + + private: + std::array delay_history_; + Level level_; + size_t stability_counter_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_CLOCKDRIFT_DETECTOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.cc new file mode 100644 index 00000000..f4fb74d2 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/coarse_filter_update_gain.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +CoarseFilterUpdateGain::CoarseFilterUpdateGain( + const EchoCanceller3Config::Filter::CoarseConfiguration& config, + size_t config_change_duration_blocks) + : config_change_duration_blocks_( + static_cast(config_change_duration_blocks)) { + SetConfig(config, true); + RTC_DCHECK_LT(0, config_change_duration_blocks_); + one_by_config_change_duration_blocks_ = 1.f / config_change_duration_blocks_; +} + +void CoarseFilterUpdateGain::HandleEchoPathChange() { + poor_signal_excitation_counter_ = 0; + call_counter_ = 0; +} + +void CoarseFilterUpdateGain::Compute( + const std::array& render_power, + const RenderSignalAnalyzer& render_signal_analyzer, + const FftData& E_coarse, + size_t size_partitions, + bool saturated_capture_signal, + FftData* G) { + RTC_DCHECK(G); + ++call_counter_; + + UpdateCurrentConfig(); + + if (render_signal_analyzer.PoorSignalExcitation()) { + poor_signal_excitation_counter_ = 0; + } + + // Do not update the filter if the render is not sufficiently excited. + if (++poor_signal_excitation_counter_ < size_partitions || + saturated_capture_signal || call_counter_ <= size_partitions) { + G->re.fill(0.f); + G->im.fill(0.f); + return; + } + + // Compute mu. + std::array mu; + const auto& X2 = render_power; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + if (X2[k] > current_config_.noise_gate) { + mu[k] = current_config_.rate / X2[k]; + } else { + mu[k] = 0.f; + } + } + + // Avoid updating the filter close to narrow bands in the render signals. + render_signal_analyzer.MaskRegionsAroundNarrowBands(&mu); + + // G = mu * E * X2. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + G->re[k] = mu[k] * E_coarse.re[k]; + G->im[k] = mu[k] * E_coarse.im[k]; + } +} + +void CoarseFilterUpdateGain::UpdateCurrentConfig() { + RTC_DCHECK_GE(config_change_duration_blocks_, config_change_counter_); + if (config_change_counter_ > 0) { + if (--config_change_counter_ > 0) { + auto average = [](float from, float to, float from_weight) { + return from * from_weight + to * (1.f - from_weight); + }; + + float change_factor = + config_change_counter_ * one_by_config_change_duration_blocks_; + + current_config_.rate = + average(old_target_config_.rate, target_config_.rate, change_factor); + current_config_.noise_gate = + average(old_target_config_.noise_gate, target_config_.noise_gate, + change_factor); + } else { + current_config_ = old_target_config_ = target_config_; + } + } + RTC_DCHECK_LE(0, config_change_counter_); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.h b/pkg/apm/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.h new file mode 100644 index 00000000..a1a1399b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_COARSE_FILTER_UPDATE_GAIN_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_COARSE_FILTER_UPDATE_GAIN_H_ + +#include + +#include + +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/render_signal_analyzer.h" + +namespace webrtc { + +// Provides functionality for computing the fixed gain for the coarse filter. +class CoarseFilterUpdateGain { + public: + explicit CoarseFilterUpdateGain( + const EchoCanceller3Config::Filter::CoarseConfiguration& config, + size_t config_change_duration_blocks); + + // Takes action in the case of a known echo path change. + void HandleEchoPathChange(); + + // Computes the gain. + void Compute(const std::array& render_power, + const RenderSignalAnalyzer& render_signal_analyzer, + const FftData& E_coarse, + size_t size_partitions, + bool saturated_capture_signal, + FftData* G); + + // Sets a new config. + void SetConfig( + const EchoCanceller3Config::Filter::CoarseConfiguration& config, + bool immediate_effect) { + if (immediate_effect) { + old_target_config_ = current_config_ = target_config_ = config; + config_change_counter_ = 0; + } else { + old_target_config_ = current_config_; + target_config_ = config; + config_change_counter_ = config_change_duration_blocks_; + } + } + + private: + EchoCanceller3Config::Filter::CoarseConfiguration current_config_; + EchoCanceller3Config::Filter::CoarseConfiguration target_config_; + EchoCanceller3Config::Filter::CoarseConfiguration old_target_config_; + const int config_change_duration_blocks_; + float one_by_config_change_duration_blocks_; + // TODO(peah): Check whether this counter should instead be initialized to a + // large value. + size_t poor_signal_excitation_counter_ = 0; + size_t call_counter_ = 0; + int config_change_counter_ = 0; + + void UpdateCurrentConfig(); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_COARSE_FILTER_UPDATE_GAIN_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc new file mode 100644 index 00000000..ed643f44 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/comfort_noise_generator.h" + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "modules/audio_processing/aec3/vector_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Computes the noise floor value that matches a WGN input of noise_floor_dbfs. +float GetNoiseFloorFactor(float noise_floor_dbfs) { + // kdBfsNormalization = 20.f*log10(32768.f). + constexpr float kdBfsNormalization = 90.30899869919436f; + return 64.f * powf(10.f, (kdBfsNormalization + noise_floor_dbfs) * 0.1f); +} + +// Table of sqrt(2) * sin(2*pi*i/32). +constexpr float kSqrt2Sin[32] = { + +0.0000000f, +0.2758994f, +0.5411961f, +0.7856950f, +1.0000000f, + +1.1758756f, +1.3065630f, +1.3870398f, +1.4142136f, +1.3870398f, + +1.3065630f, +1.1758756f, +1.0000000f, +0.7856950f, +0.5411961f, + +0.2758994f, +0.0000000f, -0.2758994f, -0.5411961f, -0.7856950f, + -1.0000000f, -1.1758756f, -1.3065630f, -1.3870398f, -1.4142136f, + -1.3870398f, -1.3065630f, -1.1758756f, -1.0000000f, -0.7856950f, + -0.5411961f, -0.2758994f}; + +void GenerateComfortNoise(Aec3Optimization optimization, + const std::array& N2, + uint32_t* seed, + FftData* lower_band_noise, + FftData* upper_band_noise) { + FftData* N_low = lower_band_noise; + FftData* N_high = upper_band_noise; + + // Compute square root spectrum. + std::array N; + std::copy(N2.begin(), N2.end(), N.begin()); + aec3::VectorMath(optimization).Sqrt(N); + + // Compute the noise level for the upper bands. + constexpr float kOneByNumBands = 1.f / (kFftLengthBy2Plus1 / 2 + 1); + constexpr int kFftLengthBy2Plus1By2 = kFftLengthBy2Plus1 / 2; + const float high_band_noise_level = + std::accumulate(N.begin() + kFftLengthBy2Plus1By2, N.end(), 0.f) * + kOneByNumBands; + + // The analysis and synthesis windowing cause loss of power when + // cross-fading the noise where frames are completely uncorrelated + // (generated with random phase), hence the factor sqrt(2). + // This is not the case for the speech signal where the input is overlapping + // (strong correlation). + N_low->re[0] = N_low->re[kFftLengthBy2] = N_high->re[0] = + N_high->re[kFftLengthBy2] = 0.f; + for (size_t k = 1; k < kFftLengthBy2; k++) { + constexpr int kIndexMask = 32 - 1; + // Generate a random 31-bit integer. + seed[0] = (seed[0] * 69069 + 1) & (0x80000000 - 1); + // Convert to a 5-bit index. + int i = seed[0] >> 26; + + // y = sqrt(2) * sin(a) + const float x = kSqrt2Sin[i]; + // x = sqrt(2) * cos(a) = sqrt(2) * sin(a + pi/2) + const float y = kSqrt2Sin[(i + 8) & kIndexMask]; + + // Form low-frequency noise via spectral shaping. + N_low->re[k] = N[k] * x; + N_low->im[k] = N[k] * y; + + // Form the high-frequency noise via simple levelling. + N_high->re[k] = high_band_noise_level * x; + N_high->im[k] = high_band_noise_level * y; + } +} + +} // namespace + +ComfortNoiseGenerator::ComfortNoiseGenerator(const EchoCanceller3Config& config, + Aec3Optimization optimization, + size_t num_capture_channels) + : optimization_(optimization), + seed_(42), + num_capture_channels_(num_capture_channels), + noise_floor_(GetNoiseFloorFactor(config.comfort_noise.noise_floor_dbfs)), + N2_initial_( + std::make_unique>>( + num_capture_channels_)), + Y2_smoothed_(num_capture_channels_), + N2_(num_capture_channels_) { + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + (*N2_initial_)[ch].fill(0.f); + Y2_smoothed_[ch].fill(0.f); + N2_[ch].fill(1.0e6f); + } +} + +ComfortNoiseGenerator::~ComfortNoiseGenerator() = default; + +void ComfortNoiseGenerator::Compute( + bool saturated_capture, + ArrayView> capture_spectrum, + ArrayView lower_band_noise, + ArrayView upper_band_noise) { + const auto& Y2 = capture_spectrum; + + if (!saturated_capture) { + // Smooth Y2. + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + std::transform(Y2_smoothed_[ch].begin(), Y2_smoothed_[ch].end(), + Y2[ch].begin(), Y2_smoothed_[ch].begin(), + [](float a, float b) { return a + 0.1f * (b - a); }); + } + + if (N2_counter_ > 50) { + // Update N2 from Y2_smoothed. + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + std::transform(N2_[ch].begin(), N2_[ch].end(), Y2_smoothed_[ch].begin(), + N2_[ch].begin(), [](float a, float b) { + return b < a ? (0.9f * b + 0.1f * a) * 1.0002f + : a * 1.0002f; + }); + } + } + + if (N2_initial_) { + if (++N2_counter_ == 1000) { + N2_initial_.reset(); + } else { + // Compute the N2_initial from N2. + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + std::transform(N2_[ch].begin(), N2_[ch].end(), + (*N2_initial_)[ch].begin(), (*N2_initial_)[ch].begin(), + [](float a, float b) { + return a > b ? b + 0.001f * (a - b) : a; + }); + } + } + } + + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + for (auto& n : N2_[ch]) { + n = std::max(n, noise_floor_); + } + if (N2_initial_) { + for (auto& n : (*N2_initial_)[ch]) { + n = std::max(n, noise_floor_); + } + } + } + } + + // Choose N2 estimate to use. + const auto& N2 = N2_initial_ ? (*N2_initial_) : N2_; + + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + GenerateComfortNoise(optimization_, N2[ch], &seed_, &lower_band_noise[ch], + &upper_band_noise[ch]); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/comfort_noise_generator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/comfort_noise_generator.h new file mode 100644 index 00000000..b5212fb6 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/comfort_noise_generator.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_COMFORT_NOISE_GENERATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_COMFORT_NOISE_GENERATOR_H_ + +#include + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec_state.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace aec3 { +#if defined(WEBRTC_ARCH_X86_FAMILY) + +void EstimateComfortNoise_SSE2(const std::array& N2, + uint32_t* seed, + FftData* lower_band_noise, + FftData* upper_band_noise); +#endif +void EstimateComfortNoise(const std::array& N2, + uint32_t* seed, + FftData* lower_band_noise, + FftData* upper_band_noise); + +} // namespace aec3 + +// Generates the comfort noise. +class ComfortNoiseGenerator { + public: + ComfortNoiseGenerator(const EchoCanceller3Config& config, + Aec3Optimization optimization, + size_t num_capture_channels); + ComfortNoiseGenerator() = delete; + ~ComfortNoiseGenerator(); + ComfortNoiseGenerator(const ComfortNoiseGenerator&) = delete; + + // Computes the comfort noise. + void Compute( + bool saturated_capture, + ArrayView> capture_spectrum, + ArrayView lower_band_noise, + ArrayView upper_band_noise); + + // Returns the estimate of the background noise spectrum. + ArrayView> NoiseSpectrum() const { + return N2_; + } + + private: + const Aec3Optimization optimization_; + uint32_t seed_; + const size_t num_capture_channels_; + const float noise_floor_; + std::unique_ptr>> + N2_initial_; + std::vector> Y2_smoothed_; + std::vector> N2_; + int N2_counter_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_COMFORT_NOISE_GENERATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/config_selector.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/config_selector.cc new file mode 100644 index 00000000..e6bd8297 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/config_selector.cc @@ -0,0 +1,71 @@ + +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/config_selector.h" + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// Validates that the mono and the multichannel configs have compatible fields. +bool CompatibleConfigs(const EchoCanceller3Config& mono_config, + const EchoCanceller3Config& multichannel_config) { + if (mono_config.delay.fixed_capture_delay_samples != + multichannel_config.delay.fixed_capture_delay_samples) { + return false; + } + if (mono_config.filter.export_linear_aec_output != + multichannel_config.filter.export_linear_aec_output) { + return false; + } + if (mono_config.filter.high_pass_filter_echo_reference != + multichannel_config.filter.high_pass_filter_echo_reference) { + return false; + } + if (mono_config.multi_channel.detect_stereo_content != + multichannel_config.multi_channel.detect_stereo_content) { + return false; + } + if (mono_config.multi_channel.stereo_detection_timeout_threshold_seconds != + multichannel_config.multi_channel + .stereo_detection_timeout_threshold_seconds) { + return false; + } + return true; +} + +} // namespace + +ConfigSelector::ConfigSelector( + const EchoCanceller3Config& config, + const std::optional& multichannel_config, + int num_render_input_channels) + : config_(config), multichannel_config_(multichannel_config) { + if (multichannel_config_.has_value()) { + RTC_DCHECK(CompatibleConfigs(config_, *multichannel_config_)); + } + + Update(!config_.multi_channel.detect_stereo_content && + num_render_input_channels > 1); + + RTC_DCHECK(active_config_); +} + +void ConfigSelector::Update(bool multichannel_content) { + if (multichannel_content && multichannel_config_.has_value()) { + active_config_ = &(*multichannel_config_); + } else { + active_config_ = &config_; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/config_selector.h b/pkg/apm/webrtc/modules/audio_processing/aec3/config_selector.h new file mode 100644 index 00000000..f56f7bac --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/config_selector.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_CONFIG_SELECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_CONFIG_SELECTOR_H_ + +#include + +#include "api/audio/echo_canceller3_config.h" + +namespace webrtc { + +// Selects the config to use. +class ConfigSelector { + public: + ConfigSelector(const EchoCanceller3Config& config, + const std::optional& multichannel_config, + int num_render_input_channels); + + // Updates the config selection based on the detection of multichannel + // content. + void Update(bool multichannel_content); + + const EchoCanceller3Config& active_config() const { return *active_config_; } + + private: + const EchoCanceller3Config config_; + const std::optional multichannel_config_; + const EchoCanceller3Config* active_config_ = nullptr; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_CONFIG_SELECTOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/decimator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/decimator.cc new file mode 100644 index 00000000..beac73e2 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/decimator.cc @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/aec3/decimator.h" + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// signal.butter(2, 3400/8000.0, 'lowpass', analog=False) +const std::vector GetLowPassFilterDS2() { + return std::vector{ + {{-1.f, 0.f}, {0.13833231f, 0.40743176f}, 0.22711796393486466f}, + {{-1.f, 0.f}, {0.13833231f, 0.40743176f}, 0.22711796393486466f}, + {{-1.f, 0.f}, {0.13833231f, 0.40743176f}, 0.22711796393486466f}}; +} + +// signal.ellip(6, 1, 40, 1800/8000, btype='lowpass', analog=False) +const std::vector GetLowPassFilterDS4() { + return std::vector{ + {{-0.08873842f, 0.99605496f}, {0.75916227f, 0.23841065f}, 0.26250696827f}, + {{0.62273832f, 0.78243018f}, {0.74892112f, 0.5410152f}, 0.26250696827f}, + {{0.71107693f, 0.70311421f}, {0.74895534f, 0.63924616f}, 0.26250696827f}}; +} + +// signal.cheby1(1, 6, [1000/8000, 2000/8000], btype='bandpass', analog=False) +const std::vector GetBandPassFilterDS8() { + return std::vector{ + {{1.f, 0.f}, {0.7601815f, 0.46423542f}, 0.10330478266505948f, true}, + {{1.f, 0.f}, {0.7601815f, 0.46423542f}, 0.10330478266505948f, true}, + {{1.f, 0.f}, {0.7601815f, 0.46423542f}, 0.10330478266505948f, true}, + {{1.f, 0.f}, {0.7601815f, 0.46423542f}, 0.10330478266505948f, true}, + {{1.f, 0.f}, {0.7601815f, 0.46423542f}, 0.10330478266505948f, true}}; +} + +// signal.butter(2, 1000/8000.0, 'highpass', analog=False) +const std::vector GetHighPassFilter() { + return std::vector{ + {{1.f, 0.f}, {0.72712179f, 0.21296904f}, 0.7570763753338849f}}; +} + +const std::vector GetPassThroughFilter() { + return std::vector{}; +} +} // namespace + +Decimator::Decimator(size_t down_sampling_factor) + : down_sampling_factor_(down_sampling_factor), + anti_aliasing_filter_(down_sampling_factor_ == 4 + ? GetLowPassFilterDS4() + : (down_sampling_factor_ == 8 + ? GetBandPassFilterDS8() + : GetLowPassFilterDS2())), + noise_reduction_filter_(down_sampling_factor_ == 8 + ? GetPassThroughFilter() + : GetHighPassFilter()) { + RTC_DCHECK(down_sampling_factor_ == 2 || down_sampling_factor_ == 4 || + down_sampling_factor_ == 8); +} + +void Decimator::Decimate(ArrayView in, ArrayView out) { + RTC_DCHECK_EQ(kBlockSize, in.size()); + RTC_DCHECK_EQ(kBlockSize / down_sampling_factor_, out.size()); + std::array x; + + // Limit the frequency content of the signal to avoid aliasing. + anti_aliasing_filter_.Process(in, x); + + // Reduce the impact of near-end noise. + noise_reduction_filter_.Process(x); + + // Downsample the signal. + for (size_t j = 0, k = 0; j < out.size(); ++j, k += down_sampling_factor_) { + RTC_DCHECK_GT(kBlockSize, k); + out[j] = x[k]; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/decimator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/decimator.h new file mode 100644 index 00000000..69a022ce --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/decimator.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/utility/cascaded_biquad_filter.h" + +namespace webrtc { + +// Provides functionality for decimating a signal. +class Decimator { + public: + explicit Decimator(size_t down_sampling_factor); + + Decimator(const Decimator&) = delete; + Decimator& operator=(const Decimator&) = delete; + + // Downsamples the signal. + void Decimate(ArrayView in, ArrayView out); + + private: + const size_t down_sampling_factor_; + CascadedBiQuadFilter anti_aliasing_filter_; + CascadedBiQuadFilter noise_reduction_filter_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/delay_estimate.h b/pkg/apm/webrtc/modules/audio_processing/aec3/delay_estimate.h new file mode 100644 index 00000000..7838a0c2 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/delay_estimate.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_DELAY_ESTIMATE_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_DELAY_ESTIMATE_H_ + +#include + +namespace webrtc { + +// Stores delay_estimates. +struct DelayEstimate { + enum class Quality { kCoarse, kRefined }; + + DelayEstimate(Quality quality, size_t delay) + : quality(quality), delay(delay) {} + + Quality quality; + size_t delay; + size_t blocks_since_last_change = 0; + size_t blocks_since_last_update = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_DELAY_ESTIMATE_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.cc new file mode 100644 index 00000000..815e1901 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/dominant_nearend_detector.h" + +#include + +namespace webrtc { +DominantNearendDetector::DominantNearendDetector( + const EchoCanceller3Config::Suppressor::DominantNearendDetection& config, + size_t num_capture_channels) + : enr_threshold_(config.enr_threshold), + enr_exit_threshold_(config.enr_exit_threshold), + snr_threshold_(config.snr_threshold), + hold_duration_(config.hold_duration), + trigger_threshold_(config.trigger_threshold), + use_during_initial_phase_(config.use_during_initial_phase), + num_capture_channels_(num_capture_channels), + trigger_counters_(num_capture_channels_), + hold_counters_(num_capture_channels_) {} + +void DominantNearendDetector::Update( + ArrayView> nearend_spectrum, + ArrayView> + residual_echo_spectrum, + ArrayView> + comfort_noise_spectrum, + bool initial_state) { + nearend_state_ = false; + + auto low_frequency_energy = [](ArrayView spectrum) { + RTC_DCHECK_LE(16, spectrum.size()); + return std::accumulate(spectrum.begin() + 1, spectrum.begin() + 16, 0.f); + }; + + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + const float ne_sum = low_frequency_energy(nearend_spectrum[ch]); + const float echo_sum = low_frequency_energy(residual_echo_spectrum[ch]); + const float noise_sum = low_frequency_energy(comfort_noise_spectrum[ch]); + + // Detect strong active nearend if the nearend is sufficiently stronger than + // the echo and the nearend noise. + if ((!initial_state || use_during_initial_phase_) && + echo_sum < enr_threshold_ * ne_sum && + ne_sum > snr_threshold_ * noise_sum) { + if (++trigger_counters_[ch] >= trigger_threshold_) { + // After a period of strong active nearend activity, flag nearend mode. + hold_counters_[ch] = hold_duration_; + trigger_counters_[ch] = trigger_threshold_; + } + } else { + // Forget previously detected strong active nearend activity. + trigger_counters_[ch] = std::max(0, trigger_counters_[ch] - 1); + } + + // Exit nearend-state early at strong echo. + if (echo_sum > enr_exit_threshold_ * ne_sum && + echo_sum > snr_threshold_ * noise_sum) { + hold_counters_[ch] = 0; + } + + // Remain in any nearend mode for a certain duration. + hold_counters_[ch] = std::max(0, hold_counters_[ch] - 1); + nearend_state_ = nearend_state_ || hold_counters_[ch] > 0; + } +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.h b/pkg/apm/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.h new file mode 100644 index 00000000..625f3d0a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_DOMINANT_NEAREND_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_DOMINANT_NEAREND_DETECTOR_H_ + +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/nearend_detector.h" + +namespace webrtc { +// Class for selecting whether the suppressor is in the nearend or echo state. +class DominantNearendDetector : public NearendDetector { + public: + DominantNearendDetector( + const EchoCanceller3Config::Suppressor::DominantNearendDetection& config, + size_t num_capture_channels); + + // Returns whether the current state is the nearend state. + bool IsNearendState() const override { return nearend_state_; } + + // Updates the state selection based on latest spectral estimates. + void Update( + ArrayView> nearend_spectrum, + ArrayView> + residual_echo_spectrum, + ArrayView> + comfort_noise_spectrum, + bool initial_state) override; + + private: + const float enr_threshold_; + const float enr_exit_threshold_; + const float snr_threshold_; + const int hold_duration_; + const int trigger_threshold_; + const bool use_during_initial_phase_; + const size_t num_capture_channels_; + + bool nearend_state_ = false; + std::vector trigger_counters_; + std::vector hold_counters_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_DOMINANT_NEAREND_DETECTOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.cc new file mode 100644 index 00000000..c105911a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.cc @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" + +#include + +namespace webrtc { + +DownsampledRenderBuffer::DownsampledRenderBuffer(size_t downsampled_buffer_size) + : size(static_cast(downsampled_buffer_size)), + buffer(downsampled_buffer_size, 0.f) { + std::fill(buffer.begin(), buffer.end(), 0.f); +} + +DownsampledRenderBuffer::~DownsampledRenderBuffer() = default; + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.h new file mode 100644 index 00000000..fbdc9b4e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_DOWNSAMPLED_RENDER_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_DOWNSAMPLED_RENDER_BUFFER_H_ + +#include + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +// Holds the circular buffer of the downsampled render data. +struct DownsampledRenderBuffer { + explicit DownsampledRenderBuffer(size_t downsampled_buffer_size); + ~DownsampledRenderBuffer(); + + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; + } + + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index > 0 ? index - 1 : size - 1; + } + + int OffsetIndex(int index, int offset) const { + RTC_DCHECK_GE(buffer.size(), offset); + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return (size + index + offset) % size; + } + + void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } + void IncWriteIndex() { write = IncIndex(write); } + void DecWriteIndex() { write = DecIndex(write); } + void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); } + void IncReadIndex() { read = IncIndex(read); } + void DecReadIndex() { read = DecIndex(read); } + + const int size; + std::vector buffer; + int write = 0; + int read = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_DOWNSAMPLED_RENDER_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_audibility.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_audibility.cc new file mode 100644 index 00000000..b0c26ba3 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_audibility.cc @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/echo_audibility.h" + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/block_buffer.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "modules/audio_processing/aec3/stationarity_estimator.h" + +namespace webrtc { + +EchoAudibility::EchoAudibility(bool use_render_stationarity_at_init) + : use_render_stationarity_at_init_(use_render_stationarity_at_init) { + Reset(); +} + +EchoAudibility::~EchoAudibility() = default; + +void EchoAudibility::Update(const RenderBuffer& render_buffer, + ArrayView average_reverb, + int delay_blocks, + bool external_delay_seen) { + UpdateRenderNoiseEstimator(render_buffer.GetSpectrumBuffer(), + render_buffer.GetBlockBuffer(), + external_delay_seen); + + if (external_delay_seen || use_render_stationarity_at_init_) { + UpdateRenderStationarityFlags(render_buffer, average_reverb, delay_blocks); + } +} + +void EchoAudibility::Reset() { + render_stationarity_.Reset(); + non_zero_render_seen_ = false; + render_spectrum_write_prev_ = std::nullopt; +} + +void EchoAudibility::UpdateRenderStationarityFlags( + const RenderBuffer& render_buffer, + ArrayView average_reverb, + int min_channel_delay_blocks) { + const SpectrumBuffer& spectrum_buffer = render_buffer.GetSpectrumBuffer(); + int idx_at_delay = spectrum_buffer.OffsetIndex(spectrum_buffer.read, + min_channel_delay_blocks); + + int num_lookahead = render_buffer.Headroom() - min_channel_delay_blocks + 1; + num_lookahead = std::max(0, num_lookahead); + + render_stationarity_.UpdateStationarityFlags(spectrum_buffer, average_reverb, + idx_at_delay, num_lookahead); +} + +void EchoAudibility::UpdateRenderNoiseEstimator( + const SpectrumBuffer& spectrum_buffer, + const BlockBuffer& block_buffer, + bool external_delay_seen) { + if (!render_spectrum_write_prev_) { + render_spectrum_write_prev_ = spectrum_buffer.write; + render_block_write_prev_ = block_buffer.write; + return; + } + int render_spectrum_write_current = spectrum_buffer.write; + if (!non_zero_render_seen_ && !external_delay_seen) { + non_zero_render_seen_ = !IsRenderTooLow(block_buffer); + } + if (non_zero_render_seen_) { + for (int idx = render_spectrum_write_prev_.value(); + idx != render_spectrum_write_current; + idx = spectrum_buffer.DecIndex(idx)) { + render_stationarity_.UpdateNoiseEstimator(spectrum_buffer.buffer[idx]); + } + } + render_spectrum_write_prev_ = render_spectrum_write_current; +} + +bool EchoAudibility::IsRenderTooLow(const BlockBuffer& block_buffer) { + const int num_render_channels = + static_cast(block_buffer.buffer[0].NumChannels()); + bool too_low = false; + const int render_block_write_current = block_buffer.write; + if (render_block_write_current == render_block_write_prev_) { + too_low = true; + } else { + for (int idx = render_block_write_prev_; idx != render_block_write_current; + idx = block_buffer.IncIndex(idx)) { + float max_abs_over_channels = 0.f; + for (int ch = 0; ch < num_render_channels; ++ch) { + ArrayView block = + block_buffer.buffer[idx].View(/*band=*/0, /*channel=*/ch); + auto r = std::minmax_element(block.cbegin(), block.cend()); + float max_abs_channel = + std::max(std::fabs(*r.first), std::fabs(*r.second)); + max_abs_over_channels = + std::max(max_abs_over_channels, max_abs_channel); + } + if (max_abs_over_channels < 10.f) { + too_low = true; // Discards all blocks if one of them is too low. + break; + } + } + } + render_block_write_prev_ = render_block_write_current; + return too_low; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_audibility.h b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_audibility.h new file mode 100644 index 00000000..44df9266 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_audibility.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_AUDIBILITY_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_AUDIBILITY_H_ + +#include + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/block_buffer.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "modules/audio_processing/aec3/stationarity_estimator.h" + +namespace webrtc { + +class EchoAudibility { + public: + explicit EchoAudibility(bool use_render_stationarity_at_init); + ~EchoAudibility(); + + EchoAudibility(const EchoAudibility&) = delete; + EchoAudibility& operator=(const EchoAudibility&) = delete; + + // Feed new render data to the echo audibility estimator. + void Update(const RenderBuffer& render_buffer, + ArrayView average_reverb, + int min_channel_delay_blocks, + bool external_delay_seen); + // Get the residual echo scaling. + void GetResidualEchoScaling(bool filter_has_had_time_to_converge, + ArrayView residual_scaling) const { + for (size_t band = 0; band < residual_scaling.size(); ++band) { + if (render_stationarity_.IsBandStationary(band) && + (filter_has_had_time_to_converge || + use_render_stationarity_at_init_)) { + residual_scaling[band] = 0.f; + } else { + residual_scaling[band] = 1.0f; + } + } + } + + // Returns true if the current render block is estimated as stationary. + bool IsBlockStationary() const { + return render_stationarity_.IsBlockStationary(); + } + + private: + // Reset the EchoAudibility class. + void Reset(); + + // Updates the render stationarity flags for the current frame. + void UpdateRenderStationarityFlags(const RenderBuffer& render_buffer, + ArrayView average_reverb, + int delay_blocks); + + // Updates the noise estimator with the new render data since the previous + // call to this method. + void UpdateRenderNoiseEstimator(const SpectrumBuffer& spectrum_buffer, + const BlockBuffer& block_buffer, + bool external_delay_seen); + + // Returns a bool being true if the render signal contains just close to zero + // values. + bool IsRenderTooLow(const BlockBuffer& block_buffer); + + std::optional render_spectrum_write_prev_; + int render_block_write_prev_; + bool non_zero_render_seen_; + const bool use_render_stationarity_at_init_; + StationarityEstimator render_stationarity_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_AUDIBILITY_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_canceller3.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_canceller3.cc new file mode 100644 index 00000000..b6752efe --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_canceller3.cc @@ -0,0 +1,1003 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/aec3/echo_canceller3.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/block.h" +#include "modules/audio_processing/aec3/block_delay_buffer.h" +#include "modules/audio_processing/aec3/block_framer.h" +#include "modules/audio_processing/aec3/block_processor.h" +#include "modules/audio_processing/aec3/frame_blocker.h" +#include "modules/audio_processing/high_pass_filter.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/experiments/field_trial_parser.h" +#include "rtc_base/logging.h" +#include "rtc_base/race_checker.h" +#include "rtc_base/swap_queue.h" + +namespace webrtc { + +namespace { + +enum class EchoCanceller3ApiCall { kCapture, kRender }; + +bool DetectSaturation(ArrayView y) { + for (size_t k = 0; k < y.size(); ++k) { + if (y[k] >= 32700.0f || y[k] <= -32700.0f) { + return true; + } + } + return false; +} + +// Retrieves a value from a field trial if it is available. If no value is +// present, the default value is returned. If the retrieved value is beyond the +// specified limits, the default value is returned instead. +void RetrieveFieldTrialValue(const FieldTrialsView& field_trials, + absl::string_view trial_name, + float min, + float max, + float* value_to_update) { + const std::string field_trial_str = field_trials.Lookup(trial_name); + + FieldTrialParameter field_trial_param(/*key=*/"", *value_to_update); + + ParseFieldTrial({&field_trial_param}, field_trial_str); + float field_trial_value = static_cast(field_trial_param.Get()); + + if (field_trial_value >= min && field_trial_value <= max && + field_trial_value != *value_to_update) { + RTC_LOG(LS_INFO) << "Key " << trial_name + << " changing AEC3 parameter value from " + << *value_to_update << " to " << field_trial_value; + *value_to_update = field_trial_value; + } +} + +void RetrieveFieldTrialValue(const FieldTrialsView& field_trials, + absl::string_view trial_name, + int min, + int max, + int* value_to_update) { + const std::string field_trial_str = field_trials.Lookup(trial_name); + + FieldTrialParameter field_trial_param(/*key=*/"", *value_to_update); + + ParseFieldTrial({&field_trial_param}, field_trial_str); + float field_trial_value = field_trial_param.Get(); + + if (field_trial_value >= min && field_trial_value <= max && + field_trial_value != *value_to_update) { + RTC_LOG(LS_INFO) << "Key " << trial_name + << " changing AEC3 parameter value from " + << *value_to_update << " to " << field_trial_value; + *value_to_update = field_trial_value; + } +} + +void FillSubFrameView( + AudioBuffer* frame, + size_t sub_frame_index, + std::vector>>* sub_frame_view) { + RTC_DCHECK_GE(1, sub_frame_index); + RTC_DCHECK_LE(0, sub_frame_index); + RTC_DCHECK_EQ(frame->num_bands(), sub_frame_view->size()); + RTC_DCHECK_EQ(frame->num_channels(), (*sub_frame_view)[0].size()); + for (size_t band = 0; band < sub_frame_view->size(); ++band) { + for (size_t channel = 0; channel < (*sub_frame_view)[0].size(); ++channel) { + (*sub_frame_view)[band][channel] = ArrayView( + &frame->split_bands(channel)[band][sub_frame_index * kSubFrameLength], + kSubFrameLength); + } + } +} + +void FillSubFrameView( + bool proper_downmix_needed, + std::vector>>* frame, + size_t sub_frame_index, + std::vector>>* sub_frame_view) { + RTC_DCHECK_GE(1, sub_frame_index); + RTC_DCHECK_EQ(frame->size(), sub_frame_view->size()); + const size_t frame_num_channels = (*frame)[0].size(); + const size_t sub_frame_num_channels = (*sub_frame_view)[0].size(); + if (frame_num_channels > sub_frame_num_channels) { + RTC_DCHECK_EQ(sub_frame_num_channels, 1u); + if (proper_downmix_needed) { + // When a proper downmix is needed (which is the case when proper stereo + // is present in the echo reference signal but the echo canceller does the + // processing in mono) downmix the echo reference by averaging the channel + // content (otherwise downmixing is done by selecting channel 0). + for (size_t band = 0; band < frame->size(); ++band) { + for (size_t ch = 1; ch < frame_num_channels; ++ch) { + for (size_t k = 0; k < kSubFrameLength; ++k) { + (*frame)[band][/*channel=*/0] + [sub_frame_index * kSubFrameLength + k] += + (*frame)[band][ch][sub_frame_index * kSubFrameLength + k]; + } + } + const float one_by_num_channels = 1.0f / frame_num_channels; + for (size_t k = 0; k < kSubFrameLength; ++k) { + (*frame)[band][/*channel=*/0][sub_frame_index * kSubFrameLength + + k] *= one_by_num_channels; + } + } + } + for (size_t band = 0; band < frame->size(); ++band) { + (*sub_frame_view)[band][/*channel=*/0] = ArrayView( + &(*frame)[band][/*channel=*/0][sub_frame_index * kSubFrameLength], + kSubFrameLength); + } + } else { + RTC_DCHECK_EQ(frame_num_channels, sub_frame_num_channels); + for (size_t band = 0; band < frame->size(); ++band) { + for (size_t channel = 0; channel < (*frame)[band].size(); ++channel) { + (*sub_frame_view)[band][channel] = ArrayView( + &(*frame)[band][channel][sub_frame_index * kSubFrameLength], + kSubFrameLength); + } + } + } +} + +void ProcessCaptureFrameContent( + AudioBuffer* linear_output, + AudioBuffer* capture, + bool level_change, + bool aec_reference_is_downmixed_stereo, + bool saturated_microphone_signal, + size_t sub_frame_index, + FrameBlocker* capture_blocker, + BlockFramer* linear_output_framer, + BlockFramer* output_framer, + BlockProcessor* block_processor, + Block* linear_output_block, + std::vector>>* linear_output_sub_frame_view, + Block* capture_block, + std::vector>>* capture_sub_frame_view) { + FillSubFrameView(capture, sub_frame_index, capture_sub_frame_view); + + if (linear_output) { + RTC_DCHECK(linear_output_framer); + RTC_DCHECK(linear_output_block); + RTC_DCHECK(linear_output_sub_frame_view); + FillSubFrameView(linear_output, sub_frame_index, + linear_output_sub_frame_view); + } + + capture_blocker->InsertSubFrameAndExtractBlock(*capture_sub_frame_view, + capture_block); + block_processor->ProcessCapture( + /*echo_path_gain_change=*/level_change || + aec_reference_is_downmixed_stereo, + saturated_microphone_signal, linear_output_block, capture_block); + output_framer->InsertBlockAndExtractSubFrame(*capture_block, + capture_sub_frame_view); + + if (linear_output) { + RTC_DCHECK(linear_output_framer); + linear_output_framer->InsertBlockAndExtractSubFrame( + *linear_output_block, linear_output_sub_frame_view); + } +} + +void ProcessRemainingCaptureFrameContent(bool level_change, + bool aec_reference_is_downmixed_stereo, + bool saturated_microphone_signal, + FrameBlocker* capture_blocker, + BlockFramer* linear_output_framer, + BlockFramer* output_framer, + BlockProcessor* block_processor, + Block* linear_output_block, + Block* block) { + if (!capture_blocker->IsBlockAvailable()) { + return; + } + + capture_blocker->ExtractBlock(block); + block_processor->ProcessCapture( + /*echo_path_gain_change=*/level_change || + aec_reference_is_downmixed_stereo, + saturated_microphone_signal, linear_output_block, block); + output_framer->InsertBlock(*block); + + if (linear_output_framer) { + RTC_DCHECK(linear_output_block); + linear_output_framer->InsertBlock(*linear_output_block); + } +} + +void BufferRenderFrameContent( + bool proper_downmix_needed, + std::vector>>* render_frame, + size_t sub_frame_index, + FrameBlocker* render_blocker, + BlockProcessor* block_processor, + Block* block, + std::vector>>* sub_frame_view) { + FillSubFrameView(proper_downmix_needed, render_frame, sub_frame_index, + sub_frame_view); + render_blocker->InsertSubFrameAndExtractBlock(*sub_frame_view, block); + block_processor->BufferRender(*block); +} + +void BufferRemainingRenderFrameContent(FrameBlocker* render_blocker, + BlockProcessor* block_processor, + Block* block) { + if (!render_blocker->IsBlockAvailable()) { + return; + } + render_blocker->ExtractBlock(block); + block_processor->BufferRender(*block); +} + +void CopyBufferIntoFrame(const AudioBuffer& buffer, + size_t num_bands, + size_t num_channels, + std::vector>>* frame) { + RTC_DCHECK_EQ(num_bands, frame->size()); + RTC_DCHECK_EQ(num_channels, (*frame)[0].size()); + RTC_DCHECK_EQ(AudioBuffer::kSplitBandSize, (*frame)[0][0].size()); + for (size_t band = 0; band < num_bands; ++band) { + for (size_t channel = 0; channel < num_channels; ++channel) { + ArrayView buffer_view( + &buffer.split_bands_const(channel)[band][0], + AudioBuffer::kSplitBandSize); + std::copy(buffer_view.begin(), buffer_view.end(), + (*frame)[band][channel].begin()); + } + } +} + +} // namespace + +// TODO(webrtc:5298): Move this to a separate file. +EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config, + const FieldTrialsView& field_trials) { + EchoCanceller3Config adjusted_cfg = config; + + if (field_trials.IsEnabled("WebRTC-Aec3StereoContentDetectionKillSwitch")) { + adjusted_cfg.multi_channel.detect_stereo_content = false; + } + + if (field_trials.IsEnabled("WebRTC-Aec3AntiHowlingMinimizationKillSwitch")) { + adjusted_cfg.suppressor.high_bands_suppression + .anti_howling_activation_threshold = 25.f; + adjusted_cfg.suppressor.high_bands_suppression.anti_howling_gain = 0.01f; + } + + if (field_trials.IsEnabled("WebRTC-Aec3UseShortConfigChangeDuration")) { + adjusted_cfg.filter.config_change_duration_blocks = 10; + } + + if (field_trials.IsEnabled("WebRTC-Aec3UseZeroInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = 0.f; + } else if (field_trials.IsEnabled( + "WebRTC-Aec3UseDot1SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = .1f; + } else if (field_trials.IsEnabled( + "WebRTC-Aec3UseDot2SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = .2f; + } else if (field_trials.IsEnabled( + "WebRTC-Aec3UseDot3SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = .3f; + } else if (field_trials.IsEnabled( + "WebRTC-Aec3UseDot6SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = .6f; + } else if (field_trials.IsEnabled( + "WebRTC-Aec3UseDot9SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = .9f; + } else if (field_trials.IsEnabled( + "WebRTC-Aec3Use1Dot2SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = 1.2f; + } else if (field_trials.IsEnabled( + "WebRTC-Aec3Use1Dot6SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = 1.6f; + } else if (field_trials.IsEnabled( + "WebRTC-Aec3Use2Dot0SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = 2.0f; + } + + if (field_trials.IsEnabled("WebRTC-Aec3HighPassFilterEchoReference")) { + adjusted_cfg.filter.high_pass_filter_echo_reference = true; + } + + if (field_trials.IsEnabled("WebRTC-Aec3EchoSaturationDetectionKillSwitch")) { + adjusted_cfg.ep_strength.echo_can_saturate = false; + } + + const std::string use_nearend_reverb_len_tunings = + field_trials.Lookup("WebRTC-Aec3UseNearendReverbLen"); + FieldTrialParameter nearend_reverb_default_len( + "default_len", adjusted_cfg.ep_strength.default_len); + FieldTrialParameter nearend_reverb_nearend_len( + "nearend_len", adjusted_cfg.ep_strength.nearend_len); + + ParseFieldTrial({&nearend_reverb_default_len, &nearend_reverb_nearend_len}, + use_nearend_reverb_len_tunings); + float default_len = static_cast(nearend_reverb_default_len.Get()); + float nearend_len = static_cast(nearend_reverb_nearend_len.Get()); + if (default_len > -1 && default_len < 1 && nearend_len > -1 && + nearend_len < 1) { + adjusted_cfg.ep_strength.default_len = + static_cast(nearend_reverb_default_len.Get()); + adjusted_cfg.ep_strength.nearend_len = + static_cast(nearend_reverb_nearend_len.Get()); + } + + if (field_trials.IsEnabled("WebRTC-Aec3ConservativeTailFreqResponse")) { + adjusted_cfg.ep_strength.use_conservative_tail_frequency_response = true; + } + + if (field_trials.IsDisabled("WebRTC-Aec3ConservativeTailFreqResponse")) { + adjusted_cfg.ep_strength.use_conservative_tail_frequency_response = false; + } + + if (field_trials.IsEnabled("WebRTC-Aec3ShortHeadroomKillSwitch")) { + // Two blocks headroom. + adjusted_cfg.delay.delay_headroom_samples = kBlockSize * 2; + } + + if (field_trials.IsEnabled("WebRTC-Aec3ClampInstQualityToZeroKillSwitch")) { + adjusted_cfg.erle.clamp_quality_estimate_to_zero = false; + } + + if (field_trials.IsEnabled("WebRTC-Aec3ClampInstQualityToOneKillSwitch")) { + adjusted_cfg.erle.clamp_quality_estimate_to_one = false; + } + + if (field_trials.IsEnabled("WebRTC-Aec3OnsetDetectionKillSwitch")) { + adjusted_cfg.erle.onset_detection = false; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceRenderDelayEstimationDownmixing")) { + adjusted_cfg.delay.render_alignment_mixing.downmix = true; + adjusted_cfg.delay.render_alignment_mixing.adaptive_selection = false; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceCaptureDelayEstimationDownmixing")) { + adjusted_cfg.delay.capture_alignment_mixing.downmix = true; + adjusted_cfg.delay.capture_alignment_mixing.adaptive_selection = false; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceCaptureDelayEstimationLeftRightPrioritization")) { + adjusted_cfg.delay.capture_alignment_mixing.prefer_first_two_channels = + true; + } + + if (field_trials.IsEnabled( + "WebRTC-" + "Aec3RenderDelayEstimationLeftRightPrioritizationKillSwitch")) { + adjusted_cfg.delay.capture_alignment_mixing.prefer_first_two_channels = + false; + } + + if (field_trials.IsEnabled("WebRTC-Aec3SensitiveDominantNearendActivation")) { + adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold = 0.5f; + } else if (field_trials.IsEnabled( + "WebRTC-Aec3VerySensitiveDominantNearendActivation")) { + adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold = 0.75f; + } + + if (field_trials.IsEnabled("WebRTC-Aec3TransparentAntiHowlingGain")) { + adjusted_cfg.suppressor.high_bands_suppression.anti_howling_gain = 1.f; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceMoreTransparentNormalSuppressorTuning")) { + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_transparent = 0.4f; + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_suppress = 0.5f; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceMoreTransparentNearendSuppressorTuning")) { + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_transparent = 1.29f; + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_suppress = 1.3f; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceMoreTransparentNormalSuppressorHfTuning")) { + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_transparent = 0.3f; + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_suppress = 0.4f; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceMoreTransparentNearendSuppressorHfTuning")) { + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_transparent = 1.09f; + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_suppress = 1.1f; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceRapidlyAdjustingNormalSuppressorTunings")) { + adjusted_cfg.suppressor.normal_tuning.max_inc_factor = 2.5f; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceRapidlyAdjustingNearendSuppressorTunings")) { + adjusted_cfg.suppressor.nearend_tuning.max_inc_factor = 2.5f; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceSlowlyAdjustingNormalSuppressorTunings")) { + adjusted_cfg.suppressor.normal_tuning.max_dec_factor_lf = .2f; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceSlowlyAdjustingNearendSuppressorTunings")) { + adjusted_cfg.suppressor.nearend_tuning.max_dec_factor_lf = .2f; + } + + if (field_trials.IsEnabled("WebRTC-Aec3EnforceConservativeHfSuppression")) { + adjusted_cfg.suppressor.conservative_hf_suppression = true; + } + + if (field_trials.IsEnabled("WebRTC-Aec3EnforceStationarityProperties")) { + adjusted_cfg.echo_audibility.use_stationarity_properties = true; + } + + if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceStationarityPropertiesAtInit")) { + adjusted_cfg.echo_audibility.use_stationarity_properties_at_init = true; + } + + if (field_trials.IsEnabled("WebRTC-Aec3EnforceLowActiveRenderLimit")) { + adjusted_cfg.render_levels.active_render_limit = 50.f; + } else if (field_trials.IsEnabled( + "WebRTC-Aec3EnforceVeryLowActiveRenderLimit")) { + adjusted_cfg.render_levels.active_render_limit = 30.f; + } + + if (field_trials.IsEnabled("WebRTC-Aec3NonlinearModeReverbKillSwitch")) { + adjusted_cfg.echo_model.model_reverb_in_nonlinear_mode = false; + } + + // Field-trial based override for the whole suppressor tuning. + const std::string suppressor_tuning_override_trial_name = + field_trials.Lookup("WebRTC-Aec3SuppressorTuningOverride"); + + FieldTrialParameter nearend_tuning_mask_lf_enr_transparent( + "nearend_tuning_mask_lf_enr_transparent", + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_transparent); + FieldTrialParameter nearend_tuning_mask_lf_enr_suppress( + "nearend_tuning_mask_lf_enr_suppress", + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_suppress); + FieldTrialParameter nearend_tuning_mask_hf_enr_transparent( + "nearend_tuning_mask_hf_enr_transparent", + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_transparent); + FieldTrialParameter nearend_tuning_mask_hf_enr_suppress( + "nearend_tuning_mask_hf_enr_suppress", + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_suppress); + FieldTrialParameter nearend_tuning_max_inc_factor( + "nearend_tuning_max_inc_factor", + adjusted_cfg.suppressor.nearend_tuning.max_inc_factor); + FieldTrialParameter nearend_tuning_max_dec_factor_lf( + "nearend_tuning_max_dec_factor_lf", + adjusted_cfg.suppressor.nearend_tuning.max_dec_factor_lf); + FieldTrialParameter normal_tuning_mask_lf_enr_transparent( + "normal_tuning_mask_lf_enr_transparent", + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_transparent); + FieldTrialParameter normal_tuning_mask_lf_enr_suppress( + "normal_tuning_mask_lf_enr_suppress", + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_suppress); + FieldTrialParameter normal_tuning_mask_hf_enr_transparent( + "normal_tuning_mask_hf_enr_transparent", + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_transparent); + FieldTrialParameter normal_tuning_mask_hf_enr_suppress( + "normal_tuning_mask_hf_enr_suppress", + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_suppress); + FieldTrialParameter normal_tuning_max_inc_factor( + "normal_tuning_max_inc_factor", + adjusted_cfg.suppressor.normal_tuning.max_inc_factor); + FieldTrialParameter normal_tuning_max_dec_factor_lf( + "normal_tuning_max_dec_factor_lf", + adjusted_cfg.suppressor.normal_tuning.max_dec_factor_lf); + FieldTrialParameter dominant_nearend_detection_enr_threshold( + "dominant_nearend_detection_enr_threshold", + adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold); + FieldTrialParameter dominant_nearend_detection_enr_exit_threshold( + "dominant_nearend_detection_enr_exit_threshold", + adjusted_cfg.suppressor.dominant_nearend_detection.enr_exit_threshold); + FieldTrialParameter dominant_nearend_detection_snr_threshold( + "dominant_nearend_detection_snr_threshold", + adjusted_cfg.suppressor.dominant_nearend_detection.snr_threshold); + FieldTrialParameter dominant_nearend_detection_hold_duration( + "dominant_nearend_detection_hold_duration", + adjusted_cfg.suppressor.dominant_nearend_detection.hold_duration); + FieldTrialParameter dominant_nearend_detection_trigger_threshold( + "dominant_nearend_detection_trigger_threshold", + adjusted_cfg.suppressor.dominant_nearend_detection.trigger_threshold); + + ParseFieldTrial( + {&nearend_tuning_mask_lf_enr_transparent, + &nearend_tuning_mask_lf_enr_suppress, + &nearend_tuning_mask_hf_enr_transparent, + &nearend_tuning_mask_hf_enr_suppress, &nearend_tuning_max_inc_factor, + &nearend_tuning_max_dec_factor_lf, + &normal_tuning_mask_lf_enr_transparent, + &normal_tuning_mask_lf_enr_suppress, + &normal_tuning_mask_hf_enr_transparent, + &normal_tuning_mask_hf_enr_suppress, &normal_tuning_max_inc_factor, + &normal_tuning_max_dec_factor_lf, + &dominant_nearend_detection_enr_threshold, + &dominant_nearend_detection_enr_exit_threshold, + &dominant_nearend_detection_snr_threshold, + &dominant_nearend_detection_hold_duration, + &dominant_nearend_detection_trigger_threshold}, + suppressor_tuning_override_trial_name); + + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_transparent = + static_cast(nearend_tuning_mask_lf_enr_transparent.Get()); + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_suppress = + static_cast(nearend_tuning_mask_lf_enr_suppress.Get()); + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_transparent = + static_cast(nearend_tuning_mask_hf_enr_transparent.Get()); + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_suppress = + static_cast(nearend_tuning_mask_hf_enr_suppress.Get()); + adjusted_cfg.suppressor.nearend_tuning.max_inc_factor = + static_cast(nearend_tuning_max_inc_factor.Get()); + adjusted_cfg.suppressor.nearend_tuning.max_dec_factor_lf = + static_cast(nearend_tuning_max_dec_factor_lf.Get()); + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_transparent = + static_cast(normal_tuning_mask_lf_enr_transparent.Get()); + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_suppress = + static_cast(normal_tuning_mask_lf_enr_suppress.Get()); + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_transparent = + static_cast(normal_tuning_mask_hf_enr_transparent.Get()); + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_suppress = + static_cast(normal_tuning_mask_hf_enr_suppress.Get()); + adjusted_cfg.suppressor.normal_tuning.max_inc_factor = + static_cast(normal_tuning_max_inc_factor.Get()); + adjusted_cfg.suppressor.normal_tuning.max_dec_factor_lf = + static_cast(normal_tuning_max_dec_factor_lf.Get()); + adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold = + static_cast(dominant_nearend_detection_enr_threshold.Get()); + adjusted_cfg.suppressor.dominant_nearend_detection.enr_exit_threshold = + static_cast(dominant_nearend_detection_enr_exit_threshold.Get()); + adjusted_cfg.suppressor.dominant_nearend_detection.snr_threshold = + static_cast(dominant_nearend_detection_snr_threshold.Get()); + adjusted_cfg.suppressor.dominant_nearend_detection.hold_duration = + dominant_nearend_detection_hold_duration.Get(); + adjusted_cfg.suppressor.dominant_nearend_detection.trigger_threshold = + dominant_nearend_detection_trigger_threshold.Get(); + + // Field trial-based overrides of individual suppressor parameters. + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNearendLfMaskTransparentOverride", + 0.f, 10.f, + &adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_transparent); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNearendLfMaskSuppressOverride", 0.f, + 10.f, &adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_suppress); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNearendHfMaskTransparentOverride", + 0.f, 10.f, + &adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_transparent); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNearendHfMaskSuppressOverride", 0.f, + 10.f, &adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_suppress); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNearendMaxIncFactorOverride", 0.f, + 10.f, &adjusted_cfg.suppressor.nearend_tuning.max_inc_factor); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNearendMaxDecFactorLfOverride", 0.f, + 10.f, &adjusted_cfg.suppressor.nearend_tuning.max_dec_factor_lf); + + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNormalLfMaskTransparentOverride", 0.f, + 10.f, &adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_transparent); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNormalLfMaskSuppressOverride", 0.f, + 10.f, &adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_suppress); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNormalHfMaskTransparentOverride", 0.f, + 10.f, &adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_transparent); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNormalHfMaskSuppressOverride", 0.f, + 10.f, &adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_suppress); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNormalMaxIncFactorOverride", 0.f, + 10.f, &adjusted_cfg.suppressor.normal_tuning.max_inc_factor); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorNormalMaxDecFactorLfOverride", 0.f, + 10.f, &adjusted_cfg.suppressor.normal_tuning.max_dec_factor_lf); + + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorDominantNearendEnrThresholdOverride", + 0.f, 100.f, + &adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold); + RetrieveFieldTrialValue( + field_trials, + "WebRTC-Aec3SuppressorDominantNearendEnrExitThresholdOverride", 0.f, + 100.f, + &adjusted_cfg.suppressor.dominant_nearend_detection.enr_exit_threshold); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorDominantNearendSnrThresholdOverride", + 0.f, 100.f, + &adjusted_cfg.suppressor.dominant_nearend_detection.snr_threshold); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorDominantNearendHoldDurationOverride", + 0, 1000, + &adjusted_cfg.suppressor.dominant_nearend_detection.hold_duration); + RetrieveFieldTrialValue( + field_trials, + "WebRTC-Aec3SuppressorDominantNearendTriggerThresholdOverride", 0, 1000, + &adjusted_cfg.suppressor.dominant_nearend_detection.trigger_threshold); + + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3SuppressorAntiHowlingGainOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.high_bands_suppression.anti_howling_gain); + + // Field trial-based overrides of individual delay estimator parameters. + RetrieveFieldTrialValue(field_trials, + "WebRTC-Aec3DelayEstimateSmoothingOverride", 0.f, 1.f, + &adjusted_cfg.delay.delay_estimate_smoothing); + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3DelayEstimateSmoothingDelayFoundOverride", 0.f, + 1.f, &adjusted_cfg.delay.delay_estimate_smoothing_delay_found); + + int max_allowed_excess_render_blocks_override = + adjusted_cfg.buffering.max_allowed_excess_render_blocks; + RetrieveFieldTrialValue( + field_trials, "WebRTC-Aec3BufferingMaxAllowedExcessRenderBlocksOverride", + 0, 20, &max_allowed_excess_render_blocks_override); + adjusted_cfg.buffering.max_allowed_excess_render_blocks = + max_allowed_excess_render_blocks_override; + return adjusted_cfg; +} + +class EchoCanceller3::RenderWriter { + public: + RenderWriter(ApmDataDumper* data_dumper, + const EchoCanceller3Config& config, + SwapQueue>>, + Aec3RenderQueueItemVerifier>* render_transfer_queue, + size_t num_bands, + size_t num_channels); + + RenderWriter() = delete; + RenderWriter(const RenderWriter&) = delete; + RenderWriter& operator=(const RenderWriter&) = delete; + + ~RenderWriter(); + void Insert(const AudioBuffer& input); + + private: + ApmDataDumper* data_dumper_; + const size_t num_bands_; + const size_t num_channels_; + std::unique_ptr high_pass_filter_; + std::vector>> render_queue_input_frame_; + SwapQueue>>, + Aec3RenderQueueItemVerifier>* render_transfer_queue_; +}; + +EchoCanceller3::RenderWriter::RenderWriter( + ApmDataDumper* data_dumper, + const EchoCanceller3Config& config, + SwapQueue>>, + Aec3RenderQueueItemVerifier>* render_transfer_queue, + size_t num_bands, + size_t num_channels) + : data_dumper_(data_dumper), + num_bands_(num_bands), + num_channels_(num_channels), + render_queue_input_frame_( + num_bands_, + std::vector>( + num_channels_, + std::vector(AudioBuffer::kSplitBandSize, 0.f))), + render_transfer_queue_(render_transfer_queue) { + RTC_DCHECK(data_dumper); + if (config.filter.high_pass_filter_echo_reference) { + high_pass_filter_ = std::make_unique(16000, num_channels); + } +} + +EchoCanceller3::RenderWriter::~RenderWriter() = default; + +void EchoCanceller3::RenderWriter::Insert(const AudioBuffer& input) { + RTC_DCHECK_EQ(AudioBuffer::kSplitBandSize, input.num_frames_per_band()); + RTC_DCHECK_EQ(num_bands_, input.num_bands()); + RTC_DCHECK_EQ(num_channels_, input.num_channels()); + + // TODO(bugs.webrtc.org/8759) Temporary work-around. + if (num_bands_ != input.num_bands()) + return; + + data_dumper_->DumpWav("aec3_render_input", AudioBuffer::kSplitBandSize, + &input.split_bands_const(0)[0][0], 16000, 1); + + CopyBufferIntoFrame(input, num_bands_, num_channels_, + &render_queue_input_frame_); + if (high_pass_filter_) { + high_pass_filter_->Process(&render_queue_input_frame_[0]); + } + + static_cast(render_transfer_queue_->Insert(&render_queue_input_frame_)); +} + +std::atomic EchoCanceller3::instance_count_(0); + +EchoCanceller3::EchoCanceller3( + const Environment& env, + const EchoCanceller3Config& config, + const std::optional& multichannel_config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels) + : env_(env), + data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), + config_(AdjustConfig(config, env.field_trials())), + sample_rate_hz_(sample_rate_hz), + num_bands_(NumBandsForRate(sample_rate_hz_)), + num_render_input_channels_(num_render_channels), + num_capture_channels_(num_capture_channels), + config_selector_(config_, + multichannel_config, + num_render_input_channels_), + multichannel_content_detector_( + config_selector_.active_config().multi_channel.detect_stereo_content, + num_render_input_channels_, + config_selector_.active_config() + .multi_channel.stereo_detection_threshold, + config_selector_.active_config() + .multi_channel.stereo_detection_timeout_threshold_seconds, + config_selector_.active_config() + .multi_channel.stereo_detection_hysteresis_seconds), + output_framer_(num_bands_, num_capture_channels_), + capture_blocker_(num_bands_, num_capture_channels_), + render_transfer_queue_( + kRenderTransferQueueSizeFrames, + std::vector>>( + num_bands_, + std::vector>( + num_render_input_channels_, + std::vector(AudioBuffer::kSplitBandSize, 0.f))), + Aec3RenderQueueItemVerifier(num_bands_, + num_render_input_channels_, + AudioBuffer::kSplitBandSize)), + render_queue_output_frame_( + num_bands_, + std::vector>( + num_render_input_channels_, + std::vector(AudioBuffer::kSplitBandSize, 0.f))), + render_block_(num_bands_, num_render_input_channels_), + capture_block_(num_bands_, num_capture_channels_), + capture_sub_frame_view_( + num_bands_, + std::vector>(num_capture_channels_)) { + RTC_DCHECK(ValidFullBandRate(sample_rate_hz_)); + + if (config_selector_.active_config().delay.fixed_capture_delay_samples > 0) { + block_delay_buffer_.reset(new BlockDelayBuffer( + num_capture_channels_, num_bands_, AudioBuffer::kSplitBandSize, + config_.delay.fixed_capture_delay_samples)); + } + + render_writer_.reset(new RenderWriter( + data_dumper_.get(), config_selector_.active_config(), + &render_transfer_queue_, num_bands_, num_render_input_channels_)); + + RTC_DCHECK_EQ(num_bands_, std::max(sample_rate_hz_, 16000) / 16000); + RTC_DCHECK_GE(kMaxNumBands, num_bands_); + + if (config_selector_.active_config().filter.export_linear_aec_output) { + linear_output_framer_.reset( + new BlockFramer(/*num_bands=*/1, num_capture_channels_)); + linear_output_block_ = + std::make_unique(/*num_bands=*/1, num_capture_channels_), + linear_output_sub_frame_view_ = std::vector>>( + 1, std::vector>(num_capture_channels_)); + } + + Initialize(); + + RTC_LOG(LS_INFO) << "AEC3 created with sample rate: " << sample_rate_hz_ + << " Hz, num render channels: " << num_render_input_channels_ + << ", num capture channels: " << num_capture_channels_; +} + +EchoCanceller3::~EchoCanceller3() = default; + +void EchoCanceller3::Initialize() { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + + num_render_channels_to_aec_ = + multichannel_content_detector_.IsProperMultiChannelContentDetected() + ? num_render_input_channels_ + : 1; + + config_selector_.Update( + multichannel_content_detector_.IsProperMultiChannelContentDetected()); + + render_block_.SetNumChannels(num_render_channels_to_aec_); + + render_blocker_.reset( + new FrameBlocker(num_bands_, num_render_channels_to_aec_)); + + block_processor_ = BlockProcessor::Create( + env_, config_selector_.active_config(), sample_rate_hz_, + num_render_channels_to_aec_, num_capture_channels_); + + render_sub_frame_view_ = std::vector>>( + num_bands_, std::vector>(num_render_channels_to_aec_)); +} + +void EchoCanceller3::AnalyzeRender(const AudioBuffer& render) { + RTC_DCHECK_RUNS_SERIALIZED(&render_race_checker_); + + RTC_DCHECK_EQ(render.num_channels(), num_render_input_channels_); + data_dumper_->DumpRaw("aec3_call_order", + static_cast(EchoCanceller3ApiCall::kRender)); + + return render_writer_->Insert(render); +} + +void EchoCanceller3::AnalyzeCapture(const AudioBuffer& capture) { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + data_dumper_->DumpWav("aec3_capture_analyze_input", capture.num_frames(), + capture.channels_const()[0], sample_rate_hz_, 1); + saturated_microphone_signal_ = false; + for (size_t channel = 0; channel < capture.num_channels(); ++channel) { + saturated_microphone_signal_ |= DetectSaturation(ArrayView( + capture.channels_const()[channel], capture.num_frames())); + if (saturated_microphone_signal_) { + break; + } + } +} + +void EchoCanceller3::ProcessCapture(AudioBuffer* capture, bool level_change) { + ProcessCapture(capture, nullptr, level_change); +} + +void EchoCanceller3::ProcessCapture(AudioBuffer* capture, + AudioBuffer* linear_output, + bool level_change) { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + RTC_DCHECK(capture); + RTC_DCHECK_EQ(num_bands_, capture->num_bands()); + RTC_DCHECK_EQ(AudioBuffer::kSplitBandSize, capture->num_frames_per_band()); + RTC_DCHECK_EQ(capture->num_channels(), num_capture_channels_); + data_dumper_->DumpRaw("aec3_call_order", + static_cast(EchoCanceller3ApiCall::kCapture)); + + if (linear_output && !linear_output_framer_) { + RTC_LOG(LS_ERROR) << "Trying to retrieve the linear AEC output without " + "properly configuring AEC3."; + RTC_DCHECK_NOTREACHED(); + } + + // Report capture call in the metrics and periodically update API call + // metrics. + api_call_metrics_.ReportCaptureCall(); + + // Optionally delay the capture signal. + if (config_selector_.active_config().delay.fixed_capture_delay_samples > 0) { + RTC_DCHECK(block_delay_buffer_); + block_delay_buffer_->DelaySignal(capture); + } + + ArrayView capture_lower_band = ArrayView( + &capture->split_bands(0)[0][0], AudioBuffer::kSplitBandSize); + + data_dumper_->DumpWav("aec3_capture_input", capture_lower_band, 16000, 1); + + EmptyRenderQueue(); + + ProcessCaptureFrameContent( + linear_output, capture, level_change, + multichannel_content_detector_.IsTemporaryMultiChannelContentDetected(), + saturated_microphone_signal_, 0, &capture_blocker_, + linear_output_framer_.get(), &output_framer_, block_processor_.get(), + linear_output_block_.get(), &linear_output_sub_frame_view_, + &capture_block_, &capture_sub_frame_view_); + + ProcessCaptureFrameContent( + linear_output, capture, level_change, + multichannel_content_detector_.IsTemporaryMultiChannelContentDetected(), + saturated_microphone_signal_, 1, &capture_blocker_, + linear_output_framer_.get(), &output_framer_, block_processor_.get(), + linear_output_block_.get(), &linear_output_sub_frame_view_, + &capture_block_, &capture_sub_frame_view_); + + ProcessRemainingCaptureFrameContent( + level_change, + multichannel_content_detector_.IsTemporaryMultiChannelContentDetected(), + saturated_microphone_signal_, &capture_blocker_, + linear_output_framer_.get(), &output_framer_, block_processor_.get(), + linear_output_block_.get(), &capture_block_); + + data_dumper_->DumpWav("aec3_capture_output", AudioBuffer::kSplitBandSize, + &capture->split_bands(0)[0][0], 16000, 1); +} + +EchoControl::Metrics EchoCanceller3::GetMetrics() const { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + Metrics metrics; + block_processor_->GetMetrics(&metrics); + return metrics; +} + +void EchoCanceller3::SetAudioBufferDelay(int delay_ms) { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + block_processor_->SetAudioBufferDelay(delay_ms); +} + +void EchoCanceller3::SetCaptureOutputUsage(bool capture_output_used) { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + block_processor_->SetCaptureOutputUsage(capture_output_used); +} + +bool EchoCanceller3::ActiveProcessing() const { + return true; +} + +void EchoCanceller3::SetBlockProcessorForTesting( + std::unique_ptr block_processor) { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + RTC_DCHECK(block_processor); + block_processor_ = std::move(block_processor); +} + +void EchoCanceller3::EmptyRenderQueue() { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + bool frame_to_buffer = + render_transfer_queue_.Remove(&render_queue_output_frame_); + while (frame_to_buffer) { + // Report render call in the metrics. + api_call_metrics_.ReportRenderCall(); + + if (multichannel_content_detector_.UpdateDetection( + render_queue_output_frame_)) { + // Reinitialize the AEC when proper stereo is detected. + Initialize(); + } + + // Buffer frame content. + BufferRenderFrameContent( + /*proper_downmix_needed=*/multichannel_content_detector_ + .IsTemporaryMultiChannelContentDetected(), + &render_queue_output_frame_, 0, render_blocker_.get(), + block_processor_.get(), &render_block_, &render_sub_frame_view_); + + BufferRenderFrameContent( + /*proper_downmix_needed=*/multichannel_content_detector_ + .IsTemporaryMultiChannelContentDetected(), + &render_queue_output_frame_, 1, render_blocker_.get(), + block_processor_.get(), &render_block_, &render_sub_frame_view_); + + BufferRemainingRenderFrameContent(render_blocker_.get(), + block_processor_.get(), &render_block_); + + frame_to_buffer = + render_transfer_queue_.Remove(&render_queue_output_frame_); + } +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_canceller3.h b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_canceller3.h new file mode 100644 index 00000000..a10cb137 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_canceller3.h @@ -0,0 +1,231 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_CANCELLER3_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_CANCELLER3_H_ + +#include + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "modules/audio_processing/aec3/api_call_jitter_metrics.h" +#include "modules/audio_processing/aec3/block_delay_buffer.h" +#include "modules/audio_processing/aec3/block_framer.h" +#include "modules/audio_processing/aec3/block_processor.h" +#include "modules/audio_processing/aec3/config_selector.h" +#include "modules/audio_processing/aec3/frame_blocker.h" +#include "modules/audio_processing/aec3/multi_channel_content_detector.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/race_checker.h" +#include "rtc_base/swap_queue.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +// Method for adjusting config parameter dependencies. +// Only to be used externally to AEC3 for testing purposes. +// TODO(webrtc:5298): Move this to a separate file. +EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config, + const FieldTrialsView& field_trials); + +// Functor for verifying the invariance of the frames being put into the render +// queue. +class Aec3RenderQueueItemVerifier { + public: + Aec3RenderQueueItemVerifier(size_t num_bands, + size_t num_channels, + size_t frame_length) + : num_bands_(num_bands), + num_channels_(num_channels), + frame_length_(frame_length) {} + + bool operator()(const std::vector>>& v) const { + if (v.size() != num_bands_) { + return false; + } + for (const auto& band : v) { + if (band.size() != num_channels_) { + return false; + } + for (const auto& channel : band) { + if (channel.size() != frame_length_) { + return false; + } + } + } + return true; + } + + private: + const size_t num_bands_; + const size_t num_channels_; + const size_t frame_length_; +}; + +// Main class for the echo canceller3. +// It does 4 things: +// -Receives 10 ms frames of band-split audio. +// -Provides the lower level echo canceller functionality with +// blocks of 64 samples of audio data. +// -Partially handles the jitter in the render and capture API +// call sequence. +// +// The class is supposed to be used in a non-concurrent manner apart from the +// AnalyzeRender call which can be called concurrently with the other methods. +class EchoCanceller3 : public EchoControl { + public: + EchoCanceller3(const Environment& env, + const EchoCanceller3Config& config, + const std::optional& multichannel_config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels); + + ~EchoCanceller3() override; + + EchoCanceller3(const EchoCanceller3&) = delete; + EchoCanceller3& operator=(const EchoCanceller3&) = delete; + + // Analyzes and stores an internal copy of the split-band domain render + // signal. + void AnalyzeRender(AudioBuffer* render) override { AnalyzeRender(*render); } + // Analyzes the full-band domain capture signal to detect signal saturation. + void AnalyzeCapture(AudioBuffer* capture) override { + AnalyzeCapture(*capture); + } + // Processes the split-band domain capture signal in order to remove any echo + // present in the signal. + void ProcessCapture(AudioBuffer* capture, bool level_change) override; + // As above, but also returns the linear filter output. + void ProcessCapture(AudioBuffer* capture, + AudioBuffer* linear_output, + bool level_change) override; + // Collect current metrics from the echo canceller. + Metrics GetMetrics() const override; + // Provides an optional external estimate of the audio buffer delay. + void SetAudioBufferDelay(int delay_ms) override; + + // Specifies whether the capture output will be used. The purpose of this is + // to allow the echo controller to deactivate some of the processing when the + // resulting output is anyway not used, for instance when the endpoint is + // muted. + void SetCaptureOutputUsage(bool capture_output_used) override; + + bool ActiveProcessing() const override; + + // Signals whether an external detector has detected echo leakage from the + // echo canceller. + // Note that in the case echo leakage has been flagged, it should be unflagged + // once it is no longer occurring. + void UpdateEchoLeakageStatus(bool leakage_detected) { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + block_processor_->UpdateEchoLeakageStatus(leakage_detected); + } + + private: + friend class EchoCanceller3Tester; + FRIEND_TEST_ALL_PREFIXES(EchoCanceller3, DetectionOfProperStereo); + FRIEND_TEST_ALL_PREFIXES(EchoCanceller3, + DetectionOfProperStereoUsingThreshold); + FRIEND_TEST_ALL_PREFIXES(EchoCanceller3, + DetectionOfProperStereoUsingHysteresis); + FRIEND_TEST_ALL_PREFIXES(EchoCanceller3, + StereoContentDetectionForMonoSignals); + + class RenderWriter; + + // (Re-)Initializes the selected subset of the EchoCanceller3 fields, at + // creation as well as during reconfiguration. + void Initialize(); + + // Only for testing. Replaces the internal block processor. + void SetBlockProcessorForTesting( + std::unique_ptr block_processor); + + // Only for testing. Returns whether stereo processing is active. + bool StereoRenderProcessingActiveForTesting() const { + return multichannel_content_detector_.IsProperMultiChannelContentDetected(); + } + + // Only for testing. + const EchoCanceller3Config& GetActiveConfigForTesting() const { + return config_selector_.active_config(); + } + + // Empties the render SwapQueue. + void EmptyRenderQueue(); + + // Analyzes and stores an internal copy of the split-band domain render + // signal. + void AnalyzeRender(const AudioBuffer& render); + // Analyzes the full-band domain capture signal to detect signal saturation. + void AnalyzeCapture(const AudioBuffer& capture); + + const Environment env_; + RaceChecker capture_race_checker_; + RaceChecker render_race_checker_; + + // State that is accessed by the AnalyzeRender call. + std::unique_ptr render_writer_ + RTC_GUARDED_BY(render_race_checker_); + + // State that may be accessed by the capture thread. + static std::atomic instance_count_; + std::unique_ptr data_dumper_; + const EchoCanceller3Config config_; + const int sample_rate_hz_; + const int num_bands_; + const size_t num_render_input_channels_; + size_t num_render_channels_to_aec_; + const size_t num_capture_channels_; + ConfigSelector config_selector_; + MultiChannelContentDetector multichannel_content_detector_; + std::unique_ptr linear_output_framer_ + RTC_GUARDED_BY(capture_race_checker_); + BlockFramer output_framer_ RTC_GUARDED_BY(capture_race_checker_); + FrameBlocker capture_blocker_ RTC_GUARDED_BY(capture_race_checker_); + std::unique_ptr render_blocker_ + RTC_GUARDED_BY(capture_race_checker_); + SwapQueue>>, + Aec3RenderQueueItemVerifier> + render_transfer_queue_; + std::unique_ptr block_processor_ + RTC_GUARDED_BY(capture_race_checker_); + std::vector>> render_queue_output_frame_ + RTC_GUARDED_BY(capture_race_checker_); + bool saturated_microphone_signal_ RTC_GUARDED_BY(capture_race_checker_) = + false; + Block render_block_ RTC_GUARDED_BY(capture_race_checker_); + std::unique_ptr linear_output_block_ + RTC_GUARDED_BY(capture_race_checker_); + Block capture_block_ RTC_GUARDED_BY(capture_race_checker_); + std::vector>> render_sub_frame_view_ + RTC_GUARDED_BY(capture_race_checker_); + std::vector>> linear_output_sub_frame_view_ + RTC_GUARDED_BY(capture_race_checker_); + std::vector>> capture_sub_frame_view_ + RTC_GUARDED_BY(capture_race_checker_); + std::unique_ptr block_delay_buffer_ + RTC_GUARDED_BY(capture_race_checker_); + ApiCallJitterMetrics api_call_metrics_ RTC_GUARDED_BY(capture_race_checker_); +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_CANCELLER3_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.cc new file mode 100644 index 00000000..32eb5d8f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.cc @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/aec3/echo_path_delay_estimator.h" + +#include + +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +EchoPathDelayEstimator::EchoPathDelayEstimator( + ApmDataDumper* data_dumper, + const EchoCanceller3Config& config, + size_t num_capture_channels) + : data_dumper_(data_dumper), + down_sampling_factor_(config.delay.down_sampling_factor), + sub_block_size_(down_sampling_factor_ != 0 + ? kBlockSize / down_sampling_factor_ + : kBlockSize), + capture_mixer_(num_capture_channels, + config.delay.capture_alignment_mixing), + capture_decimator_(down_sampling_factor_), + matched_filter_( + data_dumper_, + DetectOptimization(), + sub_block_size_, + kMatchedFilterWindowSizeSubBlocks, + config.delay.num_filters, + kMatchedFilterAlignmentShiftSizeSubBlocks, + config.delay.down_sampling_factor == 8 + ? config.render_levels.poor_excitation_render_limit_ds8 + : config.render_levels.poor_excitation_render_limit, + config.delay.delay_estimate_smoothing, + config.delay.delay_estimate_smoothing_delay_found, + config.delay.delay_candidate_detection_threshold, + config.delay.detect_pre_echo), + matched_filter_lag_aggregator_(data_dumper_, + matched_filter_.GetMaxFilterLag(), + config.delay) { + RTC_DCHECK(data_dumper); + RTC_DCHECK(down_sampling_factor_ > 0); +} + +EchoPathDelayEstimator::~EchoPathDelayEstimator() = default; + +void EchoPathDelayEstimator::Reset(bool reset_delay_confidence) { + Reset(true, reset_delay_confidence); +} + +std::optional EchoPathDelayEstimator::EstimateDelay( + const DownsampledRenderBuffer& render_buffer, + const Block& capture) { + std::array downsampled_capture_data; + ArrayView downsampled_capture(downsampled_capture_data.data(), + sub_block_size_); + + std::array downmixed_capture; + capture_mixer_.ProduceOutput(capture, downmixed_capture); + capture_decimator_.Decimate(downmixed_capture, downsampled_capture); + data_dumper_->DumpWav("aec3_capture_decimator_output", + downsampled_capture.size(), downsampled_capture.data(), + 16000 / down_sampling_factor_, 1); + matched_filter_.Update(render_buffer, downsampled_capture, + matched_filter_lag_aggregator_.ReliableDelayFound()); + + std::optional aggregated_matched_filter_lag = + matched_filter_lag_aggregator_.Aggregate( + matched_filter_.GetBestLagEstimate()); + + // Run clockdrift detection. + if (aggregated_matched_filter_lag && + (*aggregated_matched_filter_lag).quality == + DelayEstimate::Quality::kRefined) + clockdrift_detector_.Update( + matched_filter_lag_aggregator_.GetDelayAtHighestPeak()); + + // TODO(peah): Move this logging outside of this class once EchoCanceller3 + // development is done. + data_dumper_->DumpRaw( + "aec3_echo_path_delay_estimator_delay", + aggregated_matched_filter_lag + ? static_cast(aggregated_matched_filter_lag->delay * + down_sampling_factor_) + : -1); + + // Return the detected delay in samples as the aggregated matched filter lag + // compensated by the down sampling factor for the signal being correlated. + if (aggregated_matched_filter_lag) { + aggregated_matched_filter_lag->delay *= down_sampling_factor_; + } + + if (old_aggregated_lag_ && aggregated_matched_filter_lag && + old_aggregated_lag_->delay == aggregated_matched_filter_lag->delay) { + ++consistent_estimate_counter_; + } else { + consistent_estimate_counter_ = 0; + } + old_aggregated_lag_ = aggregated_matched_filter_lag; + constexpr size_t kNumBlocksPerSecondBy2 = kNumBlocksPerSecond / 2; + if (consistent_estimate_counter_ > kNumBlocksPerSecondBy2) { + Reset(false, false); + } + + return aggregated_matched_filter_lag; +} + +void EchoPathDelayEstimator::Reset(bool reset_lag_aggregator, + bool reset_delay_confidence) { + if (reset_lag_aggregator) { + matched_filter_lag_aggregator_.Reset(reset_delay_confidence); + } + matched_filter_.Reset(/*full_reset=*/reset_lag_aggregator); + old_aggregated_lag_ = std::nullopt; + consistent_estimate_counter_ = 0; +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.h new file mode 100644 index 00000000..bd5c3b06 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_DELAY_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_DELAY_ESTIMATOR_H_ + +#include + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/alignment_mixer.h" +#include "modules/audio_processing/aec3/block.h" +#include "modules/audio_processing/aec3/clockdrift_detector.h" +#include "modules/audio_processing/aec3/decimator.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/matched_filter.h" +#include "modules/audio_processing/aec3/matched_filter_lag_aggregator.h" + +namespace webrtc { + +class ApmDataDumper; +struct DownsampledRenderBuffer; +struct EchoCanceller3Config; + +// Estimates the delay of the echo path. +class EchoPathDelayEstimator { + public: + EchoPathDelayEstimator(ApmDataDumper* data_dumper, + const EchoCanceller3Config& config, + size_t num_capture_channels); + ~EchoPathDelayEstimator(); + + EchoPathDelayEstimator(const EchoPathDelayEstimator&) = delete; + EchoPathDelayEstimator& operator=(const EchoPathDelayEstimator&) = delete; + + // Resets the estimation. If the delay confidence is reset, the reset behavior + // is as if the call is restarted. + void Reset(bool reset_delay_confidence); + + // Produce a delay estimate if such is avaliable. + std::optional EstimateDelay( + const DownsampledRenderBuffer& render_buffer, + const Block& capture); + + // Log delay estimator properties. + void LogDelayEstimationProperties(int sample_rate_hz, size_t shift) const { + matched_filter_.LogFilterProperties(sample_rate_hz, shift, + down_sampling_factor_); + } + + // Returns the level of detected clockdrift. + ClockdriftDetector::Level Clockdrift() const { + return clockdrift_detector_.ClockdriftLevel(); + } + + private: + ApmDataDumper* const data_dumper_; + const size_t down_sampling_factor_; + const size_t sub_block_size_; + AlignmentMixer capture_mixer_; + Decimator capture_decimator_; + MatchedFilter matched_filter_; + MatchedFilterLagAggregator matched_filter_lag_aggregator_; + std::optional old_aggregated_lag_; + size_t consistent_estimate_counter_ = 0; + ClockdriftDetector clockdrift_detector_; + + // Internal reset method with more granularity. + void Reset(bool reset_lag_aggregator, bool reset_delay_confidence); +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_DELAY_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_variability.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_variability.cc new file mode 100644 index 00000000..0ae9cff9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_variability.cc @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/echo_path_variability.h" + +namespace webrtc { + +EchoPathVariability::EchoPathVariability(bool gain_change, + DelayAdjustment delay_change, + bool clock_drift) + : gain_change(gain_change), + delay_change(delay_change), + clock_drift(clock_drift) {} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_variability.h b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_variability.h new file mode 100644 index 00000000..45b33c21 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_path_variability.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_VARIABILITY_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_VARIABILITY_H_ + +namespace webrtc { + +struct EchoPathVariability { + enum class DelayAdjustment { kNone, kBufferFlush, kNewDetectedDelay }; + + EchoPathVariability(bool gain_change, + DelayAdjustment delay_change, + bool clock_drift); + + bool AudioPathChanged() const { + return gain_change || delay_change != DelayAdjustment::kNone; + } + bool gain_change; + DelayAdjustment delay_change; + bool clock_drift; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_VARIABILITY_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover.cc new file mode 100644 index 00000000..c19da94d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover.cc @@ -0,0 +1,527 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/aec3/echo_remover.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/environment/environment.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec3_fft.h" +#include "modules/audio_processing/aec3/aec_state.h" +#include "modules/audio_processing/aec3/comfort_noise_generator.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/echo_remover_metrics.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/render_signal_analyzer.h" +#include "modules/audio_processing/aec3/residual_echo_estimator.h" +#include "modules/audio_processing/aec3/subtractor.h" +#include "modules/audio_processing/aec3/subtractor_output.h" +#include "modules/audio_processing/aec3/suppression_filter.h" +#include "modules/audio_processing/aec3/suppression_gain.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +namespace { + +// Maximum number of channels for which the capture channel data is stored on +// the stack. If the number of channels are larger than this, they are stored +// using scratch memory that is pre-allocated on the heap. The reason for this +// partitioning is not to waste heap space for handling the more common numbers +// of channels, while at the same time not limiting the support for higher +// numbers of channels by enforcing the capture channel data to be stored on the +// stack using a fixed maximum value. +constexpr size_t kMaxNumChannelsOnStack = 2; + +// Chooses the number of channels to store on the heap when that is required due +// to the number of capture channels being larger than the pre-defined number +// of channels to store on the stack. +size_t NumChannelsOnHeap(size_t num_capture_channels) { + return num_capture_channels > kMaxNumChannelsOnStack ? num_capture_channels + : 0; +} + +void LinearEchoPower(const FftData& E, + const FftData& Y, + std::array* S2) { + for (size_t k = 0; k < E.re.size(); ++k) { + (*S2)[k] = (Y.re[k] - E.re[k]) * (Y.re[k] - E.re[k]) + + (Y.im[k] - E.im[k]) * (Y.im[k] - E.im[k]); + } +} + +// Fades between two input signals using a fix-sized transition. +void SignalTransition(ArrayView from, + ArrayView to, + ArrayView out) { + if (from == to) { + RTC_DCHECK_EQ(to.size(), out.size()); + std::copy(to.begin(), to.end(), out.begin()); + } else { + constexpr size_t kTransitionSize = 30; + constexpr float kOneByTransitionSizePlusOne = 1.f / (kTransitionSize + 1); + + RTC_DCHECK_EQ(from.size(), to.size()); + RTC_DCHECK_EQ(from.size(), out.size()); + RTC_DCHECK_LE(kTransitionSize, out.size()); + + for (size_t k = 0; k < kTransitionSize; ++k) { + float a = (k + 1) * kOneByTransitionSizePlusOne; + out[k] = a * to[k] + (1.f - a) * from[k]; + } + + std::copy(to.begin() + kTransitionSize, to.end(), + out.begin() + kTransitionSize); + } +} + +// Computes a windowed (square root Hanning) padded FFT and updates the related +// memory. +void WindowedPaddedFft(const Aec3Fft& fft, + ArrayView v, + ArrayView v_old, + FftData* V) { + fft.PaddedFft(v, v_old, Aec3Fft::Window::kSqrtHanning, V); + std::copy(v.begin(), v.end(), v_old.begin()); +} + +// Class for removing the echo from the capture signal. +class EchoRemoverImpl final : public EchoRemover { + public: + EchoRemoverImpl(const Environment& env, + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels); + ~EchoRemoverImpl() override; + EchoRemoverImpl(const EchoRemoverImpl&) = delete; + EchoRemoverImpl& operator=(const EchoRemoverImpl&) = delete; + + void GetMetrics(EchoControl::Metrics* metrics) const override; + + // Removes the echo from a block of samples from the capture signal. The + // supplied render signal is assumed to be pre-aligned with the capture + // signal. + void ProcessCapture(EchoPathVariability echo_path_variability, + bool capture_signal_saturation, + const std::optional& external_delay, + RenderBuffer* render_buffer, + Block* linear_output, + Block* capture) override; + + // Updates the status on whether echo leakage is detected in the output of the + // echo remover. + void UpdateEchoLeakageStatus(bool leakage_detected) override { + echo_leakage_detected_ = leakage_detected; + } + + void SetCaptureOutputUsage(bool capture_output_used) override { + capture_output_used_ = capture_output_used; + } + + private: + // Selects which of the coarse and refined linear filter outputs that is most + // appropriate to pass to the suppressor and forms the linear filter output by + // smoothly transition between those. + void FormLinearFilterOutput(const SubtractorOutput& subtractor_output, + ArrayView output); + + static std::atomic instance_count_; + const EchoCanceller3Config config_; + const Aec3Fft fft_; + std::unique_ptr data_dumper_; + const Aec3Optimization optimization_; + const int sample_rate_hz_; + const size_t num_render_channels_; + const size_t num_capture_channels_; + const bool use_coarse_filter_output_; + Subtractor subtractor_; + SuppressionGain suppression_gain_; + ComfortNoiseGenerator cng_; + SuppressionFilter suppression_filter_; + RenderSignalAnalyzer render_signal_analyzer_; + ResidualEchoEstimator residual_echo_estimator_; + bool echo_leakage_detected_ = false; + bool capture_output_used_ = true; + AecState aec_state_; + EchoRemoverMetrics metrics_; + std::vector> e_old_; + std::vector> y_old_; + size_t block_counter_ = 0; + int gain_change_hangover_ = 0; + bool refined_filter_output_last_selected_ = true; + + std::vector> e_heap_; + std::vector> Y2_heap_; + std::vector> E2_heap_; + std::vector> R2_heap_; + std::vector> R2_unbounded_heap_; + std::vector> S2_linear_heap_; + std::vector Y_heap_; + std::vector E_heap_; + std::vector comfort_noise_heap_; + std::vector high_band_comfort_noise_heap_; + std::vector subtractor_output_heap_; +}; + +std::atomic EchoRemoverImpl::instance_count_(0); + +EchoRemoverImpl::EchoRemoverImpl(const Environment& env, + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels) + : config_(config), + fft_(), + data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), + optimization_(DetectOptimization()), + sample_rate_hz_(sample_rate_hz), + num_render_channels_(num_render_channels), + num_capture_channels_(num_capture_channels), + use_coarse_filter_output_( + config_.filter.enable_coarse_filter_output_usage), + subtractor_(env, + config, + num_render_channels_, + num_capture_channels_, + data_dumper_.get(), + optimization_), + suppression_gain_(config_, + optimization_, + sample_rate_hz, + num_capture_channels), + cng_(config_, optimization_, num_capture_channels_), + suppression_filter_(optimization_, + sample_rate_hz_, + num_capture_channels_), + render_signal_analyzer_(config_), + residual_echo_estimator_(env, config_, num_render_channels), + aec_state_(env, config_, num_capture_channels_), + e_old_(num_capture_channels_, {0.f}), + y_old_(num_capture_channels_, {0.f}), + e_heap_(NumChannelsOnHeap(num_capture_channels_), {0.f}), + Y2_heap_(NumChannelsOnHeap(num_capture_channels_)), + E2_heap_(NumChannelsOnHeap(num_capture_channels_)), + R2_heap_(NumChannelsOnHeap(num_capture_channels_)), + R2_unbounded_heap_(NumChannelsOnHeap(num_capture_channels_)), + S2_linear_heap_(NumChannelsOnHeap(num_capture_channels_)), + Y_heap_(NumChannelsOnHeap(num_capture_channels_)), + E_heap_(NumChannelsOnHeap(num_capture_channels_)), + comfort_noise_heap_(NumChannelsOnHeap(num_capture_channels_)), + high_band_comfort_noise_heap_(NumChannelsOnHeap(num_capture_channels_)), + subtractor_output_heap_(NumChannelsOnHeap(num_capture_channels_)) { + RTC_DCHECK(ValidFullBandRate(sample_rate_hz)); +} + +EchoRemoverImpl::~EchoRemoverImpl() = default; + +void EchoRemoverImpl::GetMetrics(EchoControl::Metrics* metrics) const { + // Echo return loss (ERL) is inverted to go from gain to attenuation. + metrics->echo_return_loss = -10.0 * std::log10(aec_state_.ErlTimeDomain()); + metrics->echo_return_loss_enhancement = + Log2TodB(aec_state_.FullBandErleLog2()); +} + +void EchoRemoverImpl::ProcessCapture( + EchoPathVariability echo_path_variability, + bool capture_signal_saturation, + const std::optional& external_delay, + RenderBuffer* render_buffer, + Block* linear_output, + Block* capture) { + ++block_counter_; + const Block& x = render_buffer->GetBlock(0); + Block* y = capture; + RTC_DCHECK(render_buffer); + RTC_DCHECK(y); + RTC_DCHECK_EQ(x.NumBands(), NumBandsForRate(sample_rate_hz_)); + RTC_DCHECK_EQ(y->NumBands(), NumBandsForRate(sample_rate_hz_)); + RTC_DCHECK_EQ(x.NumChannels(), num_render_channels_); + RTC_DCHECK_EQ(y->NumChannels(), num_capture_channels_); + + // Stack allocated data to use when the number of channels is low. + std::array, kMaxNumChannelsOnStack> e_stack; + std::array, kMaxNumChannelsOnStack> + Y2_stack; + std::array, kMaxNumChannelsOnStack> + E2_stack; + std::array, kMaxNumChannelsOnStack> + R2_stack; + std::array, kMaxNumChannelsOnStack> + R2_unbounded_stack; + std::array, kMaxNumChannelsOnStack> + S2_linear_stack; + std::array Y_stack; + std::array E_stack; + std::array comfort_noise_stack; + std::array high_band_comfort_noise_stack; + std::array subtractor_output_stack; + + ArrayView> e(e_stack.data(), + num_capture_channels_); + ArrayView> Y2(Y2_stack.data(), + num_capture_channels_); + ArrayView> E2(E2_stack.data(), + num_capture_channels_); + ArrayView> R2(R2_stack.data(), + num_capture_channels_); + ArrayView> R2_unbounded( + R2_unbounded_stack.data(), num_capture_channels_); + ArrayView> S2_linear( + S2_linear_stack.data(), num_capture_channels_); + ArrayView Y(Y_stack.data(), num_capture_channels_); + ArrayView E(E_stack.data(), num_capture_channels_); + ArrayView comfort_noise(comfort_noise_stack.data(), + num_capture_channels_); + ArrayView high_band_comfort_noise( + high_band_comfort_noise_stack.data(), num_capture_channels_); + ArrayView subtractor_output(subtractor_output_stack.data(), + num_capture_channels_); + if (NumChannelsOnHeap(num_capture_channels_) > 0) { + // If the stack-allocated space is too small, use the heap for storing the + // microphone data. + e = ArrayView>(e_heap_.data(), + num_capture_channels_); + Y2 = ArrayView>( + Y2_heap_.data(), num_capture_channels_); + E2 = ArrayView>( + E2_heap_.data(), num_capture_channels_); + R2 = ArrayView>( + R2_heap_.data(), num_capture_channels_); + R2_unbounded = ArrayView>( + R2_unbounded_heap_.data(), num_capture_channels_); + S2_linear = ArrayView>( + S2_linear_heap_.data(), num_capture_channels_); + Y = ArrayView(Y_heap_.data(), num_capture_channels_); + E = ArrayView(E_heap_.data(), num_capture_channels_); + comfort_noise = + ArrayView(comfort_noise_heap_.data(), num_capture_channels_); + high_band_comfort_noise = ArrayView( + high_band_comfort_noise_heap_.data(), num_capture_channels_); + subtractor_output = ArrayView( + subtractor_output_heap_.data(), num_capture_channels_); + } + + data_dumper_->DumpWav("aec3_echo_remover_capture_input", + y->View(/*band=*/0, /*channel=*/0), 16000, 1); + data_dumper_->DumpWav("aec3_echo_remover_render_input", + x.View(/*band=*/0, /*channel=*/0), 16000, 1); + data_dumper_->DumpRaw("aec3_echo_remover_capture_input", + y->View(/*band=*/0, /*channel=*/0)); + data_dumper_->DumpRaw("aec3_echo_remover_render_input", + x.View(/*band=*/0, /*channel=*/0)); + + aec_state_.UpdateCaptureSaturation(capture_signal_saturation); + + if (echo_path_variability.AudioPathChanged()) { + // Ensure that the gain change is only acted on once per frame. + if (echo_path_variability.gain_change) { + if (gain_change_hangover_ == 0) { + constexpr int kMaxBlocksPerFrame = 3; + gain_change_hangover_ = kMaxBlocksPerFrame; + LoggingSeverity log_level = config_.delay.log_warning_on_delay_changes + ? LS_WARNING + : LS_VERBOSE; + RTC_LOG_V(log_level) + << "Gain change detected at block " << block_counter_; + } else { + echo_path_variability.gain_change = false; + } + } + + subtractor_.HandleEchoPathChange(echo_path_variability); + aec_state_.HandleEchoPathChange(echo_path_variability); + + if (echo_path_variability.delay_change != + EchoPathVariability::DelayAdjustment::kNone) { + suppression_gain_.SetInitialState(true); + } + } + if (gain_change_hangover_ > 0) { + --gain_change_hangover_; + } + + // Analyze the render signal. + render_signal_analyzer_.Update(*render_buffer, + aec_state_.MinDirectPathFilterDelay()); + + // State transition. + if (aec_state_.TransitionTriggered()) { + subtractor_.ExitInitialState(); + suppression_gain_.SetInitialState(false); + } + + // Perform linear echo cancellation. + subtractor_.Process(*render_buffer, *y, render_signal_analyzer_, aec_state_, + subtractor_output); + + // Compute spectra. + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + FormLinearFilterOutput(subtractor_output[ch], e[ch]); + WindowedPaddedFft(fft_, y->View(/*band=*/0, ch), y_old_[ch], &Y[ch]); + WindowedPaddedFft(fft_, e[ch], e_old_[ch], &E[ch]); + LinearEchoPower(E[ch], Y[ch], &S2_linear[ch]); + Y[ch].Spectrum(optimization_, Y2[ch]); + E[ch].Spectrum(optimization_, E2[ch]); + } + + // Optionally return the linear filter output. + if (linear_output) { + RTC_DCHECK_GE(1, linear_output->NumBands()); + RTC_DCHECK_EQ(num_capture_channels_, linear_output->NumChannels()); + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + std::copy(e[ch].begin(), e[ch].end(), + linear_output->begin(/*band=*/0, ch)); + } + } + + // Update the AEC state information. + aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponses(), + subtractor_.FilterImpulseResponses(), *render_buffer, E2, + Y2, subtractor_output); + + // Choose the linear output. + const auto& Y_fft = aec_state_.UseLinearFilterOutput() ? E : Y; + + data_dumper_->DumpWav("aec3_output_linear", + y->View(/*band=*/0, /*channel=*/0), 16000, 1); + data_dumper_->DumpWav("aec3_output_linear2", kBlockSize, &e[0][0], 16000, 1); + + // Estimate the comfort noise. + cng_.Compute(aec_state_.SaturatedCapture(), Y2, comfort_noise, + high_band_comfort_noise); + + // Only do the below processing if the output of the audio processing module + // is used. + std::array G; + if (capture_output_used_) { + // Estimate the residual echo power. + residual_echo_estimator_.Estimate(aec_state_, *render_buffer, S2_linear, Y2, + suppression_gain_.IsDominantNearend(), R2, + R2_unbounded); + + // Suppressor nearend estimate. + if (aec_state_.UsableLinearEstimate()) { + // E2 is bound by Y2. + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + std::transform(E2[ch].begin(), E2[ch].end(), Y2[ch].begin(), + E2[ch].begin(), + [](float a, float b) { return std::min(a, b); }); + } + } + const auto& nearend_spectrum = aec_state_.UsableLinearEstimate() ? E2 : Y2; + + // Suppressor echo estimate. + const auto& echo_spectrum = + aec_state_.UsableLinearEstimate() ? S2_linear : R2; + + // Determine if the suppressor should assume clock drift. + const bool clock_drift = config_.echo_removal_control.has_clock_drift || + echo_path_variability.clock_drift; + + // Compute preferred gains. + float high_bands_gain; + suppression_gain_.GetGain(nearend_spectrum, echo_spectrum, R2, R2_unbounded, + cng_.NoiseSpectrum(), render_signal_analyzer_, + aec_state_, x, clock_drift, &high_bands_gain, &G); + + suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G, + high_bands_gain, Y_fft, y); + + } else { + G.fill(0.f); + } + + // Update the metrics. + metrics_.Update(aec_state_, cng_.NoiseSpectrum()[0], G); + + // Debug outputs for the purpose of development and analysis. + data_dumper_->DumpWav("aec3_echo_estimate", kBlockSize, + &subtractor_output[0].s_refined[0], 16000, 1); + data_dumper_->DumpRaw("aec3_output", y->View(/*band=*/0, /*channel=*/0)); + data_dumper_->DumpRaw("aec3_narrow_render", + render_signal_analyzer_.NarrowPeakBand() ? 1 : 0); + data_dumper_->DumpRaw("aec3_N2", cng_.NoiseSpectrum()[0]); + data_dumper_->DumpRaw("aec3_suppressor_gain", G); + data_dumper_->DumpWav("aec3_output", y->View(/*band=*/0, /*channel=*/0), + 16000, 1); + data_dumper_->DumpRaw("aec3_using_subtractor_output[0]", + aec_state_.UseLinearFilterOutput() ? 1 : 0); + data_dumper_->DumpRaw("aec3_E2", E2[0]); + data_dumper_->DumpRaw("aec3_S2_linear", S2_linear[0]); + data_dumper_->DumpRaw("aec3_Y2", Y2[0]); + data_dumper_->DumpRaw( + "aec3_X2", render_buffer->Spectrum( + aec_state_.MinDirectPathFilterDelay())[/*channel=*/0]); + data_dumper_->DumpRaw("aec3_R2", R2[0]); + data_dumper_->DumpRaw("aec3_filter_delay", + aec_state_.MinDirectPathFilterDelay()); + data_dumper_->DumpRaw("aec3_capture_saturation", + aec_state_.SaturatedCapture() ? 1 : 0); +} + +void EchoRemoverImpl::FormLinearFilterOutput( + const SubtractorOutput& subtractor_output, + ArrayView output) { + RTC_DCHECK_EQ(subtractor_output.e_refined.size(), output.size()); + RTC_DCHECK_EQ(subtractor_output.e_coarse.size(), output.size()); + bool use_refined_output = true; + if (use_coarse_filter_output_) { + // As the output of the refined adaptive filter generally should be better + // than the coarse filter output, add a margin and threshold for when + // choosing the coarse filter output. + if (subtractor_output.e2_coarse < 0.9f * subtractor_output.e2_refined && + subtractor_output.y2 > 30.f * 30.f * kBlockSize && + (subtractor_output.s2_refined > 60.f * 60.f * kBlockSize || + subtractor_output.s2_coarse > 60.f * 60.f * kBlockSize)) { + use_refined_output = false; + } else { + // If the refined filter is diverged, choose the filter output that has + // the lowest power. + if (subtractor_output.e2_coarse < subtractor_output.e2_refined && + subtractor_output.y2 < subtractor_output.e2_refined) { + use_refined_output = false; + } + } + } + + SignalTransition(refined_filter_output_last_selected_ + ? subtractor_output.e_refined + : subtractor_output.e_coarse, + use_refined_output ? subtractor_output.e_refined + : subtractor_output.e_coarse, + output); + refined_filter_output_last_selected_ = use_refined_output; +} + +} // namespace + +std::unique_ptr EchoRemover::Create( + const Environment& env, + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels) { + return std::make_unique( + env, config, sample_rate_hz, num_render_channels, num_capture_channels); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover.h b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover.h new file mode 100644 index 00000000..18a7bc3c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_H_ + +#include +#include +#include + +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "api/environment/environment.h" +#include "modules/audio_processing/aec3/block.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/render_buffer.h" + +namespace webrtc { + +// Class for removing the echo from the capture signal. +class EchoRemover { + public: + static std::unique_ptr Create(const Environment& env, + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels); + virtual ~EchoRemover() = default; + + // Get current metrics. + virtual void GetMetrics(EchoControl::Metrics* metrics) const = 0; + + // Removes the echo from a block of samples from the capture signal. The + // supplied render signal is assumed to be pre-aligned with the capture + // signal. + virtual void ProcessCapture( + EchoPathVariability echo_path_variability, + bool capture_signal_saturation, + const std::optional& external_delay, + RenderBuffer* render_buffer, + Block* linear_output, + Block* capture) = 0; + + // Updates the status on whether echo leakage is detected in the output of the + // echo remover. + virtual void UpdateEchoLeakageStatus(bool leakage_detected) = 0; + + // Specifies whether the capture output will be used. The purpose of this is + // to allow the echo remover to deactivate some of the processing when the + // resulting output is anyway not used, for instance when the endpoint is + // muted. + virtual void SetCaptureOutputUsage(bool capture_output_used) = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover_metrics.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover_metrics.cc new file mode 100644 index 00000000..aa13da9a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover_metrics.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/echo_remover_metrics.h" + +#include +#include + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +EchoRemoverMetrics::DbMetric::DbMetric() : DbMetric(0.f, 0.f, 0.f) {} +EchoRemoverMetrics::DbMetric::DbMetric(float sum_value, + float floor_value, + float ceil_value) + : sum_value(sum_value), floor_value(floor_value), ceil_value(ceil_value) {} + +void EchoRemoverMetrics::DbMetric::Update(float value) { + sum_value += value; + floor_value = std::min(floor_value, value); + ceil_value = std::max(ceil_value, value); +} + +void EchoRemoverMetrics::DbMetric::UpdateInstant(float value) { + sum_value = value; + floor_value = std::min(floor_value, value); + ceil_value = std::max(ceil_value, value); +} + +EchoRemoverMetrics::EchoRemoverMetrics() { + ResetMetrics(); +} + +void EchoRemoverMetrics::ResetMetrics() { + erl_time_domain_ = DbMetric(0.f, 10000.f, 0.000f); + erle_time_domain_ = DbMetric(0.f, 0.f, 1000.f); + saturated_capture_ = false; +} + +void EchoRemoverMetrics::Update( + const AecState& aec_state, + const std::array& /* comfort_noise_spectrum */, + const std::array& /* suppressor_gain */) { + metrics_reported_ = false; + if (++block_counter_ <= kMetricsCollectionBlocks) { + erl_time_domain_.UpdateInstant(aec_state.ErlTimeDomain()); + erle_time_domain_.UpdateInstant(aec_state.FullBandErleLog2()); + saturated_capture_ = saturated_capture_ || aec_state.SaturatedCapture(); + } else { + // Report the metrics over several frames in order to lower the impact of + // the logarithms involved on the computational complexity. + switch (block_counter_) { + case kMetricsCollectionBlocks + 1: + RTC_HISTOGRAM_BOOLEAN( + "WebRTC.Audio.EchoCanceller.UsableLinearEstimate", + static_cast(aec_state.UsableLinearEstimate() ? 1 : 0)); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.EchoCanceller.FilterDelay", + aec_state.MinDirectPathFilterDelay(), 0, 30, + 31); + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.EchoCanceller.CaptureSaturation", + static_cast(saturated_capture_ ? 1 : 0)); + break; + case kMetricsCollectionBlocks + 2: + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erl.Value", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_time_domain_.sum_value), + 0, 59, 30); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erl.Max", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_time_domain_.ceil_value), + 0, 59, 30); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erl.Min", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_time_domain_.floor_value), + 0, 59, 30); + break; + case kMetricsCollectionBlocks + 3: + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erle.Value", + aec3::TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, + erle_time_domain_.sum_value), + 0, 19, 20); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erle.Max", + aec3::TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, + erle_time_domain_.ceil_value), + 0, 19, 20); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erle.Min", + aec3::TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, + erle_time_domain_.floor_value), + 0, 19, 20); + metrics_reported_ = true; + RTC_DCHECK_EQ(kMetricsReportingIntervalBlocks, block_counter_); + block_counter_ = 0; + ResetMetrics(); + break; + default: + RTC_DCHECK_NOTREACHED(); + break; + } + } +} + +namespace aec3 { + +void UpdateDbMetric(const std::array& value, + std::array* statistic) { + RTC_DCHECK(statistic); + // Truncation is intended in the band width computation. + constexpr int kNumBands = 2; + constexpr int kBandWidth = 65 / kNumBands; + constexpr float kOneByBandWidth = 1.f / kBandWidth; + RTC_DCHECK_EQ(kNumBands, statistic->size()); + RTC_DCHECK_EQ(65, value.size()); + for (size_t k = 0; k < statistic->size(); ++k) { + float average_band = + std::accumulate(value.begin() + kBandWidth * k, + value.begin() + kBandWidth * (k + 1), 0.f) * + kOneByBandWidth; + (*statistic)[k].Update(average_band); + } +} + +int TransformDbMetricForReporting(bool negate, + float min_value, + float max_value, + float offset, + float scaling, + float value) { + float new_value = 10.f * std::log10(value * scaling + 1e-10f) + offset; + if (negate) { + new_value = -new_value; + } + return static_cast(SafeClamp(new_value, min_value, max_value)); +} + +} // namespace aec3 + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover_metrics.h b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover_metrics.h new file mode 100644 index 00000000..aec8084d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/echo_remover_metrics.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_METRICS_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_METRICS_H_ + +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec_state.h" + +namespace webrtc { + +// Handles the reporting of metrics for the echo remover. +class EchoRemoverMetrics { + public: + struct DbMetric { + DbMetric(); + DbMetric(float sum_value, float floor_value, float ceil_value); + void Update(float value); + void UpdateInstant(float value); + float sum_value; + float floor_value; + float ceil_value; + }; + + EchoRemoverMetrics(); + + EchoRemoverMetrics(const EchoRemoverMetrics&) = delete; + EchoRemoverMetrics& operator=(const EchoRemoverMetrics&) = delete; + + // Updates the metric with new data. + void Update( + const AecState& aec_state, + const std::array& comfort_noise_spectrum, + const std::array& suppressor_gain); + + // Returns true if the metrics have just been reported, otherwise false. + bool MetricsReported() { return metrics_reported_; } + + private: + // Resets the metrics. + void ResetMetrics(); + + int block_counter_ = 0; + DbMetric erl_time_domain_; + DbMetric erle_time_domain_; + bool saturated_capture_ = false; + bool metrics_reported_ = false; +}; + +namespace aec3 { + +// Updates a banded metric of type DbMetric with the values in the supplied +// array. +void UpdateDbMetric(const std::array& value, + std::array* statistic); + +// Transforms a DbMetric from the linear domain into the logarithmic domain. +int TransformDbMetricForReporting(bool negate, + float min_value, + float max_value, + float offset, + float scaling, + float value); + +} // namespace aec3 + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_METRICS_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/erl_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/erl_estimator.cc new file mode 100644 index 00000000..d3eeafc4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/erl_estimator.cc @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/erl_estimator.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +constexpr float kMinErl = 0.01f; +constexpr float kMaxErl = 1000.f; + +} // namespace + +ErlEstimator::ErlEstimator(size_t startup_phase_length_blocks_) + : startup_phase_length_blocks__(startup_phase_length_blocks_) { + erl_.fill(kMaxErl); + hold_counters_.fill(0); + erl_time_domain_ = kMaxErl; + hold_counter_time_domain_ = 0; +} + +ErlEstimator::~ErlEstimator() = default; + +void ErlEstimator::Reset() { + blocks_since_reset_ = 0; +} + +void ErlEstimator::Update( + const std::vector& converged_filters, + ArrayView> render_spectra, + ArrayView> capture_spectra) { + const size_t num_capture_channels = converged_filters.size(); + RTC_DCHECK_EQ(capture_spectra.size(), num_capture_channels); + + // Corresponds to WGN of power -46 dBFS. + constexpr float kX2Min = 44015068.0f; + + const auto first_converged_iter = + std::find(converged_filters.begin(), converged_filters.end(), true); + const bool any_filter_converged = + first_converged_iter != converged_filters.end(); + + if (++blocks_since_reset_ < startup_phase_length_blocks__ || + !any_filter_converged) { + return; + } + + // Use the maximum spectrum across capture and the maximum across render. + std::array max_capture_spectrum_data; + std::array max_capture_spectrum = + capture_spectra[/*channel=*/0]; + if (num_capture_channels > 1) { + // Initialize using the first channel with a converged filter. + const size_t first_converged = + std::distance(converged_filters.begin(), first_converged_iter); + RTC_DCHECK_GE(first_converged, 0); + RTC_DCHECK_LT(first_converged, num_capture_channels); + max_capture_spectrum_data = capture_spectra[first_converged]; + + for (size_t ch = first_converged + 1; ch < num_capture_channels; ++ch) { + if (!converged_filters[ch]) { + continue; + } + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + max_capture_spectrum_data[k] = + std::max(max_capture_spectrum_data[k], capture_spectra[ch][k]); + } + } + max_capture_spectrum = max_capture_spectrum_data; + } + + const size_t num_render_channels = render_spectra.size(); + std::array max_render_spectrum_data; + ArrayView max_render_spectrum = + render_spectra[/*channel=*/0]; + if (num_render_channels > 1) { + std::copy(render_spectra[0].begin(), render_spectra[0].end(), + max_render_spectrum_data.begin()); + for (size_t ch = 1; ch < num_render_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + max_render_spectrum_data[k] = + std::max(max_render_spectrum_data[k], render_spectra[ch][k]); + } + } + max_render_spectrum = max_render_spectrum_data; + } + + const auto& X2 = max_render_spectrum; + const auto& Y2 = max_capture_spectrum; + + // Update the estimates in a maximum statistics manner. + for (size_t k = 1; k < kFftLengthBy2; ++k) { + if (X2[k] > kX2Min) { + const float new_erl = Y2[k] / X2[k]; + if (new_erl < erl_[k]) { + hold_counters_[k - 1] = 1000; + erl_[k] += 0.1f * (new_erl - erl_[k]); + erl_[k] = std::max(erl_[k], kMinErl); + } + } + } + + std::for_each(hold_counters_.begin(), hold_counters_.end(), + [](int& a) { --a; }); + std::transform(hold_counters_.begin(), hold_counters_.end(), erl_.begin() + 1, + erl_.begin() + 1, [](int a, float b) { + return a > 0 ? b : std::min(kMaxErl, 2.f * b); + }); + + erl_[0] = erl_[1]; + erl_[kFftLengthBy2] = erl_[kFftLengthBy2 - 1]; + + // Compute ERL over all frequency bins. + const float X2_sum = std::accumulate(X2.begin(), X2.end(), 0.0f); + + if (X2_sum > kX2Min * X2.size()) { + const float Y2_sum = std::accumulate(Y2.begin(), Y2.end(), 0.0f); + const float new_erl = Y2_sum / X2_sum; + if (new_erl < erl_time_domain_) { + hold_counter_time_domain_ = 1000; + erl_time_domain_ += 0.1f * (new_erl - erl_time_domain_); + erl_time_domain_ = std::max(erl_time_domain_, kMinErl); + } + } + + --hold_counter_time_domain_; + erl_time_domain_ = (hold_counter_time_domain_ > 0) + ? erl_time_domain_ + : std::min(kMaxErl, 2.f * erl_time_domain_); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/erl_estimator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/erl_estimator.h new file mode 100644 index 00000000..b793ddec --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/erl_estimator.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ERL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ERL_ESTIMATOR_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// Estimates the echo return loss based on the signal spectra. +class ErlEstimator { + public: + explicit ErlEstimator(size_t startup_phase_length_blocks_); + ~ErlEstimator(); + + ErlEstimator(const ErlEstimator&) = delete; + ErlEstimator& operator=(const ErlEstimator&) = delete; + + // Resets the ERL estimation. + void Reset(); + + // Updates the ERL estimate. + void Update( + const std::vector& converged_filters, + ArrayView> render_spectra, + ArrayView> capture_spectra); + + // Returns the most recent ERL estimate. + const std::array& Erl() const { return erl_; } + float ErlTimeDomain() const { return erl_time_domain_; } + + private: + const size_t startup_phase_length_blocks__; + std::array erl_; + std::array hold_counters_; + float erl_time_domain_; + int hold_counter_time_domain_; + size_t blocks_since_reset_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ERL_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/erle_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/erle_estimator.cc new file mode 100644 index 00000000..f1edfe60 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/erle_estimator.cc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/erle_estimator.h" + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +ErleEstimator::ErleEstimator(const Environment& env, + size_t startup_phase_length_blocks, + const EchoCanceller3Config& config, + size_t num_capture_channels) + : startup_phase_length_blocks_(startup_phase_length_blocks), + fullband_erle_estimator_(config.erle, num_capture_channels), + subband_erle_estimator_(env, config, num_capture_channels) { + if (config.erle.num_sections > 1) { + signal_dependent_erle_estimator_ = + std::make_unique(config, + num_capture_channels); + } + Reset(true); +} + +ErleEstimator::~ErleEstimator() = default; + +void ErleEstimator::Reset(bool delay_change) { + fullband_erle_estimator_.Reset(); + subband_erle_estimator_.Reset(); + if (signal_dependent_erle_estimator_) { + signal_dependent_erle_estimator_->Reset(); + } + if (delay_change) { + blocks_since_reset_ = 0; + } +} + +void ErleEstimator::Update( + const RenderBuffer& render_buffer, + ArrayView>> + filter_frequency_responses, + ArrayView avg_render_spectrum_with_reverb, + ArrayView> capture_spectra, + ArrayView> subtractor_spectra, + const std::vector& converged_filters) { + RTC_DCHECK_EQ(subband_erle_estimator_.Erle(/*onset_compensated=*/true).size(), + capture_spectra.size()); + RTC_DCHECK_EQ(subband_erle_estimator_.Erle(/*onset_compensated=*/true).size(), + subtractor_spectra.size()); + const auto& X2_reverb = avg_render_spectrum_with_reverb; + const auto& Y2 = capture_spectra; + const auto& E2 = subtractor_spectra; + + if (++blocks_since_reset_ < startup_phase_length_blocks_) { + return; + } + + subband_erle_estimator_.Update(X2_reverb, Y2, E2, converged_filters); + + if (signal_dependent_erle_estimator_) { + signal_dependent_erle_estimator_->Update( + render_buffer, filter_frequency_responses, X2_reverb, Y2, E2, + subband_erle_estimator_.Erle(/*onset_compensated=*/false), + subband_erle_estimator_.Erle(/*onset_compensated=*/true), + converged_filters); + } + + fullband_erle_estimator_.Update(X2_reverb, Y2, E2, converged_filters); +} + +void ErleEstimator::Dump( + const std::unique_ptr& data_dumper) const { + fullband_erle_estimator_.Dump(data_dumper); + subband_erle_estimator_.Dump(data_dumper); + if (signal_dependent_erle_estimator_) { + signal_dependent_erle_estimator_->Dump(data_dumper); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/erle_estimator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/erle_estimator.h new file mode 100644 index 00000000..cb014b0e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/erle_estimator.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ERLE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ERLE_ESTIMATOR_H_ + +#include + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/fullband_erle_estimator.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/signal_dependent_erle_estimator.h" +#include "modules/audio_processing/aec3/subband_erle_estimator.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +// Estimates the echo return loss enhancement. One estimate is done per subband +// and another one is done using the aggreation of energy over all the subbands. +class ErleEstimator { + public: + ErleEstimator(const Environment& env, + size_t startup_phase_length_blocks, + const EchoCanceller3Config& config, + size_t num_capture_channels); + ~ErleEstimator(); + + // Resets the fullband ERLE estimator and the subbands ERLE estimators. + void Reset(bool delay_change); + + // Updates the ERLE estimates. + void Update( + const RenderBuffer& render_buffer, + ArrayView>> + filter_frequency_responses, + ArrayView + avg_render_spectrum_with_reverb, + ArrayView> capture_spectra, + ArrayView> subtractor_spectra, + const std::vector& converged_filters); + + // Returns the most recent subband ERLE estimates. + ArrayView> Erle( + bool onset_compensated) const { + return signal_dependent_erle_estimator_ + ? signal_dependent_erle_estimator_->Erle(onset_compensated) + : subband_erle_estimator_.Erle(onset_compensated); + } + + // Returns the non-capped subband ERLE. + ArrayView> ErleUnbounded() const { + // Unbounded ERLE is only used with the subband erle estimator where the + // ERLE is often capped at low values. When the signal dependent ERLE + // estimator is used the capped ERLE is returned. + return !signal_dependent_erle_estimator_ + ? subband_erle_estimator_.ErleUnbounded() + : signal_dependent_erle_estimator_->Erle( + /*onset_compensated=*/false); + } + + // Returns the subband ERLE that are estimated during onsets (only used for + // testing). + ArrayView> ErleDuringOnsets() + const { + return subband_erle_estimator_.ErleDuringOnsets(); + } + + // Returns the fullband ERLE estimate. + float FullbandErleLog2() const { + return fullband_erle_estimator_.FullbandErleLog2(); + } + + // Returns an estimation of the current linear filter quality based on the + // current and past fullband ERLE estimates. The returned value is a float + // vector with content between 0 and 1 where 1 indicates that, at this current + // time instant, the linear filter is reaching its maximum subtraction + // performance. + ArrayView> GetInstLinearQualityEstimates() const { + return fullband_erle_estimator_.GetInstLinearQualityEstimates(); + } + + void Dump(const std::unique_ptr& data_dumper) const; + + private: + const size_t startup_phase_length_blocks_; + FullBandErleEstimator fullband_erle_estimator_; + SubbandErleEstimator subband_erle_estimator_; + std::unique_ptr + signal_dependent_erle_estimator_; + size_t blocks_since_reset_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ERLE_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/fft_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/fft_buffer.cc new file mode 100644 index 00000000..1ce2d31d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/fft_buffer.cc @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/fft_buffer.h" + +namespace webrtc { + +FftBuffer::FftBuffer(size_t size, size_t num_channels) + : size(static_cast(size)), + buffer(size, std::vector(num_channels)) { + for (auto& block : buffer) { + for (auto& channel_fft_data : block) { + channel_fft_data.Clear(); + } + } +} + +FftBuffer::~FftBuffer() = default; + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/fft_buffer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/fft_buffer.h new file mode 100644 index 00000000..41873158 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/fft_buffer.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_FFT_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_FFT_BUFFER_H_ + +#include + +#include + +#include "modules/audio_processing/aec3/fft_data.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Struct for bundling a circular buffer of FftData objects together with the +// read and write indices. +struct FftBuffer { + FftBuffer(size_t size, size_t num_channels); + ~FftBuffer(); + + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; + } + + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index > 0 ? index - 1 : size - 1; + } + + int OffsetIndex(int index, int offset) const { + RTC_DCHECK_GE(buffer.size(), offset); + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return (size + index + offset) % size; + } + + void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } + void IncWriteIndex() { write = IncIndex(write); } + void DecWriteIndex() { write = DecIndex(write); } + void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); } + void IncReadIndex() { read = IncIndex(read); } + void DecReadIndex() { read = DecIndex(read); } + + const int size; + std::vector> buffer; + int write = 0; + int read = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_FFT_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/fft_data.h b/pkg/apm/webrtc/modules/audio_processing/aec3/fft_data.h new file mode 100644 index 00000000..d8ac43e9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/fft_data.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_FFT_DATA_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_FFT_DATA_H_ + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// Struct that holds imaginary data produced from 128 point real-valued FFTs. +struct FftData { + // Copies the data in src. + void Assign(const FftData& src) { + std::copy(src.re.begin(), src.re.end(), re.begin()); + std::copy(src.im.begin(), src.im.end(), im.begin()); + im[0] = im[kFftLengthBy2] = 0; + } + + // Clears all the imaginary. + void Clear() { + re.fill(0.f); + im.fill(0.f); + } + + // Computes the power spectrum of the data. + void SpectrumAVX2(ArrayView power_spectrum) const; + + // Computes the power spectrum of the data. + void Spectrum(Aec3Optimization optimization, + ArrayView power_spectrum) const { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, power_spectrum.size()); + switch (optimization) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: { + constexpr int kNumFourBinBands = kFftLengthBy2 / 4; + constexpr int kLimit = kNumFourBinBands * 4; + for (size_t k = 0; k < kLimit; k += 4) { + const __m128 r = _mm_loadu_ps(&re[k]); + const __m128 i = _mm_loadu_ps(&im[k]); + const __m128 ii = _mm_mul_ps(i, i); + const __m128 rr = _mm_mul_ps(r, r); + const __m128 rrii = _mm_add_ps(rr, ii); + _mm_storeu_ps(&power_spectrum[k], rrii); + } + power_spectrum[kFftLengthBy2] = re[kFftLengthBy2] * re[kFftLengthBy2] + + im[kFftLengthBy2] * im[kFftLengthBy2]; + } break; + case Aec3Optimization::kAvx2: + SpectrumAVX2(power_spectrum); + break; +#endif + default: + std::transform(re.begin(), re.end(), im.begin(), power_spectrum.begin(), + [](float a, float b) { return a * a + b * b; }); + } + } + + // Copy the data from an interleaved array. + void CopyFromPackedArray(const std::array& v) { + re[0] = v[0]; + re[kFftLengthBy2] = v[1]; + im[0] = im[kFftLengthBy2] = 0; + for (size_t k = 1, j = 2; k < kFftLengthBy2; ++k) { + re[k] = v[j++]; + im[k] = v[j++]; + } + } + + // Copies the data into an interleaved array. + void CopyToPackedArray(std::array* v) const { + RTC_DCHECK(v); + (*v)[0] = re[0]; + (*v)[1] = re[kFftLengthBy2]; + for (size_t k = 1, j = 2; k < kFftLengthBy2; ++k) { + (*v)[j++] = re[k]; + (*v)[j++] = im[k]; + } + } + + std::array re; + std::array im; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_FFT_DATA_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/filter_analyzer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/filter_analyzer.cc new file mode 100644 index 00000000..b5bcad74 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/filter_analyzer.cc @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/filter_analyzer.h" + +#include + +#include +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +size_t FindPeakIndex(ArrayView filter_time_domain, + size_t peak_index_in, + size_t start_sample, + size_t end_sample) { + size_t peak_index_out = peak_index_in; + float max_h2 = + filter_time_domain[peak_index_out] * filter_time_domain[peak_index_out]; + for (size_t k = start_sample; k <= end_sample; ++k) { + float tmp = filter_time_domain[k] * filter_time_domain[k]; + if (tmp > max_h2) { + peak_index_out = k; + max_h2 = tmp; + } + } + + return peak_index_out; +} + +} // namespace + +std::atomic FilterAnalyzer::instance_count_(0); + +FilterAnalyzer::FilterAnalyzer(const EchoCanceller3Config& config, + size_t num_capture_channels) + : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), + bounded_erl_(config.ep_strength.bounded_erl), + default_gain_(config.ep_strength.default_gain), + h_highpass_(num_capture_channels, + std::vector( + GetTimeDomainLength(config.filter.refined.length_blocks), + 0.f)), + filter_analysis_states_(num_capture_channels, + FilterAnalysisState(config)), + filter_delays_blocks_(num_capture_channels, 0) { + Reset(); +} + +FilterAnalyzer::~FilterAnalyzer() = default; + +void FilterAnalyzer::Reset() { + blocks_since_reset_ = 0; + ResetRegion(); + for (auto& state : filter_analysis_states_) { + state.Reset(default_gain_); + } + std::fill(filter_delays_blocks_.begin(), filter_delays_blocks_.end(), 0); +} + +void FilterAnalyzer::Update( + ArrayView> filters_time_domain, + const RenderBuffer& render_buffer, + bool* any_filter_consistent, + float* max_echo_path_gain) { + RTC_DCHECK(any_filter_consistent); + RTC_DCHECK(max_echo_path_gain); + RTC_DCHECK_EQ(filters_time_domain.size(), filter_analysis_states_.size()); + RTC_DCHECK_EQ(filters_time_domain.size(), h_highpass_.size()); + + ++blocks_since_reset_; + SetRegionToAnalyze(filters_time_domain[0].size()); + AnalyzeRegion(filters_time_domain, render_buffer); + + // Aggregate the results for all capture channels. + auto& st_ch0 = filter_analysis_states_[0]; + *any_filter_consistent = st_ch0.consistent_estimate; + *max_echo_path_gain = st_ch0.gain; + min_filter_delay_blocks_ = filter_delays_blocks_[0]; + for (size_t ch = 1; ch < filters_time_domain.size(); ++ch) { + auto& st_ch = filter_analysis_states_[ch]; + *any_filter_consistent = + *any_filter_consistent || st_ch.consistent_estimate; + *max_echo_path_gain = std::max(*max_echo_path_gain, st_ch.gain); + min_filter_delay_blocks_ = + std::min(min_filter_delay_blocks_, filter_delays_blocks_[ch]); + } +} + +void FilterAnalyzer::AnalyzeRegion( + ArrayView> filters_time_domain, + const RenderBuffer& render_buffer) { + // Preprocess the filter to avoid issues with low-frequency components in the + // filter. + PreProcessFilters(filters_time_domain); + data_dumper_->DumpRaw("aec3_linear_filter_processed_td", h_highpass_[0]); + + constexpr float kOneByBlockSize = 1.f / kBlockSize; + for (size_t ch = 0; ch < filters_time_domain.size(); ++ch) { + RTC_DCHECK_LT(region_.start_sample_, filters_time_domain[ch].size()); + RTC_DCHECK_LT(region_.end_sample_, filters_time_domain[ch].size()); + + auto& st_ch = filter_analysis_states_[ch]; + RTC_DCHECK_EQ(h_highpass_[ch].size(), filters_time_domain[ch].size()); + RTC_DCHECK_GT(h_highpass_[ch].size(), 0); + st_ch.peak_index = std::min(st_ch.peak_index, h_highpass_[ch].size() - 1); + + st_ch.peak_index = + FindPeakIndex(h_highpass_[ch], st_ch.peak_index, region_.start_sample_, + region_.end_sample_); + filter_delays_blocks_[ch] = st_ch.peak_index >> kBlockSizeLog2; + UpdateFilterGain(h_highpass_[ch], &st_ch); + st_ch.filter_length_blocks = + filters_time_domain[ch].size() * kOneByBlockSize; + + st_ch.consistent_estimate = st_ch.consistent_filter_detector.Detect( + h_highpass_[ch], region_, + render_buffer.GetBlock(-filter_delays_blocks_[ch]), st_ch.peak_index, + filter_delays_blocks_[ch]); + } +} + +void FilterAnalyzer::UpdateFilterGain(ArrayView filter_time_domain, + FilterAnalysisState* st) { + bool sufficient_time_to_converge = + blocks_since_reset_ > 5 * kNumBlocksPerSecond; + + if (sufficient_time_to_converge && st->consistent_estimate) { + st->gain = fabsf(filter_time_domain[st->peak_index]); + } else { + // TODO(peah): Verify whether this check against a float is ok. + if (st->gain) { + st->gain = std::max(st->gain, fabsf(filter_time_domain[st->peak_index])); + } + } + + if (bounded_erl_ && st->gain) { + st->gain = std::max(st->gain, 0.01f); + } +} + +void FilterAnalyzer::PreProcessFilters( + ArrayView> filters_time_domain) { + for (size_t ch = 0; ch < filters_time_domain.size(); ++ch) { + RTC_DCHECK_LT(region_.start_sample_, filters_time_domain[ch].size()); + RTC_DCHECK_LT(region_.end_sample_, filters_time_domain[ch].size()); + + RTC_DCHECK_GE(h_highpass_[ch].capacity(), filters_time_domain[ch].size()); + h_highpass_[ch].resize(filters_time_domain[ch].size()); + // Minimum phase high-pass filter with cutoff frequency at about 600 Hz. + constexpr std::array h = { + {0.7929742f, -0.36072128f, -0.47047766f}}; + + std::fill(h_highpass_[ch].begin() + region_.start_sample_, + h_highpass_[ch].begin() + region_.end_sample_ + 1, 0.f); + float* h_highpass_ch = h_highpass_[ch].data(); + const float* filters_time_domain_ch = filters_time_domain[ch].data(); + const size_t region_end = region_.end_sample_; + for (size_t k = std::max(h.size() - 1, region_.start_sample_); + k <= region_end; ++k) { + float tmp = h_highpass_ch[k]; + for (size_t j = 0; j < h.size(); ++j) { + tmp += filters_time_domain_ch[k - j] * h[j]; + } + h_highpass_ch[k] = tmp; + } + } +} + +void FilterAnalyzer::ResetRegion() { + region_.start_sample_ = 0; + region_.end_sample_ = 0; +} + +void FilterAnalyzer::SetRegionToAnalyze(size_t filter_size) { + constexpr size_t kNumberBlocksToUpdate = 1; + auto& r = region_; + r.start_sample_ = r.end_sample_ >= filter_size - 1 ? 0 : r.end_sample_ + 1; + r.end_sample_ = + std::min(r.start_sample_ + kNumberBlocksToUpdate * kBlockSize - 1, + filter_size - 1); + + // Check range. + RTC_DCHECK_LT(r.start_sample_, filter_size); + RTC_DCHECK_LT(r.end_sample_, filter_size); + RTC_DCHECK_LE(r.start_sample_, r.end_sample_); +} + +FilterAnalyzer::ConsistentFilterDetector::ConsistentFilterDetector( + const EchoCanceller3Config& config) + : active_render_threshold_(config.render_levels.active_render_limit * + config.render_levels.active_render_limit * + kFftLengthBy2) { + Reset(); +} + +void FilterAnalyzer::ConsistentFilterDetector::Reset() { + significant_peak_ = false; + filter_floor_accum_ = 0.f; + filter_secondary_peak_ = 0.f; + filter_floor_low_limit_ = 0; + filter_floor_high_limit_ = 0; + consistent_estimate_counter_ = 0; + consistent_delay_reference_ = -10; +} + +bool FilterAnalyzer::ConsistentFilterDetector::Detect( + ArrayView filter_to_analyze, + const FilterRegion& region, + const Block& x_block, + size_t peak_index, + int delay_blocks) { + if (region.start_sample_ == 0) { + filter_floor_accum_ = 0.f; + filter_secondary_peak_ = 0.f; + filter_floor_low_limit_ = peak_index < 64 ? 0 : peak_index - 64; + filter_floor_high_limit_ = + peak_index > filter_to_analyze.size() - 129 ? 0 : peak_index + 128; + } + + float filter_floor_accum = filter_floor_accum_; + float filter_secondary_peak = filter_secondary_peak_; + for (size_t k = region.start_sample_; + k < std::min(region.end_sample_ + 1, filter_floor_low_limit_); ++k) { + float abs_h = fabsf(filter_to_analyze[k]); + filter_floor_accum += abs_h; + filter_secondary_peak = std::max(filter_secondary_peak, abs_h); + } + + for (size_t k = std::max(filter_floor_high_limit_, region.start_sample_); + k <= region.end_sample_; ++k) { + float abs_h = fabsf(filter_to_analyze[k]); + filter_floor_accum += abs_h; + filter_secondary_peak = std::max(filter_secondary_peak, abs_h); + } + filter_floor_accum_ = filter_floor_accum; + filter_secondary_peak_ = filter_secondary_peak; + + if (region.end_sample_ == filter_to_analyze.size() - 1) { + float filter_floor = filter_floor_accum_ / + (filter_floor_low_limit_ + filter_to_analyze.size() - + filter_floor_high_limit_); + + float abs_peak = fabsf(filter_to_analyze[peak_index]); + significant_peak_ = abs_peak > 10.f * filter_floor && + abs_peak > 2.f * filter_secondary_peak_; + } + + if (significant_peak_) { + bool active_render_block = false; + for (int ch = 0; ch < x_block.NumChannels(); ++ch) { + ArrayView x_channel = + x_block.View(/*band=*/0, ch); + const float x_energy = std::inner_product( + x_channel.begin(), x_channel.end(), x_channel.begin(), 0.f); + if (x_energy > active_render_threshold_) { + active_render_block = true; + break; + } + } + + if (consistent_delay_reference_ == delay_blocks) { + if (active_render_block) { + ++consistent_estimate_counter_; + } + } else { + consistent_estimate_counter_ = 0; + consistent_delay_reference_ = delay_blocks; + } + } + return consistent_estimate_counter_ > 1.5f * kNumBlocksPerSecond; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/filter_analyzer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/filter_analyzer.h new file mode 100644 index 00000000..9e87fbb9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/filter_analyzer.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_FILTER_ANALYZER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_FILTER_ANALYZER_H_ + +#include + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/block.h" + +namespace webrtc { + +class ApmDataDumper; +class RenderBuffer; + +// Class for analyzing the properties of an adaptive filter. +class FilterAnalyzer { + public: + FilterAnalyzer(const EchoCanceller3Config& config, + size_t num_capture_channels); + ~FilterAnalyzer(); + + FilterAnalyzer(const FilterAnalyzer&) = delete; + FilterAnalyzer& operator=(const FilterAnalyzer&) = delete; + + // Resets the analysis. + void Reset(); + + // Updates the estimates with new input data. + void Update(ArrayView> filters_time_domain, + const RenderBuffer& render_buffer, + bool* any_filter_consistent, + float* max_echo_path_gain); + + // Returns the delay in blocks for each filter. + ArrayView FilterDelaysBlocks() const { + return filter_delays_blocks_; + } + + // Returns the minimum delay of all filters in terms of blocks. + int MinFilterDelayBlocks() const { return min_filter_delay_blocks_; } + + // Returns the number of blocks for the current used filter. + int FilterLengthBlocks() const { + return filter_analysis_states_[0].filter_length_blocks; + } + + // Returns the preprocessed filter. + ArrayView> GetAdjustedFilters() const { + return h_highpass_; + } + + // Public for testing purposes only. + void SetRegionToAnalyze(size_t filter_size); + + private: + struct FilterAnalysisState; + + void AnalyzeRegion(ArrayView> filters_time_domain, + const RenderBuffer& render_buffer); + + void UpdateFilterGain(ArrayView filters_time_domain, + FilterAnalysisState* st); + void PreProcessFilters( + ArrayView> filters_time_domain); + + void ResetRegion(); + + struct FilterRegion { + size_t start_sample_; + size_t end_sample_; + }; + + // This class checks whether the shape of the impulse response has been + // consistent over time. + class ConsistentFilterDetector { + public: + explicit ConsistentFilterDetector(const EchoCanceller3Config& config); + void Reset(); + bool Detect(ArrayView filter_to_analyze, + const FilterRegion& region, + const Block& x_block, + size_t peak_index, + int delay_blocks); + + private: + bool significant_peak_; + float filter_floor_accum_; + float filter_secondary_peak_; + size_t filter_floor_low_limit_; + size_t filter_floor_high_limit_; + const float active_render_threshold_; + size_t consistent_estimate_counter_ = 0; + int consistent_delay_reference_ = -10; + }; + + struct FilterAnalysisState { + explicit FilterAnalysisState(const EchoCanceller3Config& config) + : filter_length_blocks(config.filter.refined_initial.length_blocks), + consistent_filter_detector(config) { + Reset(config.ep_strength.default_gain); + } + + void Reset(float default_gain) { + peak_index = 0; + gain = default_gain; + consistent_filter_detector.Reset(); + } + + float gain; + size_t peak_index; + int filter_length_blocks; + bool consistent_estimate = false; + ConsistentFilterDetector consistent_filter_detector; + }; + + static std::atomic instance_count_; + std::unique_ptr data_dumper_; + const bool bounded_erl_; + const float default_gain_; + std::vector> h_highpass_; + + size_t blocks_since_reset_ = 0; + FilterRegion region_; + + std::vector filter_analysis_states_; + std::vector filter_delays_blocks_; + + int min_filter_delay_blocks_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_FILTER_ANALYZER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/frame_blocker.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/frame_blocker.cc new file mode 100644 index 00000000..154dc126 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/frame_blocker.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/frame_blocker.h" + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +FrameBlocker::FrameBlocker(size_t num_bands, size_t num_channels) + : num_bands_(num_bands), + num_channels_(num_channels), + buffer_(num_bands_, std::vector>(num_channels)) { + RTC_DCHECK_LT(0, num_bands); + RTC_DCHECK_LT(0, num_channels); + for (auto& band : buffer_) { + for (auto& channel : band) { + channel.reserve(kBlockSize); + RTC_DCHECK(channel.empty()); + } + } +} + +FrameBlocker::~FrameBlocker() = default; + +void FrameBlocker::InsertSubFrameAndExtractBlock( + const std::vector>>& sub_frame, + Block* block) { + RTC_DCHECK(block); + RTC_DCHECK_EQ(num_bands_, block->NumBands()); + RTC_DCHECK_EQ(num_bands_, sub_frame.size()); + for (size_t band = 0; band < num_bands_; ++band) { + RTC_DCHECK_EQ(num_channels_, block->NumChannels()); + RTC_DCHECK_EQ(num_channels_, sub_frame[band].size()); + for (size_t channel = 0; channel < num_channels_; ++channel) { + RTC_DCHECK_GE(kBlockSize - 16, buffer_[band][channel].size()); + RTC_DCHECK_EQ(kSubFrameLength, sub_frame[band][channel].size()); + const int samples_to_block = kBlockSize - buffer_[band][channel].size(); + std::copy(buffer_[band][channel].begin(), buffer_[band][channel].end(), + block->begin(band, channel)); + std::copy(sub_frame[band][channel].begin(), + sub_frame[band][channel].begin() + samples_to_block, + block->begin(band, channel) + kBlockSize - samples_to_block); + buffer_[band][channel].clear(); + buffer_[band][channel].insert( + buffer_[band][channel].begin(), + sub_frame[band][channel].begin() + samples_to_block, + sub_frame[band][channel].end()); + } + } +} + +bool FrameBlocker::IsBlockAvailable() const { + return kBlockSize == buffer_[0][0].size(); +} + +void FrameBlocker::ExtractBlock(Block* block) { + RTC_DCHECK(block); + RTC_DCHECK_EQ(num_bands_, block->NumBands()); + RTC_DCHECK_EQ(num_channels_, block->NumChannels()); + RTC_DCHECK(IsBlockAvailable()); + for (size_t band = 0; band < num_bands_; ++band) { + for (size_t channel = 0; channel < num_channels_; ++channel) { + RTC_DCHECK_EQ(kBlockSize, buffer_[band][channel].size()); + std::copy(buffer_[band][channel].begin(), buffer_[band][channel].end(), + block->begin(band, channel)); + buffer_[band][channel].clear(); + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/frame_blocker.h b/pkg/apm/webrtc/modules/audio_processing/aec3/frame_blocker.h new file mode 100644 index 00000000..4cac7566 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/frame_blocker.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_FRAME_BLOCKER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_FRAME_BLOCKER_H_ + +#include + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/block.h" + +namespace webrtc { + +// Class for producing 64 sample multiband blocks from frames consisting of 2 +// subframes of 80 samples. +class FrameBlocker { + public: + FrameBlocker(size_t num_bands, size_t num_channels); + ~FrameBlocker(); + FrameBlocker(const FrameBlocker&) = delete; + FrameBlocker& operator=(const FrameBlocker&) = delete; + + // Inserts one 80 sample multiband subframe from the multiband frame and + // extracts one 64 sample multiband block. + void InsertSubFrameAndExtractBlock( + const std::vector>>& sub_frame, + Block* block); + // Reports whether a multiband block of 64 samples is available for + // extraction. + bool IsBlockAvailable() const; + // Extracts a multiband block of 64 samples. + void ExtractBlock(Block* block); + + private: + const size_t num_bands_; + const size_t num_channels_; + std::vector>> buffer_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_FRAME_BLOCKER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.cc new file mode 100644 index 00000000..498c21b5 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.cc @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/fullband_erle_estimator.h" + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +namespace { +constexpr float kEpsilon = 1e-3f; +constexpr float kX2BandEnergyThreshold = 44015068.0f; +constexpr int kBlocksToHoldErle = 100; +constexpr int kPointsToAccumulate = 6; +} // namespace + +FullBandErleEstimator::FullBandErleEstimator( + const EchoCanceller3Config::Erle& config, + size_t num_capture_channels) + : min_erle_log2_(FastApproxLog2f(config.min + kEpsilon)), + max_erle_lf_log2_(FastApproxLog2f(config.max_l + kEpsilon)), + hold_counters_instantaneous_erle_(num_capture_channels, 0), + erle_time_domain_log2_(num_capture_channels, min_erle_log2_), + instantaneous_erle_(num_capture_channels, ErleInstantaneous(config)), + linear_filters_qualities_(num_capture_channels) { + Reset(); +} + +FullBandErleEstimator::~FullBandErleEstimator() = default; + +void FullBandErleEstimator::Reset() { + for (auto& instantaneous_erle_ch : instantaneous_erle_) { + instantaneous_erle_ch.Reset(); + } + + UpdateQualityEstimates(); + std::fill(erle_time_domain_log2_.begin(), erle_time_domain_log2_.end(), + min_erle_log2_); + std::fill(hold_counters_instantaneous_erle_.begin(), + hold_counters_instantaneous_erle_.end(), 0); +} + +void FullBandErleEstimator::Update( + ArrayView X2, + ArrayView> Y2, + ArrayView> E2, + const std::vector& converged_filters) { + for (size_t ch = 0; ch < Y2.size(); ++ch) { + if (converged_filters[ch]) { + // Computes the fullband ERLE. + const float X2_sum = std::accumulate(X2.begin(), X2.end(), 0.0f); + if (X2_sum > kX2BandEnergyThreshold * X2.size()) { + const float Y2_sum = + std::accumulate(Y2[ch].begin(), Y2[ch].end(), 0.0f); + const float E2_sum = + std::accumulate(E2[ch].begin(), E2[ch].end(), 0.0f); + if (instantaneous_erle_[ch].Update(Y2_sum, E2_sum)) { + hold_counters_instantaneous_erle_[ch] = kBlocksToHoldErle; + erle_time_domain_log2_[ch] += + 0.05f * ((instantaneous_erle_[ch].GetInstErleLog2().value()) - + erle_time_domain_log2_[ch]); + erle_time_domain_log2_[ch] = + std::max(erle_time_domain_log2_[ch], min_erle_log2_); + } + } + } + --hold_counters_instantaneous_erle_[ch]; + if (hold_counters_instantaneous_erle_[ch] == 0) { + instantaneous_erle_[ch].ResetAccumulators(); + } + } + + UpdateQualityEstimates(); +} + +void FullBandErleEstimator::Dump( + const std::unique_ptr& data_dumper) const { + data_dumper->DumpRaw("aec3_fullband_erle_log2", FullbandErleLog2()); + instantaneous_erle_[0].Dump(data_dumper); +} + +void FullBandErleEstimator::UpdateQualityEstimates() { + for (size_t ch = 0; ch < instantaneous_erle_.size(); ++ch) { + linear_filters_qualities_[ch] = + instantaneous_erle_[ch].GetQualityEstimate(); + } +} + +FullBandErleEstimator::ErleInstantaneous::ErleInstantaneous( + const EchoCanceller3Config::Erle& config) + : clamp_inst_quality_to_zero_(config.clamp_quality_estimate_to_zero), + clamp_inst_quality_to_one_(config.clamp_quality_estimate_to_one) { + Reset(); +} + +FullBandErleEstimator::ErleInstantaneous::~ErleInstantaneous() = default; + +bool FullBandErleEstimator::ErleInstantaneous::Update(const float Y2_sum, + const float E2_sum) { + bool update_estimates = false; + E2_acum_ += E2_sum; + Y2_acum_ += Y2_sum; + num_points_++; + if (num_points_ == kPointsToAccumulate) { + if (E2_acum_ > 0.f) { + update_estimates = true; + erle_log2_ = FastApproxLog2f(Y2_acum_ / E2_acum_ + kEpsilon); + } + num_points_ = 0; + E2_acum_ = 0.f; + Y2_acum_ = 0.f; + } + + if (update_estimates) { + UpdateMaxMin(); + UpdateQualityEstimate(); + } + return update_estimates; +} + +void FullBandErleEstimator::ErleInstantaneous::Reset() { + ResetAccumulators(); + max_erle_log2_ = -10.f; // -30 dB. + min_erle_log2_ = 33.f; // 100 dB. + inst_quality_estimate_ = 0.f; +} + +void FullBandErleEstimator::ErleInstantaneous::ResetAccumulators() { + erle_log2_ = std::nullopt; + inst_quality_estimate_ = 0.f; + num_points_ = 0; + E2_acum_ = 0.f; + Y2_acum_ = 0.f; +} + +void FullBandErleEstimator::ErleInstantaneous::Dump( + const std::unique_ptr& data_dumper) const { + data_dumper->DumpRaw("aec3_fullband_erle_inst_log2", + erle_log2_ ? *erle_log2_ : -10.f); + data_dumper->DumpRaw( + "aec3_erle_instantaneous_quality", + GetQualityEstimate() ? GetQualityEstimate().value() : 0.f); + data_dumper->DumpRaw("aec3_fullband_erle_max_log2", max_erle_log2_); + data_dumper->DumpRaw("aec3_fullband_erle_min_log2", min_erle_log2_); +} + +void FullBandErleEstimator::ErleInstantaneous::UpdateMaxMin() { + RTC_DCHECK(erle_log2_); + // Adding the forgetting factors for the maximum and minimum and capping the + // result to the incoming value. + max_erle_log2_ -= 0.0004f; // Forget factor, approx 1dB every 3 sec. + max_erle_log2_ = std::max(max_erle_log2_, erle_log2_.value()); + min_erle_log2_ += 0.0004f; // Forget factor, approx 1dB every 3 sec. + min_erle_log2_ = std::min(min_erle_log2_, erle_log2_.value()); +} + +void FullBandErleEstimator::ErleInstantaneous::UpdateQualityEstimate() { + const float alpha = 0.07f; + float quality_estimate = 0.f; + RTC_DCHECK(erle_log2_); + // TODO(peah): Currently, the estimate can become be less than 0; this should + // be corrected. + if (max_erle_log2_ > min_erle_log2_) { + quality_estimate = (erle_log2_.value() - min_erle_log2_) / + (max_erle_log2_ - min_erle_log2_); + } + if (quality_estimate > inst_quality_estimate_) { + inst_quality_estimate_ = quality_estimate; + } else { + inst_quality_estimate_ += + alpha * (quality_estimate - inst_quality_estimate_); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.h new file mode 100644 index 00000000..d012d347 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_FULLBAND_ERLE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_FULLBAND_ERLE_ESTIMATOR_H_ + +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +// Estimates the echo return loss enhancement using the energy of all the +// freuquency bands. +class FullBandErleEstimator { + public: + FullBandErleEstimator(const EchoCanceller3Config::Erle& config, + size_t num_capture_channels); + ~FullBandErleEstimator(); + // Resets the ERLE estimator. + void Reset(); + + // Updates the ERLE estimator. + void Update(ArrayView X2, + ArrayView> Y2, + ArrayView> E2, + const std::vector& converged_filters); + + // Returns the fullband ERLE estimates in log2 units. + float FullbandErleLog2() const { + float min_erle = erle_time_domain_log2_[0]; + for (size_t ch = 1; ch < erle_time_domain_log2_.size(); ++ch) { + min_erle = std::min(min_erle, erle_time_domain_log2_[ch]); + } + return min_erle; + } + + // Returns an estimation of the current linear filter quality. It returns a + // float number between 0 and 1 mapping 1 to the highest possible quality. + ArrayView> GetInstLinearQualityEstimates() const { + return linear_filters_qualities_; + } + + void Dump(const std::unique_ptr& data_dumper) const; + + private: + void UpdateQualityEstimates(); + + class ErleInstantaneous { + public: + explicit ErleInstantaneous(const EchoCanceller3Config::Erle& config); + ~ErleInstantaneous(); + + // Updates the estimator with a new point, returns true + // if the instantaneous ERLE was updated due to having enough + // points for performing the estimate. + bool Update(float Y2_sum, float E2_sum); + // Resets the instantaneous ERLE estimator to its initial state. + void Reset(); + // Resets the members related with an instantaneous estimate. + void ResetAccumulators(); + // Returns the instantaneous ERLE in log2 units. + std::optional GetInstErleLog2() const { return erle_log2_; } + // Gets an indication between 0 and 1 of the performance of the linear + // filter for the current time instant. + std::optional GetQualityEstimate() const { + if (erle_log2_) { + float value = inst_quality_estimate_; + if (clamp_inst_quality_to_zero_) { + value = std::max(0.f, value); + } + if (clamp_inst_quality_to_one_) { + value = std::min(1.f, value); + } + return std::optional(value); + } + return std::nullopt; + } + void Dump(const std::unique_ptr& data_dumper) const; + + private: + void UpdateMaxMin(); + void UpdateQualityEstimate(); + const bool clamp_inst_quality_to_zero_; + const bool clamp_inst_quality_to_one_; + std::optional erle_log2_; + float inst_quality_estimate_; + float max_erle_log2_; + float min_erle_log2_; + float Y2_acum_; + float E2_acum_; + int num_points_; + }; + + const float min_erle_log2_; + const float max_erle_lf_log2_; + std::vector hold_counters_instantaneous_erle_; + std::vector erle_time_domain_log2_; + std::vector instantaneous_erle_; + std::vector> linear_filters_qualities_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_FULLBAND_ERLE_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter.cc new file mode 100644 index 00000000..90d550a2 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter.cc @@ -0,0 +1,825 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/aec3/matched_filter.h" + +#include +#include + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_HAS_NEON) +#include +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif +#include +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace { + +// Subsample rate used for computing the accumulated error. +// The implementation of some core functions depends on this constant being +// equal to 4. +constexpr int kAccumulatedErrorSubSampleRate = 4; + +void UpdateAccumulatedError( + const webrtc::ArrayView instantaneous_accumulated_error, + const webrtc::ArrayView accumulated_error, + float one_over_error_sum_anchor) { + static constexpr float kSmoothConstantIncreases = 0.015f; + for (size_t k = 0; k < instantaneous_accumulated_error.size(); ++k) { + float error_norm = + instantaneous_accumulated_error[k] * one_over_error_sum_anchor; + if (error_norm < accumulated_error[k]) { + accumulated_error[k] = error_norm; + } else { + accumulated_error[k] += + kSmoothConstantIncreases * (error_norm - accumulated_error[k]); + } + } +} + +size_t ComputePreEchoLag(const webrtc::ArrayView accumulated_error, + size_t lag, + size_t alignment_shift_winner) { + static constexpr float kPreEchoThreshold = 0.5f; + RTC_DCHECK_GE(lag, alignment_shift_winner); + size_t pre_echo_lag_estimate = lag - alignment_shift_winner; + size_t maximum_pre_echo_lag = + std::min(pre_echo_lag_estimate / kAccumulatedErrorSubSampleRate, + accumulated_error.size()); + for (int k = static_cast(maximum_pre_echo_lag) - 1; k >= 0; --k) { + if (accumulated_error[k] > kPreEchoThreshold) { + break; + } + pre_echo_lag_estimate = (k + 1) * kAccumulatedErrorSubSampleRate - 1; + } + return pre_echo_lag_estimate + alignment_shift_winner; +} + +} // namespace + +namespace webrtc { +namespace aec3 { + +#if defined(WEBRTC_HAS_NEON) + +inline float SumAllElements(float32x4_t elements) { + float32x2_t sum = vpadd_f32(vget_low_f32(elements), vget_high_f32(elements)); + sum = vpadd_f32(sum, sum); + return vget_lane_f32(sum, 0); +} + +void MatchedFilterCoreWithAccumulatedError_NEON( + size_t x_start_index, + float x2_sum_threshold, + float smoothing, + webrtc::ArrayView x, + webrtc::ArrayView y, + webrtc::ArrayView h, + bool* filters_updated, + float* error_sum, + webrtc::ArrayView accumulated_error, + webrtc::ArrayView scratch_memory) { + const int h_size = static_cast(h.size()); + const int x_size = static_cast(x.size()); + RTC_DCHECK_EQ(0, h_size % 4); + std::fill(accumulated_error.begin(), accumulated_error.end(), 0.0f); + // Process for all samples in the sub-block. + for (size_t i = 0; i < y.size(); ++i) { + // Apply the matched filter as filter * x, and compute x * x. + RTC_DCHECK_GT(x_size, x_start_index); + // Compute loop chunk sizes until, and after, the wraparound of the circular + // buffer for x. + const int chunk1 = + std::min(h_size, static_cast(x_size - x_start_index)); + if (chunk1 != h_size) { + const int chunk2 = h_size - chunk1; + std::copy(x.begin() + x_start_index, x.end(), scratch_memory.begin()); + std::copy(x.begin(), x.begin() + chunk2, scratch_memory.begin() + chunk1); + } + const float* x_p = + chunk1 != h_size ? scratch_memory.data() : &x[x_start_index]; + const float* h_p = &h[0]; + float* accumulated_error_p = &accumulated_error[0]; + // Initialize values for the accumulation. + float32x4_t x2_sum_128 = vdupq_n_f32(0); + float x2_sum = 0.f; + float s = 0; + // Perform 128 bit vector operations. + const int limit_by_4 = h_size >> 2; + for (int k = limit_by_4; k > 0; + --k, h_p += 4, x_p += 4, accumulated_error_p++) { + // Load the data into 128 bit vectors. + const float32x4_t x_k = vld1q_f32(x_p); + const float32x4_t h_k = vld1q_f32(h_p); + // Compute and accumulate x * x. + x2_sum_128 = vmlaq_f32(x2_sum_128, x_k, x_k); + // Compute x * h + float32x4_t hk_xk_128 = vmulq_f32(h_k, x_k); + s += SumAllElements(hk_xk_128); + const float e = s - y[i]; + accumulated_error_p[0] += e * e; + } + // Combine the accumulated vector and scalar values. + x2_sum += SumAllElements(x2_sum_128); + // Compute the matched filter error. + float e = y[i] - s; + const bool saturation = y[i] >= 32000.f || y[i] <= -32000.f; + (*error_sum) += e * e; + // Update the matched filter estimate in an NLMS manner. + if (x2_sum > x2_sum_threshold && !saturation) { + RTC_DCHECK_LT(0.f, x2_sum); + const float alpha = smoothing * e / x2_sum; + const float32x4_t alpha_128 = vmovq_n_f32(alpha); + // filter = filter + smoothing * (y - filter * x) * x / x * x. + float* h_p = &h[0]; + x_p = chunk1 != h_size ? scratch_memory.data() : &x[x_start_index]; + // Perform 128 bit vector operations. + const int limit_by_4 = h_size >> 2; + for (int k = limit_by_4; k > 0; --k, h_p += 4, x_p += 4) { + // Load the data into 128 bit vectors. + float32x4_t h_k = vld1q_f32(h_p); + const float32x4_t x_k = vld1q_f32(x_p); + // Compute h = h + alpha * x. + h_k = vmlaq_f32(h_k, alpha_128, x_k); + // Store the result. + vst1q_f32(h_p, h_k); + } + *filters_updated = true; + } + x_start_index = x_start_index > 0 ? x_start_index - 1 : x_size - 1; + } +} + +void MatchedFilterCore_NEON(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + webrtc::ArrayView x, + webrtc::ArrayView y, + webrtc::ArrayView h, + bool* filters_updated, + float* error_sum, + bool compute_accumulated_error, + webrtc::ArrayView accumulated_error, + webrtc::ArrayView scratch_memory) { + const int h_size = static_cast(h.size()); + const int x_size = static_cast(x.size()); + RTC_DCHECK_EQ(0, h_size % 4); + + if (compute_accumulated_error) { + return MatchedFilterCoreWithAccumulatedError_NEON( + x_start_index, x2_sum_threshold, smoothing, x, y, h, filters_updated, + error_sum, accumulated_error, scratch_memory); + } + + // Process for all samples in the sub-block. + for (size_t i = 0; i < y.size(); ++i) { + // Apply the matched filter as filter * x, and compute x * x. + + RTC_DCHECK_GT(x_size, x_start_index); + const float* x_p = &x[x_start_index]; + const float* h_p = &h[0]; + + // Initialize values for the accumulation. + float32x4_t s_128 = vdupq_n_f32(0); + float32x4_t x2_sum_128 = vdupq_n_f32(0); + float x2_sum = 0.f; + float s = 0; + + // Compute loop chunk sizes until, and after, the wraparound of the circular + // buffer for x. + const int chunk1 = + std::min(h_size, static_cast(x_size - x_start_index)); + + // Perform the loop in two chunks. + const int chunk2 = h_size - chunk1; + for (int limit : {chunk1, chunk2}) { + // Perform 128 bit vector operations. + const int limit_by_4 = limit >> 2; + for (int k = limit_by_4; k > 0; --k, h_p += 4, x_p += 4) { + // Load the data into 128 bit vectors. + const float32x4_t x_k = vld1q_f32(x_p); + const float32x4_t h_k = vld1q_f32(h_p); + // Compute and accumulate x * x and h * x. + x2_sum_128 = vmlaq_f32(x2_sum_128, x_k, x_k); + s_128 = vmlaq_f32(s_128, h_k, x_k); + } + + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_4 * 4; k > 0; --k, ++h_p, ++x_p) { + const float x_k = *x_p; + x2_sum += x_k * x_k; + s += *h_p * x_k; + } + + x_p = &x[0]; + } + + // Combine the accumulated vector and scalar values. + s += SumAllElements(s_128); + x2_sum += SumAllElements(x2_sum_128); + + // Compute the matched filter error. + float e = y[i] - s; + const bool saturation = y[i] >= 32000.f || y[i] <= -32000.f; + (*error_sum) += e * e; + + // Update the matched filter estimate in an NLMS manner. + if (x2_sum > x2_sum_threshold && !saturation) { + RTC_DCHECK_LT(0.f, x2_sum); + const float alpha = smoothing * e / x2_sum; + const float32x4_t alpha_128 = vmovq_n_f32(alpha); + + // filter = filter + smoothing * (y - filter * x) * x / x * x. + float* h_p = &h[0]; + x_p = &x[x_start_index]; + + // Perform the loop in two chunks. + for (int limit : {chunk1, chunk2}) { + // Perform 128 bit vector operations. + const int limit_by_4 = limit >> 2; + for (int k = limit_by_4; k > 0; --k, h_p += 4, x_p += 4) { + // Load the data into 128 bit vectors. + float32x4_t h_k = vld1q_f32(h_p); + const float32x4_t x_k = vld1q_f32(x_p); + // Compute h = h + alpha * x. + h_k = vmlaq_f32(h_k, alpha_128, x_k); + + // Store the result. + vst1q_f32(h_p, h_k); + } + + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_4 * 4; k > 0; --k, ++h_p, ++x_p) { + *h_p += alpha * *x_p; + } + + x_p = &x[0]; + } + + *filters_updated = true; + } + + x_start_index = x_start_index > 0 ? x_start_index - 1 : x_size - 1; + } +} + +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) + +void MatchedFilterCore_AccumulatedError_SSE2(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + ArrayView x, + ArrayView y, + ArrayView h, + bool* filters_updated, + float* error_sum, + ArrayView accumulated_error, + ArrayView scratch_memory) { + const int h_size = static_cast(h.size()); + const int x_size = static_cast(x.size()); + RTC_DCHECK_EQ(0, h_size % 8); + std::fill(accumulated_error.begin(), accumulated_error.end(), 0.0f); + // Process for all samples in the sub-block. + for (size_t i = 0; i < y.size(); ++i) { + // Apply the matched filter as filter * x, and compute x * x. + RTC_DCHECK_GT(x_size, x_start_index); + const int chunk1 = + std::min(h_size, static_cast(x_size - x_start_index)); + if (chunk1 != h_size) { + const int chunk2 = h_size - chunk1; + std::copy(x.begin() + x_start_index, x.end(), scratch_memory.begin()); + std::copy(x.begin(), x.begin() + chunk2, scratch_memory.begin() + chunk1); + } + const float* x_p = + chunk1 != h_size ? scratch_memory.data() : &x[x_start_index]; + const float* h_p = &h[0]; + float* a_p = &accumulated_error[0]; + __m128 s_inst_128; + __m128 s_inst_128_4; + __m128 x2_sum_128 = _mm_set1_ps(0); + __m128 x2_sum_128_4 = _mm_set1_ps(0); + __m128 e_128; + float* const s_p = reinterpret_cast(&s_inst_128); + float* const s_4_p = reinterpret_cast(&s_inst_128_4); + float* const e_p = reinterpret_cast(&e_128); + float x2_sum = 0.0f; + float s_acum = 0; + // Perform 128 bit vector operations. + const int limit_by_8 = h_size >> 3; + for (int k = limit_by_8; k > 0; --k, h_p += 8, x_p += 8, a_p += 2) { + // Load the data into 128 bit vectors. + const __m128 x_k = _mm_loadu_ps(x_p); + const __m128 h_k = _mm_loadu_ps(h_p); + const __m128 x_k_4 = _mm_loadu_ps(x_p + 4); + const __m128 h_k_4 = _mm_loadu_ps(h_p + 4); + const __m128 xx = _mm_mul_ps(x_k, x_k); + const __m128 xx_4 = _mm_mul_ps(x_k_4, x_k_4); + // Compute and accumulate x * x and h * x. + x2_sum_128 = _mm_add_ps(x2_sum_128, xx); + x2_sum_128_4 = _mm_add_ps(x2_sum_128_4, xx_4); + s_inst_128 = _mm_mul_ps(h_k, x_k); + s_inst_128_4 = _mm_mul_ps(h_k_4, x_k_4); + s_acum += s_p[0] + s_p[1] + s_p[2] + s_p[3]; + e_p[0] = s_acum - y[i]; + s_acum += s_4_p[0] + s_4_p[1] + s_4_p[2] + s_4_p[3]; + e_p[1] = s_acum - y[i]; + a_p[0] += e_p[0] * e_p[0]; + a_p[1] += e_p[1] * e_p[1]; + } + // Combine the accumulated vector and scalar values. + x2_sum_128 = _mm_add_ps(x2_sum_128, x2_sum_128_4); + float* v = reinterpret_cast(&x2_sum_128); + x2_sum += v[0] + v[1] + v[2] + v[3]; + // Compute the matched filter error. + float e = y[i] - s_acum; + const bool saturation = y[i] >= 32000.f || y[i] <= -32000.f; + (*error_sum) += e * e; + // Update the matched filter estimate in an NLMS manner. + if (x2_sum > x2_sum_threshold && !saturation) { + RTC_DCHECK_LT(0.f, x2_sum); + const float alpha = smoothing * e / x2_sum; + const __m128 alpha_128 = _mm_set1_ps(alpha); + // filter = filter + smoothing * (y - filter * x) * x / x * x. + float* h_p2 = &h[0]; + const float* x_p2 = + chunk1 != h_size ? scratch_memory.data() : &x[x_start_index]; + // Perform 128 bit vector operations. + const int limit_by_4 = h_size >> 2; + for (int k = limit_by_4; k > 0; --k, h_p2 += 4, x_p2 += 4) { + // Load the data into 128 bit vectors. + __m128 h_k = _mm_loadu_ps(h_p2); + const __m128 x_k = _mm_loadu_ps(x_p2); + // Compute h = h + alpha * x. + const __m128 alpha_x = _mm_mul_ps(alpha_128, x_k); + h_k = _mm_add_ps(h_k, alpha_x); + // Store the result. + _mm_storeu_ps(h_p2, h_k); + } + *filters_updated = true; + } + x_start_index = x_start_index > 0 ? x_start_index - 1 : x_size - 1; + } +} + +void MatchedFilterCore_SSE2(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + ArrayView x, + ArrayView y, + ArrayView h, + bool* filters_updated, + float* error_sum, + bool compute_accumulated_error, + ArrayView accumulated_error, + ArrayView scratch_memory) { + if (compute_accumulated_error) { + return MatchedFilterCore_AccumulatedError_SSE2( + x_start_index, x2_sum_threshold, smoothing, x, y, h, filters_updated, + error_sum, accumulated_error, scratch_memory); + } + const int h_size = static_cast(h.size()); + const int x_size = static_cast(x.size()); + RTC_DCHECK_EQ(0, h_size % 4); + // Process for all samples in the sub-block. + for (size_t i = 0; i < y.size(); ++i) { + // Apply the matched filter as filter * x, and compute x * x. + RTC_DCHECK_GT(x_size, x_start_index); + const float* x_p = &x[x_start_index]; + const float* h_p = &h[0]; + // Initialize values for the accumulation. + __m128 s_128 = _mm_set1_ps(0); + __m128 s_128_4 = _mm_set1_ps(0); + __m128 x2_sum_128 = _mm_set1_ps(0); + __m128 x2_sum_128_4 = _mm_set1_ps(0); + float x2_sum = 0.f; + float s = 0; + // Compute loop chunk sizes until, and after, the wraparound of the circular + // buffer for x. + const int chunk1 = + std::min(h_size, static_cast(x_size - x_start_index)); + // Perform the loop in two chunks. + const int chunk2 = h_size - chunk1; + for (int limit : {chunk1, chunk2}) { + // Perform 128 bit vector operations. + const int limit_by_8 = limit >> 3; + for (int k = limit_by_8; k > 0; --k, h_p += 8, x_p += 8) { + // Load the data into 128 bit vectors. + const __m128 x_k = _mm_loadu_ps(x_p); + const __m128 h_k = _mm_loadu_ps(h_p); + const __m128 x_k_4 = _mm_loadu_ps(x_p + 4); + const __m128 h_k_4 = _mm_loadu_ps(h_p + 4); + const __m128 xx = _mm_mul_ps(x_k, x_k); + const __m128 xx_4 = _mm_mul_ps(x_k_4, x_k_4); + // Compute and accumulate x * x and h * x. + x2_sum_128 = _mm_add_ps(x2_sum_128, xx); + x2_sum_128_4 = _mm_add_ps(x2_sum_128_4, xx_4); + const __m128 hx = _mm_mul_ps(h_k, x_k); + const __m128 hx_4 = _mm_mul_ps(h_k_4, x_k_4); + s_128 = _mm_add_ps(s_128, hx); + s_128_4 = _mm_add_ps(s_128_4, hx_4); + } + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_8 * 8; k > 0; --k, ++h_p, ++x_p) { + const float x_k = *x_p; + x2_sum += x_k * x_k; + s += *h_p * x_k; + } + x_p = &x[0]; + } + // Combine the accumulated vector and scalar values. + x2_sum_128 = _mm_add_ps(x2_sum_128, x2_sum_128_4); + float* v = reinterpret_cast(&x2_sum_128); + x2_sum += v[0] + v[1] + v[2] + v[3]; + s_128 = _mm_add_ps(s_128, s_128_4); + v = reinterpret_cast(&s_128); + s += v[0] + v[1] + v[2] + v[3]; + // Compute the matched filter error. + float e = y[i] - s; + const bool saturation = y[i] >= 32000.f || y[i] <= -32000.f; + (*error_sum) += e * e; + // Update the matched filter estimate in an NLMS manner. + if (x2_sum > x2_sum_threshold && !saturation) { + RTC_DCHECK_LT(0.f, x2_sum); + const float alpha = smoothing * e / x2_sum; + const __m128 alpha_128 = _mm_set1_ps(alpha); + // filter = filter + smoothing * (y - filter * x) * x / x * x. + float* h_p2 = &h[0]; + x_p = &x[x_start_index]; + // Perform the loop in two chunks. + for (int limit : {chunk1, chunk2}) { + // Perform 128 bit vector operations. + const int limit_by_4 = limit >> 2; + for (int k = limit_by_4; k > 0; --k, h_p2 += 4, x_p += 4) { + // Load the data into 128 bit vectors. + __m128 h_k = _mm_loadu_ps(h_p2); + const __m128 x_k = _mm_loadu_ps(x_p); + + // Compute h = h + alpha * x. + const __m128 alpha_x = _mm_mul_ps(alpha_128, x_k); + h_k = _mm_add_ps(h_k, alpha_x); + // Store the result. + _mm_storeu_ps(h_p2, h_k); + } + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_4 * 4; k > 0; --k, ++h_p2, ++x_p) { + *h_p2 += alpha * *x_p; + } + x_p = &x[0]; + } + *filters_updated = true; + } + x_start_index = x_start_index > 0 ? x_start_index - 1 : x_size - 1; + } +} +#endif + +void MatchedFilterCore(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + ArrayView x, + ArrayView y, + ArrayView h, + bool* filters_updated, + float* error_sum, + bool compute_accumulated_error, + ArrayView accumulated_error) { + if (compute_accumulated_error) { + std::fill(accumulated_error.begin(), accumulated_error.end(), 0.0f); + } + + // Process for all samples in the sub-block. + for (size_t i = 0; i < y.size(); ++i) { + // Apply the matched filter as filter * x, and compute x * x. + float x2_sum = 0.f; + float s = 0; + size_t x_index = x_start_index; + if (compute_accumulated_error) { + for (size_t k = 0; k < h.size(); ++k) { + x2_sum += x[x_index] * x[x_index]; + s += h[k] * x[x_index]; + x_index = x_index < (x.size() - 1) ? x_index + 1 : 0; + if ((k + 1 & 0b11) == 0) { + int idx = k >> 2; + accumulated_error[idx] += (y[i] - s) * (y[i] - s); + } + } + } else { + for (size_t k = 0; k < h.size(); ++k) { + x2_sum += x[x_index] * x[x_index]; + s += h[k] * x[x_index]; + x_index = x_index < (x.size() - 1) ? x_index + 1 : 0; + } + } + + // Compute the matched filter error. + float e = y[i] - s; + const bool saturation = y[i] >= 32000.f || y[i] <= -32000.f; + (*error_sum) += e * e; + + // Update the matched filter estimate in an NLMS manner. + if (x2_sum > x2_sum_threshold && !saturation) { + RTC_DCHECK_LT(0.f, x2_sum); + const float alpha = smoothing * e / x2_sum; + + // filter = filter + smoothing * (y - filter * x) * x / x * x. + size_t x_index2 = x_start_index; + for (size_t k = 0; k < h.size(); ++k) { + h[k] += alpha * x[x_index2]; + x_index2 = x_index2 < (x.size() - 1) ? x_index2 + 1 : 0; + } + *filters_updated = true; + } + + x_start_index = x_start_index > 0 ? x_start_index - 1 : x.size() - 1; + } +} + +size_t MaxSquarePeakIndex(ArrayView h) { + if (h.size() < 2) { + return 0; + } + float max_element1 = h[0] * h[0]; + float max_element2 = h[1] * h[1]; + size_t lag_estimate1 = 0; + size_t lag_estimate2 = 1; + const size_t last_index = h.size() - 1; + // Keeping track of even & odd max elements separately typically allows the + // compiler to produce more efficient code. + for (size_t k = 2; k < last_index; k += 2) { + float element1 = h[k] * h[k]; + float element2 = h[k + 1] * h[k + 1]; + if (element1 > max_element1) { + max_element1 = element1; + lag_estimate1 = k; + } + if (element2 > max_element2) { + max_element2 = element2; + lag_estimate2 = k + 1; + } + } + if (max_element2 > max_element1) { + max_element1 = max_element2; + lag_estimate1 = lag_estimate2; + } + // In case of odd h size, we have not yet checked the last element. + float last_element = h[last_index] * h[last_index]; + if (last_element > max_element1) { + return last_index; + } + return lag_estimate1; +} + +} // namespace aec3 + +MatchedFilter::MatchedFilter(ApmDataDumper* data_dumper, + Aec3Optimization optimization, + size_t sub_block_size, + size_t window_size_sub_blocks, + int num_matched_filters, + size_t alignment_shift_sub_blocks, + float excitation_limit, + float smoothing_fast, + float smoothing_slow, + float matching_filter_threshold, + bool detect_pre_echo) + : data_dumper_(data_dumper), + optimization_(optimization), + sub_block_size_(sub_block_size), + filter_intra_lag_shift_(alignment_shift_sub_blocks * sub_block_size_), + filters_( + num_matched_filters, + std::vector(window_size_sub_blocks * sub_block_size_, 0.f)), + filters_offsets_(num_matched_filters, 0), + excitation_limit_(excitation_limit), + smoothing_fast_(smoothing_fast), + smoothing_slow_(smoothing_slow), + matching_filter_threshold_(matching_filter_threshold), + detect_pre_echo_(detect_pre_echo) { + RTC_DCHECK(data_dumper); + RTC_DCHECK_LT(0, window_size_sub_blocks); + RTC_DCHECK((kBlockSize % sub_block_size) == 0); + RTC_DCHECK((sub_block_size % 4) == 0); + static_assert(kAccumulatedErrorSubSampleRate == 4); + if (detect_pre_echo_) { + accumulated_error_ = std::vector>( + num_matched_filters, + std::vector(window_size_sub_blocks * sub_block_size_ / + kAccumulatedErrorSubSampleRate, + 1.0f)); + + instantaneous_accumulated_error_ = + std::vector(window_size_sub_blocks * sub_block_size_ / + kAccumulatedErrorSubSampleRate, + 0.0f); + scratch_memory_ = + std::vector(window_size_sub_blocks * sub_block_size_); + } +} + +MatchedFilter::~MatchedFilter() = default; + +void MatchedFilter::Reset(bool full_reset) { + for (auto& f : filters_) { + std::fill(f.begin(), f.end(), 0.f); + } + + winner_lag_ = std::nullopt; + reported_lag_estimate_ = std::nullopt; + if (full_reset) { + for (auto& e : accumulated_error_) { + std::fill(e.begin(), e.end(), 1.0f); + } + number_pre_echo_updates_ = 0; + } +} + +void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer, + ArrayView capture, + bool use_slow_smoothing) { + RTC_DCHECK_EQ(sub_block_size_, capture.size()); + auto& y = capture; + + const float smoothing = + use_slow_smoothing ? smoothing_slow_ : smoothing_fast_; + + const float x2_sum_threshold = + filters_[0].size() * excitation_limit_ * excitation_limit_; + + // Compute anchor for the matched filter error. + float error_sum_anchor = 0.0f; + for (size_t k = 0; k < y.size(); ++k) { + error_sum_anchor += y[k] * y[k]; + } + + // Apply all matched filters. + float winner_error_sum = error_sum_anchor; + winner_lag_ = std::nullopt; + reported_lag_estimate_ = std::nullopt; + size_t alignment_shift = 0; + std::optional previous_lag_estimate; + const int num_filters = static_cast(filters_.size()); + int winner_index = -1; + for (int n = 0; n < num_filters; ++n) { + float error_sum = 0.f; + bool filters_updated = false; + const bool compute_pre_echo = + detect_pre_echo_ && n == last_detected_best_lag_filter_; + + size_t x_start_index = + (render_buffer.read + alignment_shift + sub_block_size_ - 1) % + render_buffer.buffer.size(); + + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + 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; + case Aec3Optimization::kAvx2: + aec3::MatchedFilterCore_AVX2( + x_start_index, x2_sum_threshold, smoothing, render_buffer.buffer, y, + filters_[n], &filters_updated, &error_sum, compute_pre_echo, + instantaneous_accumulated_error_, scratch_memory_); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: + aec3::MatchedFilterCore_NEON( + x_start_index, x2_sum_threshold, smoothing, render_buffer.buffer, y, + filters_[n], &filters_updated, &error_sum, compute_pre_echo, + instantaneous_accumulated_error_, scratch_memory_); + break; +#endif + default: + aec3::MatchedFilterCore(x_start_index, x2_sum_threshold, smoothing, + render_buffer.buffer, y, filters_[n], + &filters_updated, &error_sum, compute_pre_echo, + instantaneous_accumulated_error_); + } + + // Estimate the lag in the matched filter as the distance to the portion in + // the filter that contributes the most to the matched filter output. This + // is detected as the peak of the matched filter. + const size_t lag_estimate = aec3::MaxSquarePeakIndex(filters_[n]); + const bool reliable = + lag_estimate > 2 && lag_estimate < (filters_[n].size() - 10) && + error_sum < matching_filter_threshold_ * error_sum_anchor; + + // Find the best estimate + const size_t lag = lag_estimate + alignment_shift; + if (filters_updated && reliable && error_sum < winner_error_sum) { + winner_error_sum = error_sum; + winner_index = n; + // In case that 2 matched filters return the same winner candidate + // (overlap region), the one with the smaller index is chosen in order + // to search for pre-echoes. + if (previous_lag_estimate && previous_lag_estimate == lag) { + winner_lag_ = previous_lag_estimate; + winner_index = n - 1; + } else { + winner_lag_ = lag; + } + } + previous_lag_estimate = lag; + alignment_shift += filter_intra_lag_shift_; + } + + if (winner_index != -1) { + RTC_DCHECK(winner_lag_.has_value()); + reported_lag_estimate_ = + LagEstimate(winner_lag_.value(), /*pre_echo_lag=*/winner_lag_.value()); + if (detect_pre_echo_ && last_detected_best_lag_filter_ == winner_index) { + static constexpr float kEnergyThreshold = 1.0f; + if (error_sum_anchor > kEnergyThreshold) { + UpdateAccumulatedError(instantaneous_accumulated_error_, + accumulated_error_[winner_index], + 1.0f / error_sum_anchor); + number_pre_echo_updates_++; + } + if (number_pre_echo_updates_ >= 50) { + reported_lag_estimate_->pre_echo_lag = ComputePreEchoLag( + accumulated_error_[winner_index], winner_lag_.value(), + winner_index * filter_intra_lag_shift_ /*alignment_shift_winner*/); + } else { + reported_lag_estimate_->pre_echo_lag = winner_lag_.value(); + } + } + last_detected_best_lag_filter_ = winner_index; + } + if (ApmDataDumper::IsAvailable()) { + Dump(); + data_dumper_->DumpRaw("error_sum_anchor", error_sum_anchor / y.size()); + data_dumper_->DumpRaw("number_pre_echo_updates", number_pre_echo_updates_); + data_dumper_->DumpRaw("filter_smoothing", smoothing); + } +} + +void MatchedFilter::LogFilterProperties(int /* sample_rate_hz */, + size_t shift, + size_t downsampling_factor) const { + size_t alignment_shift = 0; + constexpr int kFsBy1000 = 16; + for (size_t k = 0; k < filters_.size(); ++k) { + int start = static_cast(alignment_shift * downsampling_factor); + int end = static_cast((alignment_shift + filters_[k].size()) * + downsampling_factor); + RTC_LOG(LS_VERBOSE) << "Filter " << k << ": start: " + << (start - static_cast(shift)) / kFsBy1000 + << " ms, end: " + << (end - static_cast(shift)) / kFsBy1000 + << " ms."; + alignment_shift += filter_intra_lag_shift_; + } +} + +void MatchedFilter::Dump() { + for (size_t n = 0; n < filters_.size(); ++n) { + const size_t lag_estimate = aec3::MaxSquarePeakIndex(filters_[n]); + std::string dumper_filter = "aec3_correlator_" + std::to_string(n) + "_h"; + data_dumper_->DumpRaw(dumper_filter.c_str(), filters_[n]); + std::string dumper_lag = "aec3_correlator_lag_" + std::to_string(n); + data_dumper_->DumpRaw(dumper_lag.c_str(), + lag_estimate + n * filter_intra_lag_shift_); + if (detect_pre_echo_) { + std::string dumper_error = + "aec3_correlator_error_" + std::to_string(n) + "_h"; + data_dumper_->DumpRaw(dumper_error.c_str(), accumulated_error_[n]); + + size_t pre_echo_lag = ComputePreEchoLag( + accumulated_error_[n], lag_estimate + n * filter_intra_lag_shift_, + n * filter_intra_lag_shift_); + std::string dumper_pre_lag = + "aec3_correlator_pre_echo_lag_" + std::to_string(n); + data_dumper_->DumpRaw(dumper_pre_lag.c_str(), pre_echo_lag); + if (static_cast(n) == last_detected_best_lag_filter_) { + data_dumper_->DumpRaw("aec3_pre_echo_delay_winner_inst", pre_echo_lag); + } + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter.h b/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter.h new file mode 100644 index 00000000..63992bd1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/gtest_prod_util.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { + +class ApmDataDumper; +struct DownsampledRenderBuffer; + +namespace aec3 { + +#if defined(WEBRTC_HAS_NEON) + +// Filter core for the matched filter that is optimized for NEON. +void MatchedFilterCore_NEON(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + webrtc::ArrayView x, + webrtc::ArrayView y, + webrtc::ArrayView h, + bool* filters_updated, + float* error_sum, + bool compute_accumulation_error, + webrtc::ArrayView accumulated_error, + webrtc::ArrayView scratch_memory); + +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) + +// Filter core for the matched filter that is optimized for SSE2. +void MatchedFilterCore_SSE2(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + ArrayView x, + ArrayView y, + ArrayView h, + bool* filters_updated, + float* error_sum, + bool compute_accumulated_error, + ArrayView accumulated_error, + ArrayView scratch_memory); + +// Filter core for the matched filter that is optimized for AVX2. +void MatchedFilterCore_AVX2(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + ArrayView x, + ArrayView y, + ArrayView h, + bool* filters_updated, + float* error_sum, + bool compute_accumulated_error, + ArrayView accumulated_error, + ArrayView scratch_memory); + +#endif + +// Filter core for the matched filter. +void MatchedFilterCore(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + ArrayView x, + ArrayView y, + ArrayView h, + bool* filters_updated, + float* error_sum, + bool compute_accumulation_error, + ArrayView accumulated_error); + +// Find largest peak of squared values in array. +size_t MaxSquarePeakIndex(ArrayView h); + +} // namespace aec3 + +// Produces recursively updated cross-correlation estimates for several signal +// shifts where the intra-shift spacing is uniform. +class MatchedFilter { + public: + // Stores properties for the lag estimate corresponding to a particular signal + // shift. + struct LagEstimate { + LagEstimate() = default; + LagEstimate(size_t lag, size_t pre_echo_lag) + : lag(lag), pre_echo_lag(pre_echo_lag) {} + size_t lag = 0; + size_t pre_echo_lag = 0; + }; + + MatchedFilter(ApmDataDumper* data_dumper, + Aec3Optimization optimization, + size_t sub_block_size, + size_t window_size_sub_blocks, + int num_matched_filters, + size_t alignment_shift_sub_blocks, + float excitation_limit, + float smoothing_fast, + float smoothing_slow, + float matching_filter_threshold, + bool detect_pre_echo); + + MatchedFilter() = delete; + MatchedFilter(const MatchedFilter&) = delete; + MatchedFilter& operator=(const MatchedFilter&) = delete; + + ~MatchedFilter(); + + // Updates the correlation with the values in the capture buffer. + void Update(const DownsampledRenderBuffer& render_buffer, + ArrayView capture, + bool use_slow_smoothing); + + // Resets the matched filter. + void Reset(bool full_reset); + + // Returns the current lag estimates. + std::optional GetBestLagEstimate() const { + return reported_lag_estimate_; + } + + // Returns the maximum filter lag. + size_t GetMaxFilterLag() const { + return filters_.size() * filter_intra_lag_shift_ + filters_[0].size(); + } + + // Log matched filter properties. + void LogFilterProperties(int sample_rate_hz, + size_t shift, + size_t downsampling_factor) const; + + private: + void Dump(); + + ApmDataDumper* const data_dumper_; + const Aec3Optimization optimization_; + const size_t sub_block_size_; + const size_t filter_intra_lag_shift_; + std::vector> filters_; + std::vector> accumulated_error_; + std::vector instantaneous_accumulated_error_; + std::vector scratch_memory_; + std::optional reported_lag_estimate_; + std::optional winner_lag_; + int last_detected_best_lag_filter_ = -1; + std::vector filters_offsets_; + int number_pre_echo_updates_ = 0; + const float excitation_limit_; + const float smoothing_fast_; + const float smoothing_slow_; + const float matching_filter_threshold_; + const bool detect_pre_echo_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc new file mode 100644 index 00000000..fd6dc5bd --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/aec3/matched_filter_lag_aggregator.h" + +#include +#include + +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { +constexpr int kPreEchoHistogramDataNotUpdated = -1; + +int GetDownSamplingBlockSizeLog2(int down_sampling_factor) { + int down_sampling_factor_log2 = 0; + down_sampling_factor >>= 1; + while (down_sampling_factor > 0) { + down_sampling_factor_log2++; + down_sampling_factor >>= 1; + } + return static_cast(kBlockSizeLog2) > down_sampling_factor_log2 + ? static_cast(kBlockSizeLog2) - down_sampling_factor_log2 + : 0; +} +} // namespace + +MatchedFilterLagAggregator::MatchedFilterLagAggregator( + ApmDataDumper* data_dumper, + size_t max_filter_lag, + const EchoCanceller3Config::Delay& delay_config) + : data_dumper_(data_dumper), + thresholds_(delay_config.delay_selection_thresholds), + headroom_(static_cast(delay_config.delay_headroom_samples / + delay_config.down_sampling_factor)), + highest_peak_aggregator_(max_filter_lag) { + if (delay_config.detect_pre_echo) { + pre_echo_lag_aggregator_ = std::make_unique( + max_filter_lag, delay_config.down_sampling_factor); + } + RTC_DCHECK(data_dumper); + RTC_DCHECK_LE(thresholds_.initial, thresholds_.converged); +} + +MatchedFilterLagAggregator::~MatchedFilterLagAggregator() = default; + +void MatchedFilterLagAggregator::Reset(bool hard_reset) { + highest_peak_aggregator_.Reset(); + if (pre_echo_lag_aggregator_ != nullptr) { + pre_echo_lag_aggregator_->Reset(); + } + if (hard_reset) { + significant_candidate_found_ = false; + } +} + +std::optional MatchedFilterLagAggregator::Aggregate( + const std::optional& lag_estimate) { + if (lag_estimate && pre_echo_lag_aggregator_) { + pre_echo_lag_aggregator_->Dump(data_dumper_); + pre_echo_lag_aggregator_->Aggregate( + std::max(0, static_cast(lag_estimate->pre_echo_lag) - headroom_)); + } + + if (lag_estimate) { + highest_peak_aggregator_.Aggregate( + std::max(0, static_cast(lag_estimate->lag) - headroom_)); + ArrayView histogram = highest_peak_aggregator_.histogram(); + int candidate = highest_peak_aggregator_.candidate(); + significant_candidate_found_ = significant_candidate_found_ || + histogram[candidate] > thresholds_.converged; + if (histogram[candidate] > thresholds_.converged || + (histogram[candidate] > thresholds_.initial && + !significant_candidate_found_)) { + DelayEstimate::Quality quality = significant_candidate_found_ + ? DelayEstimate::Quality::kRefined + : DelayEstimate::Quality::kCoarse; + int reported_delay = pre_echo_lag_aggregator_ != nullptr + ? pre_echo_lag_aggregator_->pre_echo_candidate() + : candidate; + return DelayEstimate(quality, reported_delay); + } + } + + return std::nullopt; +} + +MatchedFilterLagAggregator::HighestPeakAggregator::HighestPeakAggregator( + size_t max_filter_lag) + : histogram_(max_filter_lag + 1, 0) { + histogram_data_.fill(0); +} + +void MatchedFilterLagAggregator::HighestPeakAggregator::Reset() { + std::fill(histogram_.begin(), histogram_.end(), 0); + histogram_data_.fill(0); + histogram_data_index_ = 0; +} + +void MatchedFilterLagAggregator::HighestPeakAggregator::Aggregate(int lag) { + RTC_DCHECK_GT(histogram_.size(), histogram_data_[histogram_data_index_]); + RTC_DCHECK_LE(0, histogram_data_[histogram_data_index_]); + --histogram_[histogram_data_[histogram_data_index_]]; + histogram_data_[histogram_data_index_] = lag; + RTC_DCHECK_GT(histogram_.size(), histogram_data_[histogram_data_index_]); + RTC_DCHECK_LE(0, histogram_data_[histogram_data_index_]); + ++histogram_[histogram_data_[histogram_data_index_]]; + histogram_data_index_ = (histogram_data_index_ + 1) % histogram_data_.size(); + candidate_ = + std::distance(histogram_.begin(), + std::max_element(histogram_.begin(), histogram_.end())); +} + +MatchedFilterLagAggregator::PreEchoLagAggregator::PreEchoLagAggregator( + size_t max_filter_lag, + size_t down_sampling_factor) + : block_size_log2_(GetDownSamplingBlockSizeLog2(down_sampling_factor)), + histogram_( + ((max_filter_lag + 1) * down_sampling_factor) >> kBlockSizeLog2, + 0) { + Reset(); +} + +void MatchedFilterLagAggregator::PreEchoLagAggregator::Reset() { + std::fill(histogram_.begin(), histogram_.end(), 0); + histogram_data_.fill(kPreEchoHistogramDataNotUpdated); + histogram_data_index_ = 0; + pre_echo_candidate_ = 0; +} + +void MatchedFilterLagAggregator::PreEchoLagAggregator::Aggregate( + int pre_echo_lag) { + int pre_echo_block_size = pre_echo_lag >> block_size_log2_; + RTC_DCHECK(pre_echo_block_size >= 0 && + pre_echo_block_size < static_cast(histogram_.size())); + pre_echo_block_size = + SafeClamp(pre_echo_block_size, 0, histogram_.size() - 1); + // Remove the oldest point from the `histogram_`, it ignores the initial + // points where no updates have been done to the `histogram_data_` array. + if (histogram_data_[histogram_data_index_] != + kPreEchoHistogramDataNotUpdated) { + --histogram_[histogram_data_[histogram_data_index_]]; + } + histogram_data_[histogram_data_index_] = pre_echo_block_size; + ++histogram_[histogram_data_[histogram_data_index_]]; + histogram_data_index_ = (histogram_data_index_ + 1) % histogram_data_.size(); + int pre_echo_candidate_block_size = 0; + if (number_updates_ < kNumBlocksPerSecond * 2) { + number_updates_++; + float penalization_per_delay = 1.0f; + float max_histogram_value = -1.0f; + for (auto it = histogram_.begin(); + std::distance(it, histogram_.end()) >= + static_cast(kMatchedFilterWindowSizeSubBlocks); + it = it + kMatchedFilterWindowSizeSubBlocks) { + auto it_max_element = + std::max_element(it, it + kMatchedFilterWindowSizeSubBlocks); + float weighted_max_value = + static_cast(*it_max_element) * penalization_per_delay; + if (weighted_max_value > max_histogram_value) { + max_histogram_value = weighted_max_value; + pre_echo_candidate_block_size = + std::distance(histogram_.begin(), it_max_element); + } + penalization_per_delay *= 0.7f; + } + } else { + pre_echo_candidate_block_size = + std::distance(histogram_.begin(), + std::max_element(histogram_.begin(), histogram_.end())); + } + pre_echo_candidate_ = (pre_echo_candidate_block_size << block_size_log2_); +} + +void MatchedFilterLagAggregator::PreEchoLagAggregator::Dump( + ApmDataDumper* const data_dumper) { + data_dumper->DumpRaw("aec3_pre_echo_delay_candidate", pre_echo_candidate_); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.h new file mode 100644 index 00000000..d1b4161d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_LAG_AGGREGATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_LAG_AGGREGATOR_H_ + +#include +#include + +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/matched_filter.h" + +namespace webrtc { + +class ApmDataDumper; + +// Aggregates lag estimates produced by the MatchedFilter class into a single +// reliable combined lag estimate. +class MatchedFilterLagAggregator { + public: + MatchedFilterLagAggregator(ApmDataDumper* data_dumper, + size_t max_filter_lag, + const EchoCanceller3Config::Delay& delay_config); + + MatchedFilterLagAggregator() = delete; + MatchedFilterLagAggregator(const MatchedFilterLagAggregator&) = delete; + MatchedFilterLagAggregator& operator=(const MatchedFilterLagAggregator&) = + delete; + + ~MatchedFilterLagAggregator(); + + // Resets the aggregator. + void Reset(bool hard_reset); + + // Aggregates the provided lag estimates. + std::optional Aggregate( + const std::optional& lag_estimate); + + // Returns whether a reliable delay estimate has been found. + bool ReliableDelayFound() const { return significant_candidate_found_; } + + // Returns the delay candidate that is computed by looking at the highest peak + // on the matched filters. + int GetDelayAtHighestPeak() const { + return highest_peak_aggregator_.candidate(); + } + + private: + class PreEchoLagAggregator { + public: + PreEchoLagAggregator(size_t max_filter_lag, size_t down_sampling_factor); + void Reset(); + void Aggregate(int pre_echo_lag); + int pre_echo_candidate() const { return pre_echo_candidate_; } + void Dump(ApmDataDumper* const data_dumper); + + private: + const int block_size_log2_; + std::array histogram_data_; + std::vector histogram_; + int histogram_data_index_ = 0; + int pre_echo_candidate_ = 0; + int number_updates_ = 0; + }; + + class HighestPeakAggregator { + public: + explicit HighestPeakAggregator(size_t max_filter_lag); + void Reset(); + void Aggregate(int lag); + int candidate() const { return candidate_; } + ArrayView histogram() const { return histogram_; } + + private: + std::vector histogram_; + std::array histogram_data_; + int histogram_data_index_ = 0; + int candidate_ = -1; + }; + + ApmDataDumper* const data_dumper_; + bool significant_candidate_found_ = false; + const EchoCanceller3Config::Delay::DelaySelectionThresholds thresholds_; + const int headroom_; + HighestPeakAggregator highest_peak_aggregator_; + std::unique_ptr pre_echo_lag_aggregator_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_LAG_AGGREGATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/moving_average.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/moving_average.cc new file mode 100644 index 00000000..f89789ec --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/moving_average.cc @@ -0,0 +1,60 @@ + +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/moving_average.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace aec3 { + +MovingAverage::MovingAverage(size_t num_elem, size_t mem_len) + : num_elem_(num_elem), + mem_len_(mem_len - 1), + scaling_(1.0f / static_cast(mem_len)), + memory_(num_elem * mem_len_, 0.f), + mem_index_(0) { + RTC_DCHECK(num_elem_ > 0); + RTC_DCHECK(mem_len > 0); +} + +MovingAverage::~MovingAverage() = default; + +void MovingAverage::Average(ArrayView input, + ArrayView output) { + RTC_DCHECK(input.size() == num_elem_); + RTC_DCHECK(output.size() == num_elem_); + + // Sum all contributions. + std::copy(input.begin(), input.end(), output.begin()); + for (auto i = memory_.begin(); i < memory_.end(); i += num_elem_) { + std::transform(i, i + num_elem_, output.begin(), output.begin(), + std::plus()); + } + + // Divide by mem_len_. + for (float& o : output) { + o *= scaling_; + } + + // Update memory. + if (mem_len_ > 0) { + std::copy(input.begin(), input.end(), + memory_.begin() + mem_index_ * num_elem_); + mem_index_ = (mem_index_ + 1) % mem_len_; + } +} + +} // namespace aec3 +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/moving_average.h b/pkg/apm/webrtc/modules/audio_processing/aec3/moving_average.h new file mode 100644 index 00000000..68f2fd0f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/moving_average.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_H_ + +#include + +#include + +#include "api/array_view.h" + +namespace webrtc { +namespace aec3 { + +class MovingAverage { + public: + // Creates an instance of MovingAverage that accepts inputs of length num_elem + // and averages over mem_len inputs. + MovingAverage(size_t num_elem, size_t mem_len); + ~MovingAverage(); + + // Computes the average of input and mem_len-1 previous inputs and stores the + // result in output. + void Average(ArrayView input, ArrayView output); + + private: + const size_t num_elem_; + const size_t mem_len_; + const float scaling_; + std::vector memory_; + size_t mem_index_; +}; + +} // namespace aec3 +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/multi_channel_content_detector.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/multi_channel_content_detector.cc new file mode 100644 index 00000000..2b07585c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/multi_channel_content_detector.cc @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/multi_channel_content_detector.h" + +#include + +#include "rtc_base/checks.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +namespace { + +constexpr int kNumFramesPerSecond = 100; + +// Compares the left and right channels in the render `frame` to determine +// whether the signal is a proper stereo signal. To allow for differences +// introduced by hardware drivers, a threshold `detection_threshold` is used for +// the detection. +bool HasStereoContent(const std::vector>>& frame, + float detection_threshold) { + if (frame[0].size() < 2) { + return false; + } + + for (size_t band = 0; band < frame.size(); ++band) { + for (size_t k = 0; k < frame[band][0].size(); ++k) { + if (std::fabs(frame[band][0][k] - frame[band][1][k]) > + detection_threshold) { + return true; + } + } + } + return false; +} + +// In order to avoid logging metrics for very short lifetimes that are unlikely +// to reflect real calls and that may dilute the "real" data, logging is limited +// to lifetimes of at leats 5 seconds. +constexpr int kMinNumberOfFramesRequiredToLogMetrics = 500; + +// Continuous metrics are logged every 10 seconds. +constexpr int kFramesPer10Seconds = 1000; + +} // namespace + +MultiChannelContentDetector::MetricsLogger::MetricsLogger() {} + +MultiChannelContentDetector::MetricsLogger::~MetricsLogger() { + if (frame_counter_ < kMinNumberOfFramesRequiredToLogMetrics) + return; + + RTC_HISTOGRAM_BOOLEAN( + "WebRTC.Audio.EchoCanceller.PersistentMultichannelContentEverDetected", + any_multichannel_content_detected_ ? 1 : 0); +} + +void MultiChannelContentDetector::MetricsLogger::Update( + bool persistent_multichannel_content_detected) { + ++frame_counter_; + if (persistent_multichannel_content_detected) { + any_multichannel_content_detected_ = true; + ++persistent_multichannel_frame_counter_; + } + + if (frame_counter_ < kMinNumberOfFramesRequiredToLogMetrics) + return; + if (frame_counter_ % kFramesPer10Seconds != 0) + return; + const bool mostly_multichannel_last_10_seconds = + (persistent_multichannel_frame_counter_ >= kFramesPer10Seconds / 2); + RTC_HISTOGRAM_BOOLEAN( + "WebRTC.Audio.EchoCanceller.ProcessingPersistentMultichannelContent", + mostly_multichannel_last_10_seconds ? 1 : 0); + + persistent_multichannel_frame_counter_ = 0; +} + +MultiChannelContentDetector::MultiChannelContentDetector( + bool detect_stereo_content, + int num_render_input_channels, + float detection_threshold, + int stereo_detection_timeout_threshold_seconds, + float stereo_detection_hysteresis_seconds) + : detect_stereo_content_(detect_stereo_content), + detection_threshold_(detection_threshold), + detection_timeout_threshold_frames_( + stereo_detection_timeout_threshold_seconds > 0 + ? std::make_optional(stereo_detection_timeout_threshold_seconds * + kNumFramesPerSecond) + : std::nullopt), + stereo_detection_hysteresis_frames_(static_cast( + stereo_detection_hysteresis_seconds * kNumFramesPerSecond)), + metrics_logger_((detect_stereo_content && num_render_input_channels > 1) + ? std::make_unique() + : nullptr), + persistent_multichannel_content_detected_( + !detect_stereo_content && num_render_input_channels > 1) {} + +bool MultiChannelContentDetector::UpdateDetection( + const std::vector>>& frame) { + if (!detect_stereo_content_) { + RTC_DCHECK_EQ(frame[0].size() > 1, + persistent_multichannel_content_detected_); + return false; + } + + const bool previous_persistent_multichannel_content_detected = + persistent_multichannel_content_detected_; + const bool stereo_detected_in_frame = + HasStereoContent(frame, detection_threshold_); + + consecutive_frames_with_stereo_ = + stereo_detected_in_frame ? consecutive_frames_with_stereo_ + 1 : 0; + frames_since_stereo_detected_last_ = + stereo_detected_in_frame ? 0 : frames_since_stereo_detected_last_ + 1; + + // Detect persistent multichannel content. + if (consecutive_frames_with_stereo_ > stereo_detection_hysteresis_frames_) { + persistent_multichannel_content_detected_ = true; + } + if (detection_timeout_threshold_frames_.has_value() && + frames_since_stereo_detected_last_ >= + *detection_timeout_threshold_frames_) { + persistent_multichannel_content_detected_ = false; + } + + // Detect temporary multichannel content. + temporary_multichannel_content_detected_ = + persistent_multichannel_content_detected_ ? false + : stereo_detected_in_frame; + + if (metrics_logger_) + metrics_logger_->Update(persistent_multichannel_content_detected_); + + return previous_persistent_multichannel_content_detected != + persistent_multichannel_content_detected_; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/multi_channel_content_detector.h b/pkg/apm/webrtc/modules/audio_processing/aec3/multi_channel_content_detector.h new file mode 100644 index 00000000..57f49a49 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/multi_channel_content_detector.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_MULTI_CHANNEL_CONTENT_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_MULTI_CHANNEL_CONTENT_DETECTOR_H_ + +#include +#include + +#include +#include +#include + +namespace webrtc { + +// Analyzes audio content to determine whether the contained audio is proper +// multichannel, or only upmixed mono. To allow for differences introduced by +// hardware drivers, a threshold `detection_threshold` is used for the +// detection. +// Logs metrics continously and upon destruction. +class MultiChannelContentDetector { + public: + // If |stereo_detection_timeout_threshold_seconds| <= 0, no timeout is + // applied: Once multichannel is detected, the detector remains in that state + // for its lifetime. + MultiChannelContentDetector(bool detect_stereo_content, + int num_render_input_channels, + float detection_threshold, + int stereo_detection_timeout_threshold_seconds, + float stereo_detection_hysteresis_seconds); + + // Compares the left and right channels in the render `frame` to determine + // whether the signal is a proper multichannel signal. Returns a bool + // indicating whether a change in the proper multichannel content was + // detected. + bool UpdateDetection( + const std::vector>>& frame); + + bool IsProperMultiChannelContentDetected() const { + return persistent_multichannel_content_detected_; + } + + bool IsTemporaryMultiChannelContentDetected() const { + return temporary_multichannel_content_detected_; + } + + private: + // Tracks and logs metrics for the amount of multichannel content detected. + class MetricsLogger { + public: + MetricsLogger(); + + // The destructor logs call summary statistics. + ~MetricsLogger(); + + // Updates and logs metrics. + void Update(bool persistent_multichannel_content_detected); + + private: + int frame_counter_ = 0; + + // Counts the number of frames of persistent multichannel audio observed + // during the current metrics collection interval. + int persistent_multichannel_frame_counter_ = 0; + + // Indicates whether persistent multichannel content has ever been detected. + bool any_multichannel_content_detected_ = false; + }; + + const bool detect_stereo_content_; + const float detection_threshold_; + const std::optional detection_timeout_threshold_frames_; + const int stereo_detection_hysteresis_frames_; + + // Collects and reports metrics on the amount of multichannel content + // detected. Only created if |num_render_input_channels| > 1 and + // |detect_stereo_content_| is true. + const std::unique_ptr metrics_logger_; + + bool persistent_multichannel_content_detected_; + bool temporary_multichannel_content_detected_ = false; + int64_t frames_since_stereo_detected_last_ = 0; + int64_t consecutive_frames_with_stereo_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_MULTI_CHANNEL_CONTENT_DETECTOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/nearend_detector.h b/pkg/apm/webrtc/modules/audio_processing/aec3/nearend_detector.h new file mode 100644 index 00000000..72e798a1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/nearend_detector.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_NEAREND_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_NEAREND_DETECTOR_H_ + +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { +// Class for selecting whether the suppressor is in the nearend or echo state. +class NearendDetector { + public: + virtual ~NearendDetector() {} + + // Returns whether the current state is the nearend state. + virtual bool IsNearendState() const = 0; + + // Updates the state selection based on latest spectral estimates. + virtual void Update( + ArrayView> nearend_spectrum, + ArrayView> + residual_echo_spectrum, + ArrayView> + comfort_noise_spectrum, + bool initial_state) = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_NEAREND_DETECTOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.cc new file mode 100644 index 00000000..7d32bae4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/refined_filter_update_gain.h" + +#include +#include + +#include "modules/audio_processing/aec3/adaptive_fir_filter.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/render_signal_analyzer.h" +#include "modules/audio_processing/aec3/subtractor_output.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr float kHErrorInitial = 10000.f; +constexpr int kPoorExcitationCounterInitial = 1000; + +} // namespace + +std::atomic RefinedFilterUpdateGain::instance_count_(0); + +RefinedFilterUpdateGain::RefinedFilterUpdateGain( + const EchoCanceller3Config::Filter::RefinedConfiguration& config, + size_t config_change_duration_blocks) + : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), + config_change_duration_blocks_( + static_cast(config_change_duration_blocks)), + poor_excitation_counter_(kPoorExcitationCounterInitial) { + SetConfig(config, true); + H_error_.fill(kHErrorInitial); + RTC_DCHECK_LT(0, config_change_duration_blocks_); + one_by_config_change_duration_blocks_ = 1.f / config_change_duration_blocks_; +} + +RefinedFilterUpdateGain::~RefinedFilterUpdateGain() {} + +void RefinedFilterUpdateGain::HandleEchoPathChange( + const EchoPathVariability& echo_path_variability) { + if (echo_path_variability.gain_change) { + // TODO(bugs.webrtc.org/9526) Handle gain changes. + } + + if (echo_path_variability.delay_change != + EchoPathVariability::DelayAdjustment::kNone) { + H_error_.fill(kHErrorInitial); + } + + if (!echo_path_variability.gain_change) { + poor_excitation_counter_ = kPoorExcitationCounterInitial; + call_counter_ = 0; + } +} + +void RefinedFilterUpdateGain::Compute( + const std::array& render_power, + const RenderSignalAnalyzer& render_signal_analyzer, + const SubtractorOutput& subtractor_output, + ArrayView erl, + size_t size_partitions, + bool saturated_capture_signal, + bool disallow_leakage_diverged, + FftData* gain_fft) { + RTC_DCHECK(gain_fft); + // Introducing shorter notation to improve readability. + const FftData& E_refined = subtractor_output.E_refined; + const auto& E2_refined = subtractor_output.E2_refined; + const auto& E2_coarse = subtractor_output.E2_coarse; + FftData* G = gain_fft; + const auto& X2 = render_power; + + ++call_counter_; + + UpdateCurrentConfig(); + + if (render_signal_analyzer.PoorSignalExcitation()) { + poor_excitation_counter_ = 0; + } + + // Do not update the filter if the render is not sufficiently excited. + if (++poor_excitation_counter_ < size_partitions || + saturated_capture_signal || call_counter_ <= size_partitions) { + G->re.fill(0.f); + G->im.fill(0.f); + } else { + // Corresponds to WGN of power -39 dBFS. + std::array mu; + // mu = H_error / (0.5* H_error* X2 + n * E2). + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + if (X2[k] >= current_config_.noise_gate) { + mu[k] = H_error_[k] / + (0.5f * H_error_[k] * X2[k] + size_partitions * E2_refined[k]); + } else { + mu[k] = 0.f; + } + } + + // Avoid updating the filter close to narrow bands in the render signals. + render_signal_analyzer.MaskRegionsAroundNarrowBands(&mu); + + // H_error = H_error - 0.5 * mu * X2 * H_error. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + H_error_[k] -= 0.5f * mu[k] * X2[k] * H_error_[k]; + } + + // G = mu * E. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + G->re[k] = mu[k] * E_refined.re[k]; + G->im[k] = mu[k] * E_refined.im[k]; + } + } + + // H_error = H_error + factor * erl. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + if (E2_refined[k] <= E2_coarse[k] || disallow_leakage_diverged) { + H_error_[k] += current_config_.leakage_converged * erl[k]; + } else { + H_error_[k] += current_config_.leakage_diverged * erl[k]; + } + + H_error_[k] = std::max(H_error_[k], current_config_.error_floor); + H_error_[k] = std::min(H_error_[k], current_config_.error_ceil); + } + + data_dumper_->DumpRaw("aec3_refined_gain_H_error", H_error_); +} + +void RefinedFilterUpdateGain::UpdateCurrentConfig() { + RTC_DCHECK_GE(config_change_duration_blocks_, config_change_counter_); + if (config_change_counter_ > 0) { + if (--config_change_counter_ > 0) { + auto average = [](float from, float to, float from_weight) { + return from * from_weight + to * (1.f - from_weight); + }; + + float change_factor = + config_change_counter_ * one_by_config_change_duration_blocks_; + + current_config_.leakage_converged = + average(old_target_config_.leakage_converged, + target_config_.leakage_converged, change_factor); + current_config_.leakage_diverged = + average(old_target_config_.leakage_diverged, + target_config_.leakage_diverged, change_factor); + current_config_.error_floor = + average(old_target_config_.error_floor, target_config_.error_floor, + change_factor); + current_config_.error_ceil = + average(old_target_config_.error_ceil, target_config_.error_ceil, + change_factor); + current_config_.noise_gate = + average(old_target_config_.noise_gate, target_config_.noise_gate, + change_factor); + } else { + current_config_ = old_target_config_ = target_config_; + } + } + RTC_DCHECK_LE(0, config_change_counter_); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.h b/pkg/apm/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.h new file mode 100644 index 00000000..660a0137 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_REFINED_FILTER_UPDATE_GAIN_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_REFINED_FILTER_UPDATE_GAIN_H_ + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +class AdaptiveFirFilter; +class ApmDataDumper; +struct EchoPathVariability; +struct FftData; +class RenderSignalAnalyzer; +struct SubtractorOutput; + +// Provides functionality for computing the adaptive gain for the refined +// filter. +class RefinedFilterUpdateGain { + public: + RefinedFilterUpdateGain( + const EchoCanceller3Config::Filter::RefinedConfiguration& config, + size_t config_change_duration_blocks); + ~RefinedFilterUpdateGain(); + + RefinedFilterUpdateGain(const RefinedFilterUpdateGain&) = delete; + RefinedFilterUpdateGain& operator=(const RefinedFilterUpdateGain&) = delete; + + // Takes action in the case of a known echo path change. + void HandleEchoPathChange(const EchoPathVariability& echo_path_variability); + + // Computes the gain. + void Compute(const std::array& render_power, + const RenderSignalAnalyzer& render_signal_analyzer, + const SubtractorOutput& subtractor_output, + ArrayView erl, + size_t size_partitions, + bool saturated_capture_signal, + bool disallow_leakage_diverged, + FftData* gain_fft); + + // Sets a new config. + void SetConfig( + const EchoCanceller3Config::Filter::RefinedConfiguration& config, + bool immediate_effect) { + if (immediate_effect) { + old_target_config_ = current_config_ = target_config_ = config; + config_change_counter_ = 0; + } else { + old_target_config_ = current_config_; + target_config_ = config; + config_change_counter_ = config_change_duration_blocks_; + } + } + + private: + static std::atomic instance_count_; + std::unique_ptr data_dumper_; + const int config_change_duration_blocks_; + float one_by_config_change_duration_blocks_; + EchoCanceller3Config::Filter::RefinedConfiguration current_config_; + EchoCanceller3Config::Filter::RefinedConfiguration target_config_; + EchoCanceller3Config::Filter::RefinedConfiguration old_target_config_; + std::array H_error_; + size_t poor_excitation_counter_; + size_t call_counter_ = 0; + int config_change_counter_ = 0; + + // Updates the current config towards the target config. + void UpdateCurrentConfig(); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_REFINED_FILTER_UPDATE_GAIN_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/render_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/render_buffer.cc new file mode 100644 index 00000000..aa511e2b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/render_buffer.cc @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/render_buffer.h" + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +RenderBuffer::RenderBuffer(BlockBuffer* block_buffer, + SpectrumBuffer* spectrum_buffer, + FftBuffer* fft_buffer) + : block_buffer_(block_buffer), + spectrum_buffer_(spectrum_buffer), + fft_buffer_(fft_buffer) { + RTC_DCHECK(block_buffer_); + RTC_DCHECK(spectrum_buffer_); + RTC_DCHECK(fft_buffer_); + RTC_DCHECK_EQ(block_buffer_->buffer.size(), fft_buffer_->buffer.size()); + RTC_DCHECK_EQ(spectrum_buffer_->buffer.size(), fft_buffer_->buffer.size()); + RTC_DCHECK_EQ(spectrum_buffer_->read, fft_buffer_->read); + RTC_DCHECK_EQ(spectrum_buffer_->write, fft_buffer_->write); +} + +RenderBuffer::~RenderBuffer() = default; + +void RenderBuffer::SpectralSum( + size_t num_spectra, + std::array* X2) const { + X2->fill(0.f); + int position = spectrum_buffer_->read; + for (size_t j = 0; j < num_spectra; ++j) { + for (const auto& channel_spectrum : spectrum_buffer_->buffer[position]) { + for (size_t k = 0; k < X2->size(); ++k) { + (*X2)[k] += channel_spectrum[k]; + } + } + position = spectrum_buffer_->IncIndex(position); + } +} + +void RenderBuffer::SpectralSums( + size_t num_spectra_shorter, + size_t num_spectra_longer, + std::array* X2_shorter, + std::array* X2_longer) const { + RTC_DCHECK_LE(num_spectra_shorter, num_spectra_longer); + X2_shorter->fill(0.f); + int position = spectrum_buffer_->read; + size_t j = 0; + for (; j < num_spectra_shorter; ++j) { + for (const auto& channel_spectrum : spectrum_buffer_->buffer[position]) { + for (size_t k = 0; k < X2_shorter->size(); ++k) { + (*X2_shorter)[k] += channel_spectrum[k]; + } + } + position = spectrum_buffer_->IncIndex(position); + } + std::copy(X2_shorter->begin(), X2_shorter->end(), X2_longer->begin()); + for (; j < num_spectra_longer; ++j) { + for (const auto& channel_spectrum : spectrum_buffer_->buffer[position]) { + for (size_t k = 0; k < X2_longer->size(); ++k) { + (*X2_longer)[k] += channel_spectrum[k]; + } + } + position = spectrum_buffer_->IncIndex(position); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/render_buffer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/render_buffer.h new file mode 100644 index 00000000..e70d4821 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/render_buffer.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RENDER_BUFFER_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/block_buffer.h" +#include "modules/audio_processing/aec3/fft_buffer.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Provides a buffer of the render data for the echo remover. +class RenderBuffer { + public: + RenderBuffer(BlockBuffer* block_buffer, + SpectrumBuffer* spectrum_buffer, + FftBuffer* fft_buffer); + + RenderBuffer() = delete; + RenderBuffer(const RenderBuffer&) = delete; + RenderBuffer& operator=(const RenderBuffer&) = delete; + + ~RenderBuffer(); + + // Get a block. + const Block& GetBlock(int buffer_offset_blocks) const { + int position = + block_buffer_->OffsetIndex(block_buffer_->read, buffer_offset_blocks); + return block_buffer_->buffer[position]; + } + + // Get the spectrum from one of the FFTs in the buffer. + ArrayView> Spectrum( + int buffer_offset_ffts) const { + int position = spectrum_buffer_->OffsetIndex(spectrum_buffer_->read, + buffer_offset_ffts); + return spectrum_buffer_->buffer[position]; + } + + // Returns the circular fft buffer. + ArrayView> GetFftBuffer() const { + return fft_buffer_->buffer; + } + + // Returns the current position in the circular buffer. + size_t Position() const { + RTC_DCHECK_EQ(spectrum_buffer_->read, fft_buffer_->read); + RTC_DCHECK_EQ(spectrum_buffer_->write, fft_buffer_->write); + return fft_buffer_->read; + } + + // Returns the sum of the spectrums for a certain number of FFTs. + void SpectralSum(size_t num_spectra, + std::array* X2) const; + + // Returns the sums of the spectrums for two numbers of FFTs. + void SpectralSums(size_t num_spectra_shorter, + size_t num_spectra_longer, + std::array* X2_shorter, + std::array* X2_longer) const; + + // Gets the recent activity seen in the render signal. + bool GetRenderActivity() const { return render_activity_; } + + // Specifies the recent activity seen in the render signal. + void SetRenderActivity(bool activity) { render_activity_ = activity; } + + // Returns the headroom between the write and the read positions in the + // buffer. + int Headroom() const { + // The write and read indices are decreased over time. + int headroom = + fft_buffer_->write < fft_buffer_->read + ? fft_buffer_->read - fft_buffer_->write + : fft_buffer_->size - fft_buffer_->write + fft_buffer_->read; + + RTC_DCHECK_LE(0, headroom); + RTC_DCHECK_GE(fft_buffer_->size, headroom); + + return headroom; + } + + // Returns a reference to the spectrum buffer. + const SpectrumBuffer& GetSpectrumBuffer() const { return *spectrum_buffer_; } + + // Returns a reference to the block buffer. + const BlockBuffer& GetBlockBuffer() const { return *block_buffer_; } + + private: + const BlockBuffer* const block_buffer_; + const SpectrumBuffer* const spectrum_buffer_; + const FftBuffer* const fft_buffer_; + bool render_activity_ = false; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RENDER_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_buffer.cc new file mode 100644 index 00000000..37a7801c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_buffer.cc @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/render_delay_buffer.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec3_fft.h" +#include "modules/audio_processing/aec3/alignment_mixer.h" +#include "modules/audio_processing/aec3/block_buffer.h" +#include "modules/audio_processing/aec3/decimator.h" +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/aec3/fft_buffer.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace { + +class RenderDelayBufferImpl final : public RenderDelayBuffer { + public: + RenderDelayBufferImpl(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels); + RenderDelayBufferImpl() = delete; + ~RenderDelayBufferImpl() override; + + void Reset() override; + BufferingEvent Insert(const Block& block) override; + BufferingEvent PrepareCaptureProcessing() override; + void HandleSkippedCaptureProcessing() override; + bool AlignFromDelay(size_t delay) override; + void AlignFromExternalDelay() override; + size_t Delay() const override { return ComputeDelay(); } + size_t MaxDelay() const override { + return blocks_.buffer.size() - 1 - buffer_headroom_; + } + RenderBuffer* GetRenderBuffer() override { return &echo_remover_buffer_; } + + const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const override { + return low_rate_; + } + + int BufferLatency() const; + void SetAudioBufferDelay(int delay_ms) override; + bool HasReceivedBufferDelay() override; + + private: + static std::atomic instance_count_; + std::unique_ptr data_dumper_; + const Aec3Optimization optimization_; + const EchoCanceller3Config config_; + const float render_linear_amplitude_gain_; + const LoggingSeverity delay_log_level_; + size_t down_sampling_factor_; + const int sub_block_size_; + BlockBuffer blocks_; + SpectrumBuffer spectra_; + FftBuffer ffts_; + std::optional delay_; + RenderBuffer echo_remover_buffer_; + DownsampledRenderBuffer low_rate_; + AlignmentMixer render_mixer_; + Decimator render_decimator_; + const Aec3Fft fft_; + std::vector render_ds_; + const int buffer_headroom_; + bool last_call_was_render_ = false; + int num_api_calls_in_a_row_ = 0; + int max_observed_jitter_ = 1; + int64_t capture_call_counter_ = 0; + int64_t render_call_counter_ = 0; + bool render_activity_ = false; + size_t render_activity_counter_ = 0; + std::optional external_audio_buffer_delay_; + bool external_audio_buffer_delay_verified_after_reset_ = false; + size_t min_latency_blocks_ = 0; + size_t excess_render_detection_counter_ = 0; + + int MapDelayToTotalDelay(size_t delay) const; + int ComputeDelay() const; + void ApplyTotalDelay(int delay); + void InsertBlock(const Block& block, int previous_write); + bool DetectActiveRender(ArrayView x) const; + bool DetectExcessRenderBlocks(); + void IncrementWriteIndices(); + void IncrementLowRateReadIndices(); + void IncrementReadIndices(); + bool RenderOverrun(); + bool RenderUnderrun(); +}; + +std::atomic RenderDelayBufferImpl::instance_count_ = 0; + +RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels) + : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), + optimization_(DetectOptimization()), + config_(config), + render_linear_amplitude_gain_( + std::pow(10.0f, config_.render_levels.render_power_gain_db / 20.f)), + delay_log_level_(config_.delay.log_warning_on_delay_changes ? LS_WARNING + : LS_VERBOSE), + down_sampling_factor_(config.delay.down_sampling_factor), + sub_block_size_(static_cast(down_sampling_factor_ > 0 + ? kBlockSize / down_sampling_factor_ + : kBlockSize)), + blocks_(GetRenderDelayBufferSize(down_sampling_factor_, + config.delay.num_filters, + config.filter.refined.length_blocks), + NumBandsForRate(sample_rate_hz), + num_render_channels), + spectra_(blocks_.buffer.size(), num_render_channels), + ffts_(blocks_.buffer.size(), num_render_channels), + delay_(config_.delay.default_delay), + echo_remover_buffer_(&blocks_, &spectra_, &ffts_), + low_rate_(GetDownSampledBufferSize(down_sampling_factor_, + config.delay.num_filters)), + render_mixer_(num_render_channels, config.delay.render_alignment_mixing), + render_decimator_(down_sampling_factor_), + fft_(), + render_ds_(sub_block_size_, 0.f), + buffer_headroom_(config.filter.refined.length_blocks) { + RTC_DCHECK_EQ(blocks_.buffer.size(), ffts_.buffer.size()); + RTC_DCHECK_EQ(spectra_.buffer.size(), ffts_.buffer.size()); + for (size_t i = 0; i < blocks_.buffer.size(); ++i) { + RTC_DCHECK_EQ(blocks_.buffer[i].NumChannels(), ffts_.buffer[i].size()); + RTC_DCHECK_EQ(spectra_.buffer[i].size(), ffts_.buffer[i].size()); + } + + Reset(); +} + +RenderDelayBufferImpl::~RenderDelayBufferImpl() = default; + +// Resets the buffer delays and clears the reported delays. +void RenderDelayBufferImpl::Reset() { + last_call_was_render_ = false; + num_api_calls_in_a_row_ = 1; + min_latency_blocks_ = 0; + excess_render_detection_counter_ = 0; + + // Initialize the read index to one sub-block before the write index. + low_rate_.read = low_rate_.OffsetIndex(low_rate_.write, sub_block_size_); + + // Check for any external audio buffer delay and whether it is feasible. + if (external_audio_buffer_delay_) { + const int headroom = 2; + size_t audio_buffer_delay_to_set; + // Minimum delay is 1 (like the low-rate render buffer). + if (*external_audio_buffer_delay_ <= headroom) { + audio_buffer_delay_to_set = 1; + } else { + audio_buffer_delay_to_set = *external_audio_buffer_delay_ - headroom; + } + + audio_buffer_delay_to_set = std::min(audio_buffer_delay_to_set, MaxDelay()); + + // When an external delay estimate is available, use that delay as the + // initial render buffer delay. + ApplyTotalDelay(audio_buffer_delay_to_set); + delay_ = ComputeDelay(); + + external_audio_buffer_delay_verified_after_reset_ = false; + } else { + // If an external delay estimate is not available, use that delay as the + // initial delay. Set the render buffer delays to the default delay. + ApplyTotalDelay(config_.delay.default_delay); + + // Unset the delays which are set by AlignFromDelay. + delay_ = std::nullopt; + } +} + +// Inserts a new block into the render buffers. +RenderDelayBuffer::BufferingEvent RenderDelayBufferImpl::Insert( + const Block& block) { + ++render_call_counter_; + if (delay_) { + if (!last_call_was_render_) { + last_call_was_render_ = true; + num_api_calls_in_a_row_ = 1; + } else { + if (++num_api_calls_in_a_row_ > max_observed_jitter_) { + max_observed_jitter_ = num_api_calls_in_a_row_; + RTC_LOG_V(delay_log_level_) + << "New max number api jitter observed at render block " + << render_call_counter_ << ": " << num_api_calls_in_a_row_ + << " blocks"; + } + } + } + + // Increase the write indices to where the new blocks should be written. + const int previous_write = blocks_.write; + IncrementWriteIndices(); + + // Allow overrun and do a reset when render overrun occurrs due to more render + // data being inserted than capture data is received. + BufferingEvent event = + RenderOverrun() ? BufferingEvent::kRenderOverrun : BufferingEvent::kNone; + + // Detect and update render activity. + if (!render_activity_) { + render_activity_counter_ += + DetectActiveRender(block.View(/*band=*/0, /*channel=*/0)) ? 1 : 0; + render_activity_ = render_activity_counter_ >= 20; + } + + // Insert the new render block into the specified position. + InsertBlock(block, previous_write); + + if (event != BufferingEvent::kNone) { + Reset(); + } + + return event; +} + +void RenderDelayBufferImpl::HandleSkippedCaptureProcessing() { + ++capture_call_counter_; +} + +// Prepares the render buffers for processing another capture block. +RenderDelayBuffer::BufferingEvent +RenderDelayBufferImpl::PrepareCaptureProcessing() { + RenderDelayBuffer::BufferingEvent event = BufferingEvent::kNone; + ++capture_call_counter_; + + if (delay_) { + if (last_call_was_render_) { + last_call_was_render_ = false; + num_api_calls_in_a_row_ = 1; + } else { + if (++num_api_calls_in_a_row_ > max_observed_jitter_) { + max_observed_jitter_ = num_api_calls_in_a_row_; + RTC_LOG_V(delay_log_level_) + << "New max number api jitter observed at capture block " + << capture_call_counter_ << ": " << num_api_calls_in_a_row_ + << " blocks"; + } + } + } + + if (DetectExcessRenderBlocks()) { + // Too many render blocks compared to capture blocks. Risk of delay ending + // up before the filter used by the delay estimator. + RTC_LOG_V(delay_log_level_) + << "Excess render blocks detected at block " << capture_call_counter_; + Reset(); + event = BufferingEvent::kRenderOverrun; + } else if (RenderUnderrun()) { + // Don't increment the read indices of the low rate buffer if there is a + // render underrun. + RTC_LOG_V(delay_log_level_) + << "Render buffer underrun detected at block " << capture_call_counter_; + IncrementReadIndices(); + // Incrementing the buffer index without increasing the low rate buffer + // index means that the delay is reduced by one. + if (delay_ && *delay_ > 0) + delay_ = *delay_ - 1; + event = BufferingEvent::kRenderUnderrun; + } else { + // Increment the read indices in the render buffers to point to the most + // recent block to use in the capture processing. + IncrementLowRateReadIndices(); + IncrementReadIndices(); + } + + echo_remover_buffer_.SetRenderActivity(render_activity_); + if (render_activity_) { + render_activity_counter_ = 0; + render_activity_ = false; + } + + return event; +} + +// Sets the delay and returns a bool indicating whether the delay was changed. +bool RenderDelayBufferImpl::AlignFromDelay(size_t delay) { + RTC_DCHECK(!config_.delay.use_external_delay_estimator); + if (!external_audio_buffer_delay_verified_after_reset_ && + external_audio_buffer_delay_ && delay_) { + int difference = static_cast(delay) - static_cast(*delay_); + RTC_LOG_V(delay_log_level_) + << "Mismatch between first estimated delay after reset " + "and externally reported audio buffer delay: " + << difference << " blocks"; + external_audio_buffer_delay_verified_after_reset_ = true; + } + if (delay_ && *delay_ == delay) { + return false; + } + delay_ = delay; + + // Compute the total delay and limit the delay to the allowed range. + int total_delay = MapDelayToTotalDelay(*delay_); + total_delay = + std::min(MaxDelay(), static_cast(std::max(total_delay, 0))); + + // Apply the delay to the buffers. + ApplyTotalDelay(total_delay); + return true; +} + +void RenderDelayBufferImpl::SetAudioBufferDelay(int delay_ms) { + if (!external_audio_buffer_delay_) { + RTC_LOG_V(delay_log_level_) + << "Receiving a first externally reported audio buffer delay of " + << delay_ms << " ms."; + } + + // Convert delay from milliseconds to blocks (rounded down). + external_audio_buffer_delay_ = delay_ms / 4; +} + +bool RenderDelayBufferImpl::HasReceivedBufferDelay() { + return external_audio_buffer_delay_.has_value(); +} + +// Maps the externally computed delay to the delay used internally. +int RenderDelayBufferImpl::MapDelayToTotalDelay( + size_t external_delay_blocks) const { + const int latency_blocks = BufferLatency(); + return latency_blocks + static_cast(external_delay_blocks); +} + +// Returns the delay (not including call jitter). +int RenderDelayBufferImpl::ComputeDelay() const { + const int latency_blocks = BufferLatency(); + int internal_delay = spectra_.read >= spectra_.write + ? spectra_.read - spectra_.write + : spectra_.size + spectra_.read - spectra_.write; + + return internal_delay - latency_blocks; +} + +// Set the read indices according to the delay. +void RenderDelayBufferImpl::ApplyTotalDelay(int delay) { + RTC_LOG_V(delay_log_level_) + << "Applying total delay of " << delay << " blocks."; + blocks_.read = blocks_.OffsetIndex(blocks_.write, -delay); + spectra_.read = spectra_.OffsetIndex(spectra_.write, delay); + ffts_.read = ffts_.OffsetIndex(ffts_.write, delay); +} + +void RenderDelayBufferImpl::AlignFromExternalDelay() { + RTC_DCHECK(config_.delay.use_external_delay_estimator); + if (external_audio_buffer_delay_) { + const int64_t delay = render_call_counter_ - capture_call_counter_ + + *external_audio_buffer_delay_; + const int64_t delay_with_headroom = + delay - config_.delay.delay_headroom_samples / kBlockSize; + ApplyTotalDelay(delay_with_headroom); + } +} + +// Inserts a block into the render buffers. +void RenderDelayBufferImpl::InsertBlock(const Block& block, + int previous_write) { + auto& b = blocks_; + auto& lr = low_rate_; + auto& ds = render_ds_; + auto& f = ffts_; + auto& s = spectra_; + const size_t num_bands = b.buffer[b.write].NumBands(); + const size_t num_render_channels = b.buffer[b.write].NumChannels(); + RTC_DCHECK_EQ(block.NumBands(), num_bands); + RTC_DCHECK_EQ(block.NumChannels(), num_render_channels); + for (size_t band = 0; band < num_bands; ++band) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + std::copy(block.begin(band, ch), block.end(band, ch), + b.buffer[b.write].begin(band, ch)); + } + } + + if (render_linear_amplitude_gain_ != 1.f) { + for (size_t band = 0; band < num_bands; ++band) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + ArrayView b_view = b.buffer[b.write].View(band, ch); + for (float& sample : b_view) { + sample *= render_linear_amplitude_gain_; + } + } + } + } + + std::array downmixed_render; + render_mixer_.ProduceOutput(b.buffer[b.write], downmixed_render); + render_decimator_.Decimate(downmixed_render, ds); + data_dumper_->DumpWav("aec3_render_decimator_output", ds.size(), ds.data(), + 16000 / down_sampling_factor_, 1); + std::copy(ds.rbegin(), ds.rend(), lr.buffer.begin() + lr.write); + for (int channel = 0; channel < b.buffer[b.write].NumChannels(); ++channel) { + fft_.PaddedFft(b.buffer[b.write].View(/*band=*/0, channel), + b.buffer[previous_write].View(/*band=*/0, channel), + &f.buffer[f.write][channel]); + f.buffer[f.write][channel].Spectrum(optimization_, + s.buffer[s.write][channel]); + } +} + +bool RenderDelayBufferImpl::DetectActiveRender(ArrayView x) const { + const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f); + return x_energy > (config_.render_levels.active_render_limit * + config_.render_levels.active_render_limit) * + kFftLengthBy2; +} + +bool RenderDelayBufferImpl::DetectExcessRenderBlocks() { + bool excess_render_detected = false; + const size_t latency_blocks = static_cast(BufferLatency()); + // The recently seen minimum latency in blocks. Should be close to 0. + min_latency_blocks_ = std::min(min_latency_blocks_, latency_blocks); + // After processing a configurable number of blocks the minimum latency is + // checked. + if (++excess_render_detection_counter_ >= + config_.buffering.excess_render_detection_interval_blocks) { + // If the minimum latency is not lower than the threshold there have been + // more render than capture frames. + excess_render_detected = min_latency_blocks_ > + config_.buffering.max_allowed_excess_render_blocks; + // Reset the counter and let the minimum latency be the current latency. + min_latency_blocks_ = latency_blocks; + excess_render_detection_counter_ = 0; + } + + data_dumper_->DumpRaw("aec3_latency_blocks", latency_blocks); + data_dumper_->DumpRaw("aec3_min_latency_blocks", min_latency_blocks_); + data_dumper_->DumpRaw("aec3_excess_render_detected", excess_render_detected); + return excess_render_detected; +} + +// Computes the latency in the buffer (the number of unread sub-blocks). +int RenderDelayBufferImpl::BufferLatency() const { + const DownsampledRenderBuffer& l = low_rate_; + int latency_samples = (l.buffer.size() + l.read - l.write) % l.buffer.size(); + int latency_blocks = latency_samples / sub_block_size_; + return latency_blocks; +} + +// Increments the write indices for the render buffers. +void RenderDelayBufferImpl::IncrementWriteIndices() { + low_rate_.UpdateWriteIndex(-sub_block_size_); + blocks_.IncWriteIndex(); + spectra_.DecWriteIndex(); + ffts_.DecWriteIndex(); +} + +// Increments the read indices of the low rate render buffers. +void RenderDelayBufferImpl::IncrementLowRateReadIndices() { + low_rate_.UpdateReadIndex(-sub_block_size_); +} + +// Increments the read indices for the render buffers. +void RenderDelayBufferImpl::IncrementReadIndices() { + if (blocks_.read != blocks_.write) { + blocks_.IncReadIndex(); + spectra_.DecReadIndex(); + ffts_.DecReadIndex(); + } +} + +// Checks for a render buffer overrun. +bool RenderDelayBufferImpl::RenderOverrun() { + return low_rate_.read == low_rate_.write || blocks_.read == blocks_.write; +} + +// Checks for a render buffer underrun. +bool RenderDelayBufferImpl::RenderUnderrun() { + return low_rate_.read == low_rate_.write; +} + +} // namespace + +RenderDelayBuffer* RenderDelayBuffer::Create(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels) { + return new RenderDelayBufferImpl(config, sample_rate_hz, num_render_channels); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_buffer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_buffer.h new file mode 100644 index 00000000..6dc1aefb --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_buffer.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_BUFFER_H_ + +#include + +#include + +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/block.h" +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/aec3/render_buffer.h" + +namespace webrtc { + +// Class for buffering the incoming render blocks such that these may be +// extracted with a specified delay. +class RenderDelayBuffer { + public: + enum class BufferingEvent { + kNone, + kRenderUnderrun, + kRenderOverrun, + kApiCallSkew + }; + + static RenderDelayBuffer* Create(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels); + virtual ~RenderDelayBuffer() = default; + + // Resets the buffer alignment. + virtual void Reset() = 0; + + // Inserts a block into the buffer. + virtual BufferingEvent Insert(const Block& block) = 0; + + // Updates the buffers one step based on the specified buffer delay. Returns + // an enum indicating whether there was a special event that occurred. + virtual BufferingEvent PrepareCaptureProcessing() = 0; + + // Called on capture blocks where PrepareCaptureProcessing is not called. + virtual void HandleSkippedCaptureProcessing() = 0; + + // Sets the buffer delay and returns a bool indicating whether the delay + // changed. + virtual bool AlignFromDelay(size_t delay) = 0; + + // Sets the buffer delay from the most recently reported external delay. + virtual void AlignFromExternalDelay() = 0; + + // Gets the buffer delay. + virtual size_t Delay() const = 0; + + // Gets the buffer delay. + virtual size_t MaxDelay() const = 0; + + // Returns the render buffer for the echo remover. + virtual RenderBuffer* GetRenderBuffer() = 0; + + // Returns the downsampled render buffer. + virtual const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const = 0; + + // Returns the maximum non calusal offset that can occur in the delay buffer. + static int DelayEstimatorOffset(const EchoCanceller3Config& config); + + // Provides an optional external estimate of the audio buffer delay. + virtual void SetAudioBufferDelay(int delay_ms) = 0; + + // Returns whether an external delay estimate has been reported via + // SetAudioBufferDelay. + virtual bool HasReceivedBufferDelay() = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller.cc new file mode 100644 index 00000000..b02e516a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller.cc @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/aec3/render_delay_controller.h" + +#include + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/aec3/echo_path_delay_estimator.h" +#include "modules/audio_processing/aec3/render_delay_controller_metrics.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +class RenderDelayControllerImpl final : public RenderDelayController { + public: + RenderDelayControllerImpl(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_capture_channels); + + RenderDelayControllerImpl() = delete; + RenderDelayControllerImpl(const RenderDelayControllerImpl&) = delete; + RenderDelayControllerImpl& operator=(const RenderDelayControllerImpl&) = + delete; + + ~RenderDelayControllerImpl() override; + void Reset(bool reset_delay_confidence) override; + void LogRenderCall() override; + std::optional GetDelay( + const DownsampledRenderBuffer& render_buffer, + size_t render_delay_buffer_delay, + const Block& capture) override; + bool HasClockdrift() const override; + + private: + static std::atomic instance_count_; + std::unique_ptr data_dumper_; + const int hysteresis_limit_blocks_; + std::optional delay_; + EchoPathDelayEstimator delay_estimator_; + RenderDelayControllerMetrics metrics_; + std::optional delay_samples_; + size_t capture_call_counter_ = 0; + int delay_change_counter_ = 0; + DelayEstimate::Quality last_delay_estimate_quality_; +}; + +DelayEstimate ComputeBufferDelay( + const std::optional& current_delay, + int hysteresis_limit_blocks, + DelayEstimate estimated_delay) { + // Compute the buffer delay increase required to achieve the desired latency. + size_t new_delay_blocks = estimated_delay.delay >> kBlockSizeLog2; + // Add hysteresis. + if (current_delay) { + size_t current_delay_blocks = current_delay->delay; + if (new_delay_blocks > current_delay_blocks && + new_delay_blocks <= current_delay_blocks + hysteresis_limit_blocks) { + new_delay_blocks = current_delay_blocks; + } + } + DelayEstimate new_delay = estimated_delay; + new_delay.delay = new_delay_blocks; + return new_delay; +} + +std::atomic RenderDelayControllerImpl::instance_count_(0); + +RenderDelayControllerImpl::RenderDelayControllerImpl( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_capture_channels) + : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), + hysteresis_limit_blocks_( + static_cast(config.delay.hysteresis_limit_blocks)), + delay_estimator_(data_dumper_.get(), config, num_capture_channels), + last_delay_estimate_quality_(DelayEstimate::Quality::kCoarse) { + RTC_DCHECK(ValidFullBandRate(sample_rate_hz)); + delay_estimator_.LogDelayEstimationProperties(sample_rate_hz, 0); +} + +RenderDelayControllerImpl::~RenderDelayControllerImpl() = default; + +void RenderDelayControllerImpl::Reset(bool reset_delay_confidence) { + delay_ = std::nullopt; + delay_samples_ = std::nullopt; + delay_estimator_.Reset(reset_delay_confidence); + delay_change_counter_ = 0; + if (reset_delay_confidence) { + last_delay_estimate_quality_ = DelayEstimate::Quality::kCoarse; + } +} + +void RenderDelayControllerImpl::LogRenderCall() {} + +std::optional RenderDelayControllerImpl::GetDelay( + const DownsampledRenderBuffer& render_buffer, + size_t /* render_delay_buffer_delay */, + const Block& capture) { + ++capture_call_counter_; + + auto delay_samples = delay_estimator_.EstimateDelay(render_buffer, capture); + + if (delay_samples) { + if (!delay_samples_ || delay_samples->delay != delay_samples_->delay) { + delay_change_counter_ = 0; + } + if (delay_samples_) { + delay_samples_->blocks_since_last_change = + delay_samples_->delay == delay_samples->delay + ? delay_samples_->blocks_since_last_change + 1 + : 0; + delay_samples_->blocks_since_last_update = 0; + delay_samples_->delay = delay_samples->delay; + delay_samples_->quality = delay_samples->quality; + } else { + delay_samples_ = delay_samples; + } + } else { + if (delay_samples_) { + ++delay_samples_->blocks_since_last_change; + ++delay_samples_->blocks_since_last_update; + } + } + + if (delay_change_counter_ < 2 * kNumBlocksPerSecond) { + ++delay_change_counter_; + } + + if (delay_samples_) { + // Compute the render delay buffer delay. + const bool use_hysteresis = + last_delay_estimate_quality_ == DelayEstimate::Quality::kRefined && + delay_samples_->quality == DelayEstimate::Quality::kRefined; + delay_ = ComputeBufferDelay( + delay_, use_hysteresis ? hysteresis_limit_blocks_ : 0, *delay_samples_); + last_delay_estimate_quality_ = delay_samples_->quality; + } + + metrics_.Update(delay_samples_ ? std::optional(delay_samples_->delay) + : std::nullopt, + delay_ ? std::optional(delay_->delay) : std::nullopt, + delay_estimator_.Clockdrift()); + + data_dumper_->DumpRaw("aec3_render_delay_controller_delay", + delay_samples ? delay_samples->delay : 0); + data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay", + delay_ ? delay_->delay : 0); + + return delay_; +} + +bool RenderDelayControllerImpl::HasClockdrift() const { + return delay_estimator_.Clockdrift() != ClockdriftDetector::Level::kNone; +} + +} // namespace + +RenderDelayController* RenderDelayController::Create( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_capture_channels) { + return new RenderDelayControllerImpl(config, sample_rate_hz, + num_capture_channels); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller.h b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller.h new file mode 100644 index 00000000..b74c1619 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_H_ + +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/block.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/aec3/render_delay_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +// Class for aligning the render and capture signal using a RenderDelayBuffer. +class RenderDelayController { + public: + static RenderDelayController* Create(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_capture_channels); + virtual ~RenderDelayController() = default; + + // Resets the delay controller. If the delay confidence is reset, the reset + // behavior is as if the call is restarted. + virtual void Reset(bool reset_delay_confidence) = 0; + + // Logs a render call. + virtual void LogRenderCall() = 0; + + // Aligns the render buffer content with the capture signal. + virtual std::optional GetDelay( + const DownsampledRenderBuffer& render_buffer, + size_t render_delay_buffer_delay, + const Block& capture) = 0; + + // Returns true if clockdrift has been detected. + virtual bool HasClockdrift() const = 0; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.cc new file mode 100644 index 00000000..8d3f4153 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.cc @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/render_delay_controller_metrics.h" + +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +namespace { + +enum class DelayReliabilityCategory { + kNone, + kPoor, + kMedium, + kGood, + kExcellent, + kNumCategories +}; +enum class DelayChangesCategory { + kNone, + kFew, + kSeveral, + kMany, + kConstant, + kNumCategories +}; + +} // namespace + +RenderDelayControllerMetrics::RenderDelayControllerMetrics() = default; + +void RenderDelayControllerMetrics::Update( + std::optional delay_samples, + std::optional buffer_delay_blocks, + ClockdriftDetector::Level clockdrift) { + ++call_counter_; + + if (!initial_update) { + size_t delay_blocks; + if (delay_samples) { + ++reliable_delay_estimate_counter_; + // Add an offset by 1 (metric is halved before reporting) to reserve 0 for + // absent delay. + delay_blocks = (*delay_samples) / kBlockSize + 2; + } else { + delay_blocks = 0; + } + + if (delay_blocks != delay_blocks_) { + ++delay_change_counter_; + delay_blocks_ = delay_blocks; + } + + } else if (++initial_call_counter_ == 5 * kNumBlocksPerSecond) { + initial_update = false; + } + + if (call_counter_ == kMetricsReportingIntervalBlocks) { + int value_to_report = static_cast(delay_blocks_); + // Divide by 2 to compress metric range. + value_to_report = std::min(124, value_to_report >> 1); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.EchoCanceller.EchoPathDelay", + value_to_report, 0, 124, 125); + + // Divide by 2 to compress metric range. + // Offset by 1 to reserve 0 for absent delay. + value_to_report = buffer_delay_blocks ? (*buffer_delay_blocks + 2) >> 1 : 0; + value_to_report = std::min(124, value_to_report); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.EchoCanceller.BufferDelay", + value_to_report, 0, 124, 125); + + DelayReliabilityCategory delay_reliability; + if (reliable_delay_estimate_counter_ == 0) { + delay_reliability = DelayReliabilityCategory::kNone; + } else if (reliable_delay_estimate_counter_ > (call_counter_ >> 1)) { + delay_reliability = DelayReliabilityCategory::kExcellent; + } else if (reliable_delay_estimate_counter_ > 100) { + delay_reliability = DelayReliabilityCategory::kGood; + } else if (reliable_delay_estimate_counter_ > 10) { + delay_reliability = DelayReliabilityCategory::kMedium; + } else { + delay_reliability = DelayReliabilityCategory::kPoor; + } + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.EchoCanceller.ReliableDelayEstimates", + static_cast(delay_reliability), + static_cast(DelayReliabilityCategory::kNumCategories)); + + DelayChangesCategory delay_changes; + if (delay_change_counter_ == 0) { + delay_changes = DelayChangesCategory::kNone; + } else if (delay_change_counter_ > 10) { + delay_changes = DelayChangesCategory::kConstant; + } else if (delay_change_counter_ > 5) { + delay_changes = DelayChangesCategory::kMany; + } else if (delay_change_counter_ > 2) { + delay_changes = DelayChangesCategory::kSeveral; + } else { + delay_changes = DelayChangesCategory::kFew; + } + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.EchoCanceller.DelayChanges", + static_cast(delay_changes), + static_cast(DelayChangesCategory::kNumCategories)); + + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.EchoCanceller.Clockdrift", static_cast(clockdrift), + static_cast(ClockdriftDetector::Level::kNumCategories)); + + call_counter_ = 0; + ResetMetrics(); + } +} + +void RenderDelayControllerMetrics::ResetMetrics() { + delay_change_counter_ = 0; + reliable_delay_estimate_counter_ = 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.h b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.h new file mode 100644 index 00000000..0a6f30e8 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_METRICS_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_METRICS_H_ + +#include + +#include + +#include "modules/audio_processing/aec3/clockdrift_detector.h" + +namespace webrtc { + +// Handles the reporting of metrics for the render delay controller. +class RenderDelayControllerMetrics { + public: + RenderDelayControllerMetrics(); + + RenderDelayControllerMetrics(const RenderDelayControllerMetrics&) = delete; + RenderDelayControllerMetrics& operator=(const RenderDelayControllerMetrics&) = + delete; + + // Updates the metric with new data. + void Update(std::optional delay_samples, + std::optional buffer_delay_blocks, + ClockdriftDetector::Level clockdrift); + + private: + // Resets the metrics. + void ResetMetrics(); + + size_t delay_blocks_ = 0; + int reliable_delay_estimate_counter_ = 0; + int delay_change_counter_ = 0; + int call_counter_ = 0; + int initial_call_counter_ = 0; + bool initial_update = true; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_METRICS_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/render_signal_analyzer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/render_signal_analyzer.cc new file mode 100644 index 00000000..7970caf6 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/render_signal_analyzer.cc @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/render_signal_analyzer.h" + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { +constexpr size_t kCounterThreshold = 5; + +// Identifies local bands with narrow characteristics. +void IdentifySmallNarrowBandRegions( + const RenderBuffer& render_buffer, + const std::optional& delay_partitions, + std::array* narrow_band_counters) { + RTC_DCHECK(narrow_band_counters); + + if (!delay_partitions) { + narrow_band_counters->fill(0); + return; + } + + std::array channel_counters; + channel_counters.fill(0); + ArrayView> X2 = + render_buffer.Spectrum(*delay_partitions); + for (size_t ch = 0; ch < X2.size(); ++ch) { + for (size_t k = 1; k < kFftLengthBy2; ++k) { + if (X2[ch][k] > 3 * std::max(X2[ch][k - 1], X2[ch][k + 1])) { + ++channel_counters[k - 1]; + } + } + } + for (size_t k = 1; k < kFftLengthBy2; ++k) { + (*narrow_band_counters)[k - 1] = + channel_counters[k - 1] > 0 ? (*narrow_band_counters)[k - 1] + 1 : 0; + } +} + +// Identifies whether the signal has a single strong narrow-band component. +void IdentifyStrongNarrowBandComponent(const RenderBuffer& render_buffer, + int strong_peak_freeze_duration, + std::optional* narrow_peak_band, + size_t* narrow_peak_counter) { + RTC_DCHECK(narrow_peak_band); + RTC_DCHECK(narrow_peak_counter); + if (*narrow_peak_band && + ++(*narrow_peak_counter) > + static_cast(strong_peak_freeze_duration)) { + *narrow_peak_band = std::nullopt; + } + + const Block& x_latest = render_buffer.GetBlock(0); + float max_peak_level = 0.f; + for (int channel = 0; channel < x_latest.NumChannels(); ++channel) { + ArrayView X2_latest = + render_buffer.Spectrum(0)[channel]; + + // Identify the spectral peak. + const int peak_bin = + static_cast(std::max_element(X2_latest.begin(), X2_latest.end()) - + X2_latest.begin()); + + // Compute the level around the peak. + float non_peak_power = 0.f; + for (int k = std::max(0, peak_bin - 14); k < peak_bin - 4; ++k) { + non_peak_power = std::max(X2_latest[k], non_peak_power); + } + for (int k = peak_bin + 5; + k < std::min(peak_bin + 15, static_cast(kFftLengthBy2Plus1)); + ++k) { + non_peak_power = std::max(X2_latest[k], non_peak_power); + } + + // Assess the render signal strength. + auto result0 = std::minmax_element(x_latest.begin(/*band=*/0, channel), + x_latest.end(/*band=*/0, channel)); + float max_abs = std::max(fabs(*result0.first), fabs(*result0.second)); + + if (x_latest.NumBands() > 1) { + const auto result1 = + std::minmax_element(x_latest.begin(/*band=*/1, channel), + x_latest.end(/*band=*/1, channel)); + max_abs = + std::max(max_abs, static_cast(std::max( + fabs(*result1.first), fabs(*result1.second)))); + } + + // Detect whether the spectral peak has as strong narrowband nature. + const float peak_level = X2_latest[peak_bin]; + if (peak_bin > 0 && max_abs > 100 && peak_level > 100 * non_peak_power) { + // Store the strongest peak across channels. + if (peak_level > max_peak_level) { + max_peak_level = peak_level; + *narrow_peak_band = peak_bin; + *narrow_peak_counter = 0; + } + } + } +} + +} // namespace + +RenderSignalAnalyzer::RenderSignalAnalyzer(const EchoCanceller3Config& config) + : strong_peak_freeze_duration_(config.filter.refined.length_blocks) { + narrow_band_counters_.fill(0); +} +RenderSignalAnalyzer::~RenderSignalAnalyzer() = default; + +void RenderSignalAnalyzer::Update( + const RenderBuffer& render_buffer, + const std::optional& delay_partitions) { + // Identify bands of narrow nature. + IdentifySmallNarrowBandRegions(render_buffer, delay_partitions, + &narrow_band_counters_); + + // Identify the presence of a strong narrow band. + IdentifyStrongNarrowBandComponent(render_buffer, strong_peak_freeze_duration_, + &narrow_peak_band_, &narrow_peak_counter_); +} + +void RenderSignalAnalyzer::MaskRegionsAroundNarrowBands( + std::array* v) const { + RTC_DCHECK(v); + + // Set v to zero around narrow band signal regions. + if (narrow_band_counters_[0] > kCounterThreshold) { + (*v)[1] = (*v)[0] = 0.f; + } + for (size_t k = 2; k < kFftLengthBy2 - 1; ++k) { + if (narrow_band_counters_[k - 1] > kCounterThreshold) { + (*v)[k - 2] = (*v)[k - 1] = (*v)[k] = (*v)[k + 1] = (*v)[k + 2] = 0.f; + } + } + if (narrow_band_counters_[kFftLengthBy2 - 2] > kCounterThreshold) { + (*v)[kFftLengthBy2] = (*v)[kFftLengthBy2 - 1] = 0.f; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/render_signal_analyzer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/render_signal_analyzer.h new file mode 100644 index 00000000..058b2922 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/render_signal_analyzer.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_SIGNAL_ANALYZER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RENDER_SIGNAL_ANALYZER_H_ + +#include +#include +#include +#include + +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Provides functionality for analyzing the properties of the render signal. +class RenderSignalAnalyzer { + public: + explicit RenderSignalAnalyzer(const EchoCanceller3Config& config); + ~RenderSignalAnalyzer(); + + RenderSignalAnalyzer(const RenderSignalAnalyzer&) = delete; + RenderSignalAnalyzer& operator=(const RenderSignalAnalyzer&) = delete; + + // Updates the render signal analysis with the most recent render signal. + void Update(const RenderBuffer& render_buffer, + const std::optional& delay_partitions); + + // Returns true if the render signal is poorly exciting. + bool PoorSignalExcitation() const { + RTC_DCHECK_LT(2, narrow_band_counters_.size()); + return std::any_of(narrow_band_counters_.begin(), + narrow_band_counters_.end(), + [](size_t a) { return a > 10; }); + } + + // Zeros the array around regions with narrow bands signal characteristics. + void MaskRegionsAroundNarrowBands( + std::array* v) const; + + std::optional NarrowPeakBand() const { return narrow_peak_band_; } + + private: + const int strong_peak_freeze_duration_; + std::array narrow_band_counters_; + std::optional narrow_peak_band_; + size_t narrow_peak_counter_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RENDER_SIGNAL_ANALYZER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc new file mode 100644 index 00000000..384abfff --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/residual_echo_estimator.h" + +#include + +#include +#include + +#include "api/array_view.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "modules/audio_processing/aec3/reverb_model.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr float kDefaultTransparentModeGain = 0.01f; + +float GetTransparentModeGain() { + return kDefaultTransparentModeGain; +} + +float GetEarlyReflectionsDefaultModeGain( + const FieldTrialsView& field_trials, + const EchoCanceller3Config::EpStrength& config) { + if (field_trials.IsEnabled("WebRTC-Aec3UseLowEarlyReflectionsDefaultGain")) { + return 0.1f; + } + return config.default_gain; +} + +float GetLateReflectionsDefaultModeGain( + const FieldTrialsView& field_trials, + const EchoCanceller3Config::EpStrength& config) { + if (field_trials.IsEnabled("WebRTC-Aec3UseLowLateReflectionsDefaultGain")) { + return 0.1f; + } + return config.default_gain; +} + +bool UseErleOnsetCompensationInDominantNearend( + const FieldTrialsView& field_trials, + const EchoCanceller3Config::EpStrength& config) { + return config.erle_onset_compensation_in_dominant_nearend || + field_trials.IsEnabled( + "WebRTC-Aec3UseErleOnsetCompensationInDominantNearend"); +} + +// Computes the indexes that will be used for computing spectral power over +// the blocks surrounding the delay. +void GetRenderIndexesToAnalyze( + const SpectrumBuffer& spectrum_buffer, + const EchoCanceller3Config::EchoModel& echo_model, + int filter_delay_blocks, + int* idx_start, + int* idx_stop) { + RTC_DCHECK(idx_start); + RTC_DCHECK(idx_stop); + size_t window_start; + size_t window_end; + window_start = + std::max(0, filter_delay_blocks - + static_cast(echo_model.render_pre_window_size)); + window_end = filter_delay_blocks + + static_cast(echo_model.render_post_window_size); + *idx_start = spectrum_buffer.OffsetIndex(spectrum_buffer.read, window_start); + *idx_stop = spectrum_buffer.OffsetIndex(spectrum_buffer.read, window_end + 1); +} + +// Estimates the residual echo power based on the echo return loss enhancement +// (ERLE) and the linear power estimate. +void LinearEstimate( + ArrayView> S2_linear, + ArrayView> erle, + ArrayView> R2) { + RTC_DCHECK_EQ(S2_linear.size(), erle.size()); + RTC_DCHECK_EQ(S2_linear.size(), R2.size()); + + const size_t num_capture_channels = R2.size(); + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + RTC_DCHECK_LT(0.f, erle[ch][k]); + R2[ch][k] = S2_linear[ch][k] / erle[ch][k]; + } + } +} + +// Estimates the residual echo power based on the estimate of the echo path +// gain. +void NonLinearEstimate(float echo_path_gain, + const std::array& X2, + ArrayView> R2) { + const size_t num_capture_channels = R2.size(); + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + R2[ch][k] = X2[k] * echo_path_gain; + } + } +} + +// Applies a soft noise gate to the echo generating power. +void ApplyNoiseGate(const EchoCanceller3Config::EchoModel& config, + ArrayView X2) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + if (config.noise_gate_power > X2[k]) { + X2[k] = std::max(0.f, X2[k] - config.noise_gate_slope * + (config.noise_gate_power - X2[k])); + } + } +} + +// Estimates the echo generating signal power as gated maximal power over a +// time window. +void EchoGeneratingPower(size_t num_render_channels, + const SpectrumBuffer& spectrum_buffer, + const EchoCanceller3Config::EchoModel& echo_model, + int filter_delay_blocks, + ArrayView X2) { + int idx_stop; + int idx_start; + GetRenderIndexesToAnalyze(spectrum_buffer, echo_model, filter_delay_blocks, + &idx_start, &idx_stop); + + std::fill(X2.begin(), X2.end(), 0.f); + if (num_render_channels == 1) { + for (int k = idx_start; k != idx_stop; k = spectrum_buffer.IncIndex(k)) { + for (size_t j = 0; j < kFftLengthBy2Plus1; ++j) { + X2[j] = std::max(X2[j], spectrum_buffer.buffer[k][/*channel=*/0][j]); + } + } + } else { + for (int k = idx_start; k != idx_stop; k = spectrum_buffer.IncIndex(k)) { + std::array render_power; + render_power.fill(0.f); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const auto& channel_power = spectrum_buffer.buffer[k][ch]; + for (size_t j = 0; j < kFftLengthBy2Plus1; ++j) { + render_power[j] += channel_power[j]; + } + } + for (size_t j = 0; j < kFftLengthBy2Plus1; ++j) { + X2[j] = std::max(X2[j], render_power[j]); + } + } + } +} + +} // namespace + +ResidualEchoEstimator::ResidualEchoEstimator(const Environment& env, + const EchoCanceller3Config& config, + size_t num_render_channels) + : config_(config), + num_render_channels_(num_render_channels), + early_reflections_transparent_mode_gain_(GetTransparentModeGain()), + late_reflections_transparent_mode_gain_(GetTransparentModeGain()), + early_reflections_general_gain_( + GetEarlyReflectionsDefaultModeGain(env.field_trials(), + config_.ep_strength)), + late_reflections_general_gain_( + GetLateReflectionsDefaultModeGain(env.field_trials(), + config_.ep_strength)), + erle_onset_compensation_in_dominant_nearend_( + UseErleOnsetCompensationInDominantNearend(env.field_trials(), + config_.ep_strength)) { + Reset(); +} + +ResidualEchoEstimator::~ResidualEchoEstimator() = default; + +void ResidualEchoEstimator::Estimate( + const AecState& aec_state, + const RenderBuffer& render_buffer, + ArrayView> S2_linear, + ArrayView> Y2, + bool dominant_nearend, + ArrayView> R2, + ArrayView> R2_unbounded) { + RTC_DCHECK_EQ(R2.size(), Y2.size()); + RTC_DCHECK_EQ(R2.size(), S2_linear.size()); + + const size_t num_capture_channels = R2.size(); + + // Estimate the power of the stationary noise in the render signal. + UpdateRenderNoisePower(render_buffer); + + // Estimate the residual echo power. + if (aec_state.UsableLinearEstimate()) { + // When there is saturated echo, assume the same spectral content as is + // present in the microphone signal. + if (aec_state.SaturatedEcho()) { + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + std::copy(Y2[ch].begin(), Y2[ch].end(), R2[ch].begin()); + std::copy(Y2[ch].begin(), Y2[ch].end(), R2_unbounded[ch].begin()); + } + } else { + const bool onset_compensated = + erle_onset_compensation_in_dominant_nearend_ || !dominant_nearend; + LinearEstimate(S2_linear, aec_state.Erle(onset_compensated), R2); + LinearEstimate(S2_linear, aec_state.ErleUnbounded(), R2_unbounded); + } + + UpdateReverb(ReverbType::kLinear, aec_state, render_buffer, + dominant_nearend); + AddReverb(R2); + AddReverb(R2_unbounded); + } else { + const float echo_path_gain = + GetEchoPathGain(aec_state, /*gain_for_early_reflections=*/true); + + // When there is saturated echo, assume the same spectral content as is + // present in the microphone signal. + if (aec_state.SaturatedEcho()) { + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + std::copy(Y2[ch].begin(), Y2[ch].end(), R2[ch].begin()); + std::copy(Y2[ch].begin(), Y2[ch].end(), R2_unbounded[ch].begin()); + } + } else { + // Estimate the echo generating signal power. + std::array X2; + EchoGeneratingPower(num_render_channels_, + render_buffer.GetSpectrumBuffer(), config_.echo_model, + aec_state.MinDirectPathFilterDelay(), X2); + if (!aec_state.UseStationarityProperties()) { + ApplyNoiseGate(config_.echo_model, X2); + } + + // Subtract the stationary noise power to avoid stationary noise causing + // excessive echo suppression. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + X2[k] -= config_.echo_model.stationary_gate_slope * X2_noise_floor_[k]; + X2[k] = std::max(0.f, X2[k]); + } + + NonLinearEstimate(echo_path_gain, X2, R2); + NonLinearEstimate(echo_path_gain, X2, R2_unbounded); + } + + if (config_.echo_model.model_reverb_in_nonlinear_mode && + !aec_state.TransparentModeActive()) { + UpdateReverb(ReverbType::kNonLinear, aec_state, render_buffer, + dominant_nearend); + AddReverb(R2); + AddReverb(R2_unbounded); + } + } + + if (aec_state.UseStationarityProperties()) { + // Scale the echo according to echo audibility. + std::array residual_scaling; + aec_state.GetResidualEchoScaling(residual_scaling); + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + R2[ch][k] *= residual_scaling[k]; + R2_unbounded[ch][k] *= residual_scaling[k]; + } + } + } +} + +void ResidualEchoEstimator::Reset() { + echo_reverb_.Reset(); + X2_noise_floor_counter_.fill(config_.echo_model.noise_floor_hold); + X2_noise_floor_.fill(config_.echo_model.min_noise_floor_power); +} + +void ResidualEchoEstimator::UpdateRenderNoisePower( + const RenderBuffer& render_buffer) { + std::array render_power_data; + ArrayView> X2 = + render_buffer.Spectrum(0); + ArrayView render_power = X2[/*channel=*/0]; + if (num_render_channels_ > 1) { + render_power_data.fill(0.f); + for (size_t ch = 0; ch < num_render_channels_; ++ch) { + const auto& channel_power = X2[ch]; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + render_power_data[k] += channel_power[k]; + } + } + render_power = render_power_data; + } + + // Estimate the stationary noise power in a minimum statistics manner. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + // Decrease rapidly. + if (render_power[k] < X2_noise_floor_[k]) { + X2_noise_floor_[k] = render_power[k]; + X2_noise_floor_counter_[k] = 0; + } else { + // Increase in a delayed, leaky manner. + if (X2_noise_floor_counter_[k] >= + static_cast(config_.echo_model.noise_floor_hold)) { + X2_noise_floor_[k] = std::max(X2_noise_floor_[k] * 1.1f, + config_.echo_model.min_noise_floor_power); + } else { + ++X2_noise_floor_counter_[k]; + } + } + } +} + +// Updates the reverb estimation. +void ResidualEchoEstimator::UpdateReverb(ReverbType reverb_type, + const AecState& aec_state, + const RenderBuffer& render_buffer, + bool dominant_nearend) { + // Choose reverb partition based on what type of echo power model is used. + const size_t first_reverb_partition = + reverb_type == ReverbType::kLinear + ? aec_state.FilterLengthBlocks() + 1 + : aec_state.MinDirectPathFilterDelay() + 1; + + // Compute render power for the reverb. + std::array render_power_data; + ArrayView> X2 = + render_buffer.Spectrum(first_reverb_partition); + ArrayView render_power = X2[/*channel=*/0]; + if (num_render_channels_ > 1) { + render_power_data.fill(0.f); + for (size_t ch = 0; ch < num_render_channels_; ++ch) { + const auto& channel_power = X2[ch]; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + render_power_data[k] += channel_power[k]; + } + } + render_power = render_power_data; + } + + // Update the reverb estimate. + float reverb_decay = aec_state.ReverbDecay(/*mild=*/dominant_nearend); + if (reverb_type == ReverbType::kLinear) { + echo_reverb_.UpdateReverb( + render_power, aec_state.GetReverbFrequencyResponse(), reverb_decay); + } else { + const float echo_path_gain = + GetEchoPathGain(aec_state, /*gain_for_early_reflections=*/false); + echo_reverb_.UpdateReverbNoFreqShaping(render_power, echo_path_gain, + reverb_decay); + } +} +// Adds the estimated power of the reverb to the residual echo power. +void ResidualEchoEstimator::AddReverb( + ArrayView> R2) const { + const size_t num_capture_channels = R2.size(); + + // Add the reverb power. + ArrayView reverb_power = + echo_reverb_.reverb(); + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + R2[ch][k] += reverb_power[k]; + } + } +} + +// Chooses the echo path gain to use. +float ResidualEchoEstimator::GetEchoPathGain( + const AecState& aec_state, + bool gain_for_early_reflections) const { + float gain_amplitude; + if (aec_state.TransparentModeActive()) { + gain_amplitude = gain_for_early_reflections + ? early_reflections_transparent_mode_gain_ + : late_reflections_transparent_mode_gain_; + } else { + gain_amplitude = gain_for_early_reflections + ? early_reflections_general_gain_ + : late_reflections_general_gain_; + } + return gain_amplitude * gain_amplitude; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h new file mode 100644 index 00000000..549d3def --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RESIDUAL_ECHO_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RESIDUAL_ECHO_ESTIMATOR_H_ + +#include +#include +#include + +#include "api/audio/echo_canceller3_config.h" +#include "api/environment/environment.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec_state.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/reverb_model.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +class ResidualEchoEstimator { + public: + ResidualEchoEstimator(const Environment& env, + const EchoCanceller3Config& config, + size_t num_render_channels); + ~ResidualEchoEstimator(); + + ResidualEchoEstimator(const ResidualEchoEstimator&) = delete; + ResidualEchoEstimator& operator=(const ResidualEchoEstimator&) = delete; + + void Estimate( + const AecState& aec_state, + const RenderBuffer& render_buffer, + ArrayView> S2_linear, + ArrayView> Y2, + bool dominant_nearend, + ArrayView> R2, + ArrayView> R2_unbounded); + + private: + enum class ReverbType { kLinear, kNonLinear }; + + // Resets the state. + void Reset(); + + // Updates estimate for the power of the stationary noise component in the + // render signal. + void UpdateRenderNoisePower(const RenderBuffer& render_buffer); + + // Updates the reverb estimation. + void UpdateReverb(ReverbType reverb_type, + const AecState& aec_state, + const RenderBuffer& render_buffer, + bool dominant_nearend); + + // Adds the estimated unmodelled echo power to the residual echo power + // estimate. + void AddReverb(ArrayView> R2) const; + + // Gets the echo path gain to apply. + float GetEchoPathGain(const AecState& aec_state, + bool gain_for_early_reflections) const; + + const EchoCanceller3Config config_; + const size_t num_render_channels_; + const float early_reflections_transparent_mode_gain_; + const float late_reflections_transparent_mode_gain_; + const float early_reflections_general_gain_; + const float late_reflections_general_gain_; + const bool erle_onset_compensation_in_dominant_nearend_; + std::array X2_noise_floor_; + std::array X2_noise_floor_counter_; + ReverbModel echo_reverb_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RESIDUAL_ECHO_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.cc new file mode 100644 index 00000000..f973f86e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.cc @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/reverb_decay_estimator.h" + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +constexpr int kEarlyReverbMinSizeBlocks = 3; +constexpr int kBlocksPerSection = 6; +// Linear regression approach assumes symmetric index around 0. +constexpr float kEarlyReverbFirstPointAtLinearRegressors = + -0.5f * kBlocksPerSection * kFftLengthBy2 + 0.5f; + +// Averages the values in a block of size kFftLengthBy2; +float BlockAverage(ArrayView v, size_t block_index) { + constexpr float kOneByFftLengthBy2 = 1.f / kFftLengthBy2; + const int i = block_index * kFftLengthBy2; + RTC_DCHECK_GE(v.size(), i + kFftLengthBy2); + const float sum = + std::accumulate(v.begin() + i, v.begin() + i + kFftLengthBy2, 0.f); + return sum * kOneByFftLengthBy2; +} + +// Analyzes the gain in a block. +void AnalyzeBlockGain(const std::array& h2, + float floor_gain, + float* previous_gain, + bool* block_adapting, + bool* decaying_gain) { + float gain = std::max(BlockAverage(h2, 0), 1e-32f); + *block_adapting = + *previous_gain > 1.1f * gain || *previous_gain < 0.9f * gain; + *decaying_gain = gain > floor_gain; + *previous_gain = gain; +} + +// Arithmetic sum of $2 \sum_{i=0.5}^{(N-1)/2}i^2$ calculated directly. +constexpr float SymmetricArithmetricSum(int N) { + return N * (N * N - 1.0f) * (1.f / 12.f); +} + +// Returns the peak energy of an impulse response. +float BlockEnergyPeak(ArrayView h, int peak_block) { + RTC_DCHECK_LE((peak_block + 1) * kFftLengthBy2, h.size()); + RTC_DCHECK_GE(peak_block, 0); + float peak_value = + *std::max_element(h.begin() + peak_block * kFftLengthBy2, + h.begin() + (peak_block + 1) * kFftLengthBy2, + [](float a, float b) { return a * a < b * b; }); + return peak_value * peak_value; +} + +// Returns the average energy of an impulse response block. +float BlockEnergyAverage(ArrayView h, int block_index) { + RTC_DCHECK_LE((block_index + 1) * kFftLengthBy2, h.size()); + RTC_DCHECK_GE(block_index, 0); + constexpr float kOneByFftLengthBy2 = 1.f / kFftLengthBy2; + const auto sum_of_squares = [](float a, float b) { return a + b * b; }; + return std::accumulate(h.begin() + block_index * kFftLengthBy2, + h.begin() + (block_index + 1) * kFftLengthBy2, 0.f, + sum_of_squares) * + kOneByFftLengthBy2; +} + +} // namespace + +ReverbDecayEstimator::ReverbDecayEstimator(const EchoCanceller3Config& config) + : filter_length_blocks_(config.filter.refined.length_blocks), + filter_length_coefficients_(GetTimeDomainLength(filter_length_blocks_)), + use_adaptive_echo_decay_(config.ep_strength.default_len < 0.f), + early_reverb_estimator_(config.filter.refined.length_blocks - + kEarlyReverbMinSizeBlocks), + late_reverb_start_(kEarlyReverbMinSizeBlocks), + late_reverb_end_(kEarlyReverbMinSizeBlocks), + previous_gains_(config.filter.refined.length_blocks, 0.f), + decay_(std::fabs(config.ep_strength.default_len)), + mild_decay_(std::fabs(config.ep_strength.nearend_len)) { + RTC_DCHECK_GT(config.filter.refined.length_blocks, + static_cast(kEarlyReverbMinSizeBlocks)); +} + +ReverbDecayEstimator::~ReverbDecayEstimator() = default; + +void ReverbDecayEstimator::Update(ArrayView filter, + const std::optional& filter_quality, + int filter_delay_blocks, + bool usable_linear_filter, + bool stationary_signal) { + const int filter_size = static_cast(filter.size()); + + if (stationary_signal) { + return; + } + + bool estimation_feasible = + filter_delay_blocks <= + filter_length_blocks_ - kEarlyReverbMinSizeBlocks - 1; + estimation_feasible = + estimation_feasible && filter_size == filter_length_coefficients_; + estimation_feasible = estimation_feasible && filter_delay_blocks > 0; + estimation_feasible = estimation_feasible && usable_linear_filter; + + if (!estimation_feasible) { + ResetDecayEstimation(); + return; + } + + if (!use_adaptive_echo_decay_) { + return; + } + + const float new_smoothing = filter_quality ? *filter_quality * 0.2f : 0.f; + smoothing_constant_ = std::max(new_smoothing, smoothing_constant_); + if (smoothing_constant_ == 0.f) { + return; + } + + if (block_to_analyze_ < filter_length_blocks_) { + // Analyze the filter and accumulate data for reverb estimation. + AnalyzeFilter(filter); + ++block_to_analyze_; + } else { + // When the filter is fully analyzed, estimate the reverb decay and reset + // the block_to_analyze_ counter. + EstimateDecay(filter, filter_delay_blocks); + } +} + +void ReverbDecayEstimator::ResetDecayEstimation() { + early_reverb_estimator_.Reset(); + late_reverb_decay_estimator_.Reset(0); + block_to_analyze_ = 0; + estimation_region_candidate_size_ = 0; + estimation_region_identified_ = false; + smoothing_constant_ = 0.f; + late_reverb_start_ = 0; + late_reverb_end_ = 0; +} + +void ReverbDecayEstimator::EstimateDecay(ArrayView filter, + int peak_block) { + auto& h = filter; + RTC_DCHECK_EQ(0, h.size() % kFftLengthBy2); + + // Reset the block analysis counter. + block_to_analyze_ = + std::min(peak_block + kEarlyReverbMinSizeBlocks, filter_length_blocks_); + + // To estimate the reverb decay, the energy of the first filter section must + // be substantially larger than the last. Also, the first filter section + // energy must not deviate too much from the max peak. + const float first_reverb_gain = BlockEnergyAverage(h, block_to_analyze_); + const size_t h_size_blocks = h.size() >> kFftLengthBy2Log2; + tail_gain_ = BlockEnergyAverage(h, h_size_blocks - 1); + float peak_energy = BlockEnergyPeak(h, peak_block); + const bool sufficient_reverb_decay = first_reverb_gain > 4.f * tail_gain_; + const bool valid_filter = + first_reverb_gain > 2.f * tail_gain_ && peak_energy < 100.f; + + // Estimate the size of the regions with early and late reflections. + const int size_early_reverb = early_reverb_estimator_.Estimate(); + const int size_late_reverb = + std::max(estimation_region_candidate_size_ - size_early_reverb, 0); + + // Only update the reverb decay estimate if the size of the identified late + // reverb is sufficiently large. + if (size_late_reverb >= 5) { + if (valid_filter && late_reverb_decay_estimator_.EstimateAvailable()) { + float decay = std::pow( + 2.0f, late_reverb_decay_estimator_.Estimate() * kFftLengthBy2); + constexpr float kMaxDecay = 0.95f; // ~1 sec min RT60. + constexpr float kMinDecay = 0.02f; // ~15 ms max RT60. + decay = std::max(.97f * decay_, decay); + decay = std::min(decay, kMaxDecay); + decay = std::max(decay, kMinDecay); + decay_ += smoothing_constant_ * (decay - decay_); + } + + // Update length of decay. Must have enough data (number of sections) in + // order to estimate decay rate. + late_reverb_decay_estimator_.Reset(size_late_reverb * kFftLengthBy2); + late_reverb_start_ = + peak_block + kEarlyReverbMinSizeBlocks + size_early_reverb; + late_reverb_end_ = + block_to_analyze_ + estimation_region_candidate_size_ - 1; + } else { + late_reverb_decay_estimator_.Reset(0); + late_reverb_start_ = 0; + late_reverb_end_ = 0; + } + + // Reset variables for the identification of the region for reverb decay + // estimation. + estimation_region_identified_ = !(valid_filter && sufficient_reverb_decay); + estimation_region_candidate_size_ = 0; + + // Stop estimation of the decay until another good filter is received. + smoothing_constant_ = 0.f; + + // Reset early reflections detector. + early_reverb_estimator_.Reset(); +} + +void ReverbDecayEstimator::AnalyzeFilter(ArrayView filter) { + auto h = ArrayView( + filter.begin() + block_to_analyze_ * kFftLengthBy2, kFftLengthBy2); + + // Compute squared filter coeffiecients for the block to analyze_; + std::array h2; + std::transform(h.begin(), h.end(), h2.begin(), [](float a) { return a * a; }); + + // Map out the region for estimating the reverb decay. + bool adapting; + bool above_noise_floor; + AnalyzeBlockGain(h2, tail_gain_, &previous_gains_[block_to_analyze_], + &adapting, &above_noise_floor); + + // Count consecutive number of "good" filter sections, where "good" means: + // 1) energy is above noise floor. + // 2) energy of current section has not changed too much from last check. + estimation_region_identified_ = + estimation_region_identified_ || adapting || !above_noise_floor; + if (!estimation_region_identified_) { + ++estimation_region_candidate_size_; + } + + // Accumulate data for reverb decay estimation and for the estimation of early + // reflections. + if (block_to_analyze_ <= late_reverb_end_) { + if (block_to_analyze_ >= late_reverb_start_) { + for (float h2_k : h2) { + float h2_log2 = FastApproxLog2f(h2_k + 1e-10); + late_reverb_decay_estimator_.Accumulate(h2_log2); + early_reverb_estimator_.Accumulate(h2_log2, smoothing_constant_); + } + } else { + for (float h2_k : h2) { + float h2_log2 = FastApproxLog2f(h2_k + 1e-10); + early_reverb_estimator_.Accumulate(h2_log2, smoothing_constant_); + } + } + } +} + +void ReverbDecayEstimator::Dump(ApmDataDumper* data_dumper) const { + data_dumper->DumpRaw("aec3_reverb_decay", decay_); + data_dumper->DumpRaw("aec3_reverb_tail_energy", tail_gain_); + data_dumper->DumpRaw("aec3_reverb_alpha", smoothing_constant_); + data_dumper->DumpRaw("aec3_num_reverb_decay_blocks", + late_reverb_end_ - late_reverb_start_); + data_dumper->DumpRaw("aec3_late_reverb_start", late_reverb_start_); + data_dumper->DumpRaw("aec3_late_reverb_end", late_reverb_end_); + early_reverb_estimator_.Dump(data_dumper); +} + +void ReverbDecayEstimator::LateReverbLinearRegressor::Reset( + int num_data_points) { + RTC_DCHECK_LE(0, num_data_points); + RTC_DCHECK_EQ(0, num_data_points % 2); + const int N = num_data_points; + nz_ = 0.f; + // Arithmetic sum of $2 \sum_{i=0.5}^{(N-1)/2}i^2$ calculated directly. + nn_ = SymmetricArithmetricSum(N); + // The linear regression approach assumes symmetric index around 0. + count_ = N > 0 ? -N * 0.5f + 0.5f : 0.f; + N_ = N; + n_ = 0; +} + +void ReverbDecayEstimator::LateReverbLinearRegressor::Accumulate(float z) { + nz_ += count_ * z; + ++count_; + ++n_; +} + +float ReverbDecayEstimator::LateReverbLinearRegressor::Estimate() { + RTC_DCHECK(EstimateAvailable()); + if (nn_ == 0.f) { + RTC_DCHECK_NOTREACHED(); + return 0.f; + } + return nz_ / nn_; +} + +ReverbDecayEstimator::EarlyReverbLengthEstimator::EarlyReverbLengthEstimator( + int max_blocks) + : numerators_smooth_(max_blocks - kBlocksPerSection, 0.f), + numerators_(numerators_smooth_.size(), 0.f), + coefficients_counter_(0) { + RTC_DCHECK_LE(0, max_blocks); +} + +ReverbDecayEstimator::EarlyReverbLengthEstimator:: + ~EarlyReverbLengthEstimator() = default; + +void ReverbDecayEstimator::EarlyReverbLengthEstimator::Reset() { + coefficients_counter_ = 0; + std::fill(numerators_.begin(), numerators_.end(), 0.f); + block_counter_ = 0; +} + +void ReverbDecayEstimator::EarlyReverbLengthEstimator::Accumulate( + float value, + float smoothing) { + // Each section is composed by kBlocksPerSection blocks and each section + // overlaps with the next one in (kBlocksPerSection - 1) blocks. For example, + // the first section covers the blocks [0:5], the second covers the blocks + // [1:6] and so on. As a result, for each value, kBlocksPerSection sections + // need to be updated. + int first_section_index = std::max(block_counter_ - kBlocksPerSection + 1, 0); + int last_section_index = + std::min(block_counter_, static_cast(numerators_.size() - 1)); + float x_value = static_cast(coefficients_counter_) + + kEarlyReverbFirstPointAtLinearRegressors; + const float value_to_inc = kFftLengthBy2 * value; + float value_to_add = + x_value * value + (block_counter_ - last_section_index) * value_to_inc; + for (int section = last_section_index; section >= first_section_index; + --section, value_to_add += value_to_inc) { + numerators_[section] += value_to_add; + } + + // Check if this update was the last coefficient of the current block. In that + // case, check if we are at the end of one of the sections and update the + // numerator of the linear regressor that is computed in such section. + if (++coefficients_counter_ == kFftLengthBy2) { + if (block_counter_ >= (kBlocksPerSection - 1)) { + size_t section = block_counter_ - (kBlocksPerSection - 1); + RTC_DCHECK_GT(numerators_.size(), section); + RTC_DCHECK_GT(numerators_smooth_.size(), section); + numerators_smooth_[section] += + smoothing * (numerators_[section] - numerators_smooth_[section]); + n_sections_ = section + 1; + } + ++block_counter_; + coefficients_counter_ = 0; + } +} + +// Estimates the size in blocks of the early reverb. The estimation is done by +// comparing the tilt that is estimated in each section. As an optimization +// detail and due to the fact that all the linear regressors that are computed +// shared the same denominator, the comparison of the tilts is done by a +// comparison of the numerator of the linear regressors. +int ReverbDecayEstimator::EarlyReverbLengthEstimator::Estimate() { + constexpr float N = kBlocksPerSection * kFftLengthBy2; + constexpr float nn = SymmetricArithmetricSum(N); + // numerator_11 refers to the quantity that the linear regressor needs in the + // numerator for getting a decay equal to 1.1 (which is not a decay). + // log2(1.1) * nn / kFftLengthBy2. + constexpr float numerator_11 = 0.13750352374993502f * nn / kFftLengthBy2; + // log2(0.8) * nn / kFftLengthBy2. + constexpr float numerator_08 = -0.32192809488736229f * nn / kFftLengthBy2; + constexpr int kNumSectionsToAnalyze = 9; + + if (n_sections_ < kNumSectionsToAnalyze) { + return 0; + } + + // Estimation of the blocks that correspond to early reverberations. The + // estimation is done by analyzing the impulse response. The portions of the + // impulse response whose energy is not decreasing over its coefficients are + // considered to be part of the early reverberations. Furthermore, the blocks + // where the energy is decreasing faster than what it does at the end of the + // impulse response are also considered to be part of the early + // reverberations. The estimation is limited to the first + // kNumSectionsToAnalyze sections. + + RTC_DCHECK_LE(n_sections_, numerators_smooth_.size()); + const float min_numerator_tail = + *std::min_element(numerators_smooth_.begin() + kNumSectionsToAnalyze, + numerators_smooth_.begin() + n_sections_); + int early_reverb_size_minus_1 = 0; + for (int k = 0; k < kNumSectionsToAnalyze; ++k) { + if ((numerators_smooth_[k] > numerator_11) || + (numerators_smooth_[k] < numerator_08 && + numerators_smooth_[k] < 0.9f * min_numerator_tail)) { + early_reverb_size_minus_1 = k; + } + } + + return early_reverb_size_minus_1 == 0 ? 0 : early_reverb_size_minus_1 + 1; +} + +void ReverbDecayEstimator::EarlyReverbLengthEstimator::Dump( + ApmDataDumper* data_dumper) const { + data_dumper->DumpRaw("aec3_er_acum_numerator", numerators_smooth_); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.h new file mode 100644 index 00000000..4fcc8897 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_REVERB_DECAY_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_REVERB_DECAY_ESTIMATOR_H_ + +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" // kMaxAdaptiveFilter... + +namespace webrtc { + +class ApmDataDumper; +struct EchoCanceller3Config; + +// Class for estimating the decay of the late reverb. +class ReverbDecayEstimator { + public: + explicit ReverbDecayEstimator(const EchoCanceller3Config& config); + ~ReverbDecayEstimator(); + // Updates the decay estimate. + void Update(ArrayView filter, + const std::optional& filter_quality, + int filter_delay_blocks, + bool usable_linear_filter, + bool stationary_signal); + // Returns the decay for the exponential model. The parameter `mild` indicates + // which exponential decay to return, the default one or a milder one. + float Decay(bool mild) const { + if (use_adaptive_echo_decay_) { + return decay_; + } else { + return mild ? mild_decay_ : decay_; + } + } + // Dumps debug data. + void Dump(ApmDataDumper* data_dumper) const; + + private: + void EstimateDecay(ArrayView filter, int peak_block); + void AnalyzeFilter(ArrayView filter); + + void ResetDecayEstimation(); + + // Class for estimating the decay of the late reverb from the linear filter. + class LateReverbLinearRegressor { + public: + // Resets the estimator to receive a specified number of data points. + void Reset(int num_data_points); + // Accumulates estimation data. + void Accumulate(float z); + // Estimates the decay. + float Estimate(); + // Returns whether an estimate is available. + bool EstimateAvailable() const { return n_ == N_ && N_ != 0; } + + public: + float nz_ = 0.f; + float nn_ = 0.f; + float count_ = 0.f; + int N_ = 0; + int n_ = 0; + }; + + // Class for identifying the length of the early reverb from the linear + // filter. For identifying the early reverberations, the impulse response is + // divided in sections and the tilt of each section is computed by a linear + // regressor. + class EarlyReverbLengthEstimator { + public: + explicit EarlyReverbLengthEstimator(int max_blocks); + ~EarlyReverbLengthEstimator(); + + // Resets the estimator. + void Reset(); + // Accumulates estimation data. + void Accumulate(float value, float smoothing); + // Estimates the size in blocks of the early reverb. + int Estimate(); + // Dumps debug data. + void Dump(ApmDataDumper* data_dumper) const; + + private: + std::vector numerators_smooth_; + std::vector numerators_; + int coefficients_counter_; + int block_counter_ = 0; + int n_sections_ = 0; + }; + + const int filter_length_blocks_; + const int filter_length_coefficients_; + const bool use_adaptive_echo_decay_; + LateReverbLinearRegressor late_reverb_decay_estimator_; + EarlyReverbLengthEstimator early_reverb_estimator_; + int late_reverb_start_; + int late_reverb_end_; + int block_to_analyze_ = 0; + int estimation_region_candidate_size_ = 0; + bool estimation_region_identified_ = false; + std::vector previous_gains_; + float decay_; + float mild_decay_; + float tail_gain_ = 0.f; + float smoothing_constant_ = 0.f; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_REVERB_DECAY_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_frequency_response.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_frequency_response.cc new file mode 100644 index 00000000..508b0b82 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_frequency_response.cc @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/reverb_frequency_response.h" + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Computes the ratio of the energies between the direct path and the tail. The +// energy is computed in the power spectrum domain discarding the DC +// contributions. +float AverageDecayWithinFilter(ArrayView freq_resp_direct_path, + ArrayView freq_resp_tail) { + // Skipping the DC for the ratio computation + constexpr size_t kSkipBins = 1; + RTC_CHECK_EQ(freq_resp_direct_path.size(), freq_resp_tail.size()); + + float direct_path_energy = + std::accumulate(freq_resp_direct_path.begin() + kSkipBins, + freq_resp_direct_path.end(), 0.f); + + if (direct_path_energy == 0.f) { + return 0.f; + } + + float tail_energy = std::accumulate(freq_resp_tail.begin() + kSkipBins, + freq_resp_tail.end(), 0.f); + return tail_energy / direct_path_energy; +} + +} // namespace + +ReverbFrequencyResponse::ReverbFrequencyResponse( + bool use_conservative_tail_frequency_response) + : use_conservative_tail_frequency_response_( + use_conservative_tail_frequency_response) { + tail_response_.fill(0.0f); +} + +ReverbFrequencyResponse::~ReverbFrequencyResponse() = default; + +void ReverbFrequencyResponse::Update( + const std::vector>& + frequency_response, + int filter_delay_blocks, + const std::optional& linear_filter_quality, + bool stationary_block) { + if (stationary_block || !linear_filter_quality) { + return; + } + + Update(frequency_response, filter_delay_blocks, *linear_filter_quality); +} + +void ReverbFrequencyResponse::Update( + const std::vector>& + frequency_response, + int filter_delay_blocks, + float linear_filter_quality) { + ArrayView freq_resp_tail( + frequency_response[frequency_response.size() - 1]); + + ArrayView freq_resp_direct_path( + frequency_response[filter_delay_blocks]); + + float average_decay = + AverageDecayWithinFilter(freq_resp_direct_path, freq_resp_tail); + + const float smoothing = 0.2f * linear_filter_quality; + average_decay_ += smoothing * (average_decay - average_decay_); + + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + tail_response_[k] = freq_resp_direct_path[k] * average_decay_; + } + + if (use_conservative_tail_frequency_response_) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + tail_response_[k] = std::max(freq_resp_tail[k], tail_response_[k]); + } + } + + for (size_t k = 1; k < kFftLengthBy2; ++k) { + const float avg_neighbour = + 0.5f * (tail_response_[k - 1] + tail_response_[k + 1]); + tail_response_[k] = std::max(tail_response_[k], avg_neighbour); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_frequency_response.h b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_frequency_response.h new file mode 100644 index 00000000..3fecba73 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_frequency_response.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_REVERB_FREQUENCY_RESPONSE_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_REVERB_FREQUENCY_RESPONSE_H_ + +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// Class for updating the frequency response for the reverb. +class ReverbFrequencyResponse { + public: + explicit ReverbFrequencyResponse( + bool use_conservative_tail_frequency_response); + ~ReverbFrequencyResponse(); + + // Updates the frequency response estimate of the reverb. + void Update(const std::vector>& + frequency_response, + int filter_delay_blocks, + const std::optional& linear_filter_quality, + bool stationary_block); + + // Returns the estimated frequency response for the reverb. + ArrayView FrequencyResponse() const { return tail_response_; } + + private: + void Update(const std::vector>& + frequency_response, + int filter_delay_blocks, + float linear_filter_quality); + + const bool use_conservative_tail_frequency_response_; + float average_decay_ = 0.f; + std::array tail_response_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_REVERB_FREQUENCY_RESPONSE_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model.cc new file mode 100644 index 00000000..35816961 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/reverb_model.h" + +#include + +#include +#include + +#include "api/array_view.h" + +namespace webrtc { + +ReverbModel::ReverbModel() { + Reset(); +} + +ReverbModel::~ReverbModel() = default; + +void ReverbModel::Reset() { + reverb_.fill(0.); +} + +void ReverbModel::UpdateReverbNoFreqShaping( + ArrayView power_spectrum, + float power_spectrum_scaling, + float reverb_decay) { + if (reverb_decay > 0) { + // Update the estimate of the reverberant power. + for (size_t k = 0; k < power_spectrum.size(); ++k) { + reverb_[k] = (reverb_[k] + power_spectrum[k] * power_spectrum_scaling) * + reverb_decay; + } + } +} + +void ReverbModel::UpdateReverb(ArrayView power_spectrum, + ArrayView power_spectrum_scaling, + float reverb_decay) { + if (reverb_decay > 0) { + // Update the estimate of the reverberant power. + for (size_t k = 0; k < power_spectrum.size(); ++k) { + reverb_[k] = + (reverb_[k] + power_spectrum[k] * power_spectrum_scaling[k]) * + reverb_decay; + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model.h b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model.h new file mode 100644 index 00000000..15e24895 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// The ReverbModel class describes an exponential reverberant model +// that can be applied over power spectrums. +class ReverbModel { + public: + ReverbModel(); + ~ReverbModel(); + + // Resets the state. + void Reset(); + + // Returns the reverb. + ArrayView reverb() const { return reverb_; } + + // The methods UpdateReverbNoFreqShaping and UpdateReverb update the + // estimate of the reverberation contribution to an input/output power + // spectrum. Before applying the exponential reverberant model, the input + // power spectrum is pre-scaled. Use the method UpdateReverb when a different + // scaling should be applied per frequency and UpdateReverb_no_freq_shape if + // the same scaling should be used for all the frequencies. + void UpdateReverbNoFreqShaping(ArrayView power_spectrum, + float power_spectrum_scaling, + float reverb_decay); + + // Update the reverb based on new data. + void UpdateReverb(ArrayView power_spectrum, + ArrayView power_spectrum_scaling, + float reverb_decay); + + private: + std::array reverb_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model_estimator.cc new file mode 100644 index 00000000..398b906e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model_estimator.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/reverb_model_estimator.h" + +namespace webrtc { + +ReverbModelEstimator::ReverbModelEstimator(const EchoCanceller3Config& config, + size_t num_capture_channels) + : reverb_decay_estimators_(num_capture_channels), + reverb_frequency_responses_( + num_capture_channels, + ReverbFrequencyResponse( + config.ep_strength.use_conservative_tail_frequency_response)) { + for (size_t ch = 0; ch < reverb_decay_estimators_.size(); ++ch) { + reverb_decay_estimators_[ch] = + std::make_unique(config); + } +} + +ReverbModelEstimator::~ReverbModelEstimator() = default; + +void ReverbModelEstimator::Update( + ArrayView> impulse_responses, + ArrayView>> + frequency_responses, + ArrayView> linear_filter_qualities, + ArrayView filter_delays_blocks, + const std::vector& usable_linear_estimates, + bool stationary_block) { + const size_t num_capture_channels = reverb_decay_estimators_.size(); + RTC_DCHECK_EQ(num_capture_channels, impulse_responses.size()); + RTC_DCHECK_EQ(num_capture_channels, frequency_responses.size()); + RTC_DCHECK_EQ(num_capture_channels, usable_linear_estimates.size()); + + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + // Estimate the frequency response for the reverb. + reverb_frequency_responses_[ch].Update( + frequency_responses[ch], filter_delays_blocks[ch], + linear_filter_qualities[ch], stationary_block); + + // Estimate the reverb decay, + reverb_decay_estimators_[ch]->Update( + impulse_responses[ch], linear_filter_qualities[ch], + filter_delays_blocks[ch], usable_linear_estimates[ch], + stationary_block); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model_estimator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model_estimator.h new file mode 100644 index 00000000..8994a4a5 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/reverb_model_estimator.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_ESTIMATOR_H_ + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" // kFftLengthBy2Plus1 +#include "modules/audio_processing/aec3/reverb_decay_estimator.h" +#include "modules/audio_processing/aec3/reverb_frequency_response.h" + +namespace webrtc { + +class ApmDataDumper; + +// Class for estimating the model parameters for the reverberant echo. +class ReverbModelEstimator { + public: + ReverbModelEstimator(const EchoCanceller3Config& config, + size_t num_capture_channels); + ~ReverbModelEstimator(); + + // Updates the estimates based on new data. + void Update( + ArrayView> impulse_responses, + ArrayView>> + frequency_responses, + ArrayView> linear_filter_qualities, + ArrayView filter_delays_blocks, + const std::vector& usable_linear_estimates, + bool stationary_block); + + // Returns the exponential decay of the reverberant echo. The parameter `mild` + // indicates which exponential decay to return, the default one or a milder + // one. + // TODO(peah): Correct to properly support multiple channels. + float ReverbDecay(bool mild) const { + return reverb_decay_estimators_[0]->Decay(mild); + } + + // Return the frequency response of the reverberant echo. + // TODO(peah): Correct to properly support multiple channels. + ArrayView GetReverbFrequencyResponse() const { + return reverb_frequency_responses_[0].FrequencyResponse(); + } + + // Dumps debug data. + void Dump(ApmDataDumper* data_dumper) const { + reverb_decay_estimators_[0]->Dump(data_dumper); + } + + private: + std::vector> reverb_decay_estimators_; + std::vector reverb_frequency_responses_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc new file mode 100644 index 00000000..b2bdb27c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/signal_dependent_erle_estimator.h" + +#include +#include +#include + +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +namespace { + +constexpr std::array + kBandBoundaries = {1, 8, 16, 24, 32, 48, kFftLengthBy2Plus1}; + +std::array FormSubbandMap() { + std::array map_band_to_subband; + size_t subband = 1; + for (size_t k = 0; k < map_band_to_subband.size(); ++k) { + RTC_DCHECK_LT(subband, kBandBoundaries.size()); + if (k >= kBandBoundaries[subband]) { + subband++; + RTC_DCHECK_LT(k, kBandBoundaries[subband]); + } + map_band_to_subband[k] = subband - 1; + } + return map_band_to_subband; +} + +// Defines the size in blocks of the sections that are used for dividing the +// linear filter. The sections are split in a non-linear manner so that lower +// sections that typically represent the direct path have a larger resolution +// than the higher sections which typically represent more reverberant acoustic +// paths. +std::vector DefineFilterSectionSizes(size_t delay_headroom_blocks, + size_t num_blocks, + size_t num_sections) { + size_t filter_length_blocks = num_blocks - delay_headroom_blocks; + std::vector section_sizes(num_sections); + size_t remaining_blocks = filter_length_blocks; + size_t remaining_sections = num_sections; + size_t estimator_size = 2; + size_t idx = 0; + while (remaining_sections > 1 && + remaining_blocks > estimator_size * remaining_sections) { + RTC_DCHECK_LT(idx, section_sizes.size()); + section_sizes[idx] = estimator_size; + remaining_blocks -= estimator_size; + remaining_sections--; + estimator_size *= 2; + idx++; + } + + size_t last_groups_size = remaining_blocks / remaining_sections; + for (; idx < num_sections; idx++) { + section_sizes[idx] = last_groups_size; + } + section_sizes[num_sections - 1] += + remaining_blocks - last_groups_size * remaining_sections; + return section_sizes; +} + +// Forms the limits in blocks for each filter section. Those sections +// are used for analyzing the echo estimates and investigating which +// linear filter sections contribute most to the echo estimate energy. +std::vector SetSectionsBoundaries(size_t delay_headroom_blocks, + size_t num_blocks, + size_t num_sections) { + std::vector estimator_boundaries_blocks(num_sections + 1); + if (estimator_boundaries_blocks.size() == 2) { + estimator_boundaries_blocks[0] = 0; + estimator_boundaries_blocks[1] = num_blocks; + return estimator_boundaries_blocks; + } + RTC_DCHECK_GT(estimator_boundaries_blocks.size(), 2); + const std::vector section_sizes = + DefineFilterSectionSizes(delay_headroom_blocks, num_blocks, + estimator_boundaries_blocks.size() - 1); + + size_t idx = 0; + size_t current_size_block = 0; + RTC_DCHECK_EQ(section_sizes.size() + 1, estimator_boundaries_blocks.size()); + estimator_boundaries_blocks[0] = delay_headroom_blocks; + for (size_t k = delay_headroom_blocks; k < num_blocks; ++k) { + current_size_block++; + if (current_size_block >= section_sizes[idx]) { + idx = idx + 1; + if (idx == section_sizes.size()) { + break; + } + estimator_boundaries_blocks[idx] = k + 1; + current_size_block = 0; + } + } + estimator_boundaries_blocks[section_sizes.size()] = num_blocks; + return estimator_boundaries_blocks; +} + +std::array +SetMaxErleSubbands(float max_erle_l, float max_erle_h, size_t limit_subband_l) { + std::array max_erle; + std::fill(max_erle.begin(), max_erle.begin() + limit_subband_l, max_erle_l); + std::fill(max_erle.begin() + limit_subband_l, max_erle.end(), max_erle_h); + return max_erle; +} + +} // namespace + +SignalDependentErleEstimator::SignalDependentErleEstimator( + const EchoCanceller3Config& config, + size_t num_capture_channels) + : min_erle_(config.erle.min), + num_sections_(config.erle.num_sections), + num_blocks_(config.filter.refined.length_blocks), + delay_headroom_blocks_(config.delay.delay_headroom_samples / kBlockSize), + band_to_subband_(FormSubbandMap()), + max_erle_(SetMaxErleSubbands(config.erle.max_l, + config.erle.max_h, + band_to_subband_[kFftLengthBy2 / 2])), + section_boundaries_blocks_(SetSectionsBoundaries(delay_headroom_blocks_, + num_blocks_, + num_sections_)), + use_onset_detection_(config.erle.onset_detection), + erle_(num_capture_channels), + erle_onset_compensated_(num_capture_channels), + S2_section_accum_( + num_capture_channels, + std::vector>(num_sections_)), + erle_estimators_( + num_capture_channels, + std::vector>(num_sections_)), + erle_ref_(num_capture_channels), + correction_factors_( + num_capture_channels, + std::vector>(num_sections_)), + num_updates_(num_capture_channels), + n_active_sections_(num_capture_channels) { + RTC_DCHECK_LE(num_sections_, num_blocks_); + RTC_DCHECK_GE(num_sections_, 1); + Reset(); +} + +SignalDependentErleEstimator::~SignalDependentErleEstimator() = default; + +void SignalDependentErleEstimator::Reset() { + for (size_t ch = 0; ch < erle_.size(); ++ch) { + erle_[ch].fill(min_erle_); + erle_onset_compensated_[ch].fill(min_erle_); + for (auto& erle_estimator : erle_estimators_[ch]) { + erle_estimator.fill(min_erle_); + } + erle_ref_[ch].fill(min_erle_); + for (auto& factor : correction_factors_[ch]) { + factor.fill(1.0f); + } + num_updates_[ch].fill(0); + n_active_sections_[ch].fill(0); + } +} + +// Updates the Erle estimate by analyzing the current input signals. It takes +// the render buffer and the filter frequency response in order to do an +// estimation of the number of sections of the linear filter that are needed +// for getting the majority of the energy in the echo estimate. Based on that +// number of sections, it updates the erle estimation by introducing a +// correction factor to the erle that is given as an input to this method. +void SignalDependentErleEstimator::Update( + const RenderBuffer& render_buffer, + ArrayView>> + filter_frequency_responses, + ArrayView X2, + ArrayView> Y2, + ArrayView> E2, + ArrayView> average_erle, + ArrayView> + average_erle_onset_compensated, + const std::vector& converged_filters) { + RTC_DCHECK_GT(num_sections_, 1); + + // Gets the number of filter sections that are needed for achieving 90 % + // of the power spectrum energy of the echo estimate. + ComputeNumberOfActiveFilterSections(render_buffer, + filter_frequency_responses); + + // Updates the correction factors that is used for correcting the erle and + // adapt it to the particular characteristics of the input signal. + UpdateCorrectionFactors(X2, Y2, E2, converged_filters); + + // Applies the correction factor to the input erle for getting a more refined + // erle estimation for the current input signal. + for (size_t ch = 0; ch < erle_.size(); ++ch) { + for (size_t k = 0; k < kFftLengthBy2; ++k) { + RTC_DCHECK_GT(correction_factors_[ch].size(), n_active_sections_[ch][k]); + float correction_factor = + correction_factors_[ch][n_active_sections_[ch][k]] + [band_to_subband_[k]]; + erle_[ch][k] = SafeClamp(average_erle[ch][k] * correction_factor, + min_erle_, max_erle_[band_to_subband_[k]]); + if (use_onset_detection_) { + erle_onset_compensated_[ch][k] = + SafeClamp(average_erle_onset_compensated[ch][k] * correction_factor, + min_erle_, max_erle_[band_to_subband_[k]]); + } + } + } +} + +void SignalDependentErleEstimator::Dump( + const std::unique_ptr& data_dumper) const { + for (auto& erle : erle_estimators_[0]) { + data_dumper->DumpRaw("aec3_all_erle", erle); + } + data_dumper->DumpRaw("aec3_ref_erle", erle_ref_[0]); + for (auto& factor : correction_factors_[0]) { + data_dumper->DumpRaw("aec3_erle_correction_factor", factor); + } +} + +// Estimates for each band the smallest number of sections in the filter that +// together constitute 90% of the estimated echo energy. +void SignalDependentErleEstimator::ComputeNumberOfActiveFilterSections( + const RenderBuffer& render_buffer, + ArrayView>> + filter_frequency_responses) { + RTC_DCHECK_GT(num_sections_, 1); + // Computes an approximation of the power spectrum if the filter would have + // been limited to a certain number of filter sections. + ComputeEchoEstimatePerFilterSection(render_buffer, + filter_frequency_responses); + // For each band, computes the number of filter sections that are needed for + // achieving the 90 % energy in the echo estimate. + ComputeActiveFilterSections(); +} + +void SignalDependentErleEstimator::UpdateCorrectionFactors( + ArrayView X2, + ArrayView> Y2, + ArrayView> E2, + const std::vector& converged_filters) { + for (size_t ch = 0; ch < converged_filters.size(); ++ch) { + if (converged_filters[ch]) { + constexpr float kX2BandEnergyThreshold = 44015068.0f; + constexpr float kSmthConstantDecreases = 0.1f; + constexpr float kSmthConstantIncreases = kSmthConstantDecreases / 2.f; + auto subband_powers = [](ArrayView power_spectrum, + ArrayView power_spectrum_subbands) { + for (size_t subband = 0; subband < kSubbands; ++subband) { + RTC_DCHECK_LE(kBandBoundaries[subband + 1], power_spectrum.size()); + power_spectrum_subbands[subband] = std::accumulate( + power_spectrum.begin() + kBandBoundaries[subband], + power_spectrum.begin() + kBandBoundaries[subband + 1], 0.f); + } + }; + + std::array X2_subbands, E2_subbands, Y2_subbands; + subband_powers(X2, X2_subbands); + subband_powers(E2[ch], E2_subbands); + subband_powers(Y2[ch], Y2_subbands); + std::array idx_subbands; + for (size_t subband = 0; subband < kSubbands; ++subband) { + // When aggregating the number of active sections in the filter for + // different bands we choose to take the minimum of all of them. As an + // example, if for one of the bands it is the direct path its refined + // contributor to the final echo estimate, we consider the direct path + // is as well the refined contributor for the subband that contains that + // particular band. That aggregate number of sections will be later used + // as the identifier of the erle estimator that needs to be updated. + RTC_DCHECK_LE(kBandBoundaries[subband + 1], + n_active_sections_[ch].size()); + idx_subbands[subband] = *std::min_element( + n_active_sections_[ch].begin() + kBandBoundaries[subband], + n_active_sections_[ch].begin() + kBandBoundaries[subband + 1]); + } + + std::array new_erle; + std::array is_erle_updated; + is_erle_updated.fill(false); + new_erle.fill(0.f); + for (size_t subband = 0; subband < kSubbands; ++subband) { + if (X2_subbands[subband] > kX2BandEnergyThreshold && + E2_subbands[subband] > 0) { + new_erle[subband] = Y2_subbands[subband] / E2_subbands[subband]; + RTC_DCHECK_GT(new_erle[subband], 0); + is_erle_updated[subband] = true; + ++num_updates_[ch][subband]; + } + } + + for (size_t subband = 0; subband < kSubbands; ++subband) { + const size_t idx = idx_subbands[subband]; + RTC_DCHECK_LT(idx, erle_estimators_[ch].size()); + float alpha = new_erle[subband] > erle_estimators_[ch][idx][subband] + ? kSmthConstantIncreases + : kSmthConstantDecreases; + alpha = static_cast(is_erle_updated[subband]) * alpha; + erle_estimators_[ch][idx][subband] += + alpha * (new_erle[subband] - erle_estimators_[ch][idx][subband]); + erle_estimators_[ch][idx][subband] = SafeClamp( + erle_estimators_[ch][idx][subband], min_erle_, max_erle_[subband]); + } + + for (size_t subband = 0; subband < kSubbands; ++subband) { + float alpha = new_erle[subband] > erle_ref_[ch][subband] + ? kSmthConstantIncreases + : kSmthConstantDecreases; + alpha = static_cast(is_erle_updated[subband]) * alpha; + erle_ref_[ch][subband] += + alpha * (new_erle[subband] - erle_ref_[ch][subband]); + erle_ref_[ch][subband] = + SafeClamp(erle_ref_[ch][subband], min_erle_, max_erle_[subband]); + } + + for (size_t subband = 0; subband < kSubbands; ++subband) { + constexpr int kNumUpdateThr = 50; + if (is_erle_updated[subband] && + num_updates_[ch][subband] > kNumUpdateThr) { + const size_t idx = idx_subbands[subband]; + RTC_DCHECK_GT(erle_ref_[ch][subband], 0.f); + // Computes the ratio between the erle that is updated using all the + // points and the erle that is updated only on signals that share the + // same number of active filter sections. + float new_correction_factor = + erle_estimators_[ch][idx][subband] / erle_ref_[ch][subband]; + + correction_factors_[ch][idx][subband] += + 0.1f * + (new_correction_factor - correction_factors_[ch][idx][subband]); + } + } + } + } +} + +void SignalDependentErleEstimator::ComputeEchoEstimatePerFilterSection( + const RenderBuffer& render_buffer, + ArrayView>> + filter_frequency_responses) { + const SpectrumBuffer& spectrum_render_buffer = + render_buffer.GetSpectrumBuffer(); + const size_t num_render_channels = spectrum_render_buffer.buffer[0].size(); + const size_t num_capture_channels = S2_section_accum_.size(); + const float one_by_num_render_channels = 1.f / num_render_channels; + + RTC_DCHECK_EQ(S2_section_accum_.size(), filter_frequency_responses.size()); + + for (size_t capture_ch = 0; capture_ch < num_capture_channels; ++capture_ch) { + RTC_DCHECK_EQ(S2_section_accum_[capture_ch].size() + 1, + section_boundaries_blocks_.size()); + size_t idx_render = render_buffer.Position(); + idx_render = spectrum_render_buffer.OffsetIndex( + idx_render, section_boundaries_blocks_[0]); + + for (size_t section = 0; section < num_sections_; ++section) { + std::array X2_section; + std::array H2_section; + X2_section.fill(0.f); + H2_section.fill(0.f); + const size_t block_limit = + std::min(section_boundaries_blocks_[section + 1], + filter_frequency_responses[capture_ch].size()); + for (size_t block = section_boundaries_blocks_[section]; + block < block_limit; ++block) { + for (size_t render_ch = 0; + render_ch < spectrum_render_buffer.buffer[idx_render].size(); + ++render_ch) { + for (size_t k = 0; k < X2_section.size(); ++k) { + X2_section[k] += + spectrum_render_buffer.buffer[idx_render][render_ch][k] * + one_by_num_render_channels; + } + } + std::transform(H2_section.begin(), H2_section.end(), + filter_frequency_responses[capture_ch][block].begin(), + H2_section.begin(), std::plus()); + idx_render = spectrum_render_buffer.IncIndex(idx_render); + } + + std::transform(X2_section.begin(), X2_section.end(), H2_section.begin(), + S2_section_accum_[capture_ch][section].begin(), + std::multiplies()); + } + + for (size_t section = 1; section < num_sections_; ++section) { + std::transform(S2_section_accum_[capture_ch][section - 1].begin(), + S2_section_accum_[capture_ch][section - 1].end(), + S2_section_accum_[capture_ch][section].begin(), + S2_section_accum_[capture_ch][section].begin(), + std::plus()); + } + } +} + +void SignalDependentErleEstimator::ComputeActiveFilterSections() { + for (size_t ch = 0; ch < n_active_sections_.size(); ++ch) { + std::fill(n_active_sections_[ch].begin(), n_active_sections_[ch].end(), 0); + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + size_t section = num_sections_; + float target = 0.9f * S2_section_accum_[ch][num_sections_ - 1][k]; + while (section > 0 && S2_section_accum_[ch][section - 1][k] >= target) { + n_active_sections_[ch][k] = --section; + } + } + } +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.h new file mode 100644 index 00000000..d46fd19e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SIGNAL_DEPENDENT_ERLE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SIGNAL_DEPENDENT_ERLE_ESTIMATOR_H_ + +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +// This class estimates the dependency of the Erle to the input signal. By +// looking at the input signal, an estimation on whether the current echo +// estimate is due to the direct path or to a more reverberant one is performed. +// Once that estimation is done, it is possible to refine the average Erle that +// this class receive as an input. +class SignalDependentErleEstimator { + public: + SignalDependentErleEstimator(const EchoCanceller3Config& config, + size_t num_capture_channels); + + ~SignalDependentErleEstimator(); + + void Reset(); + + // Returns the Erle per frequency subband. + ArrayView> Erle( + bool onset_compensated) const { + return onset_compensated && use_onset_detection_ ? erle_onset_compensated_ + : erle_; + } + + // Updates the Erle estimate. The Erle that is passed as an input is required + // to be an estimation of the average Erle achieved by the linear filter. + void Update( + const RenderBuffer& render_buffer, + ArrayView>> + filter_frequency_response, + ArrayView X2, + ArrayView> Y2, + ArrayView> E2, + ArrayView> average_erle, + ArrayView> + average_erle_onset_compensated, + const std::vector& converged_filters); + + void Dump(const std::unique_ptr& data_dumper) const; + + static constexpr size_t kSubbands = 6; + + private: + void ComputeNumberOfActiveFilterSections( + const RenderBuffer& render_buffer, + ArrayView>> + filter_frequency_responses); + + void UpdateCorrectionFactors( + ArrayView X2, + ArrayView> Y2, + ArrayView> E2, + const std::vector& converged_filters); + + void ComputeEchoEstimatePerFilterSection( + const RenderBuffer& render_buffer, + ArrayView>> + filter_frequency_responses); + + void ComputeActiveFilterSections(); + + const float min_erle_; + const size_t num_sections_; + const size_t num_blocks_; + const size_t delay_headroom_blocks_; + const std::array band_to_subband_; + const std::array max_erle_; + const std::vector section_boundaries_blocks_; + const bool use_onset_detection_; + std::vector> erle_; + std::vector> erle_onset_compensated_; + std::vector>> + S2_section_accum_; + std::vector>> erle_estimators_; + std::vector> erle_ref_; + std::vector>> correction_factors_; + std::vector> num_updates_; + std::vector> n_active_sections_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SIGNAL_DEPENDENT_ERLE_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/spectrum_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/spectrum_buffer.cc new file mode 100644 index 00000000..fe32ece0 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/spectrum_buffer.cc @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/spectrum_buffer.h" + +#include + +namespace webrtc { + +SpectrumBuffer::SpectrumBuffer(size_t size, size_t num_channels) + : size(static_cast(size)), + buffer(size, + std::vector>(num_channels)) { + for (auto& channel : buffer) { + for (auto& c : channel) { + std::fill(c.begin(), c.end(), 0.f); + } + } +} + +SpectrumBuffer::~SpectrumBuffer() = default; + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/spectrum_buffer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/spectrum_buffer.h new file mode 100644 index 00000000..51e1317f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/spectrum_buffer.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SPECTRUM_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SPECTRUM_BUFFER_H_ + +#include + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Struct for bundling a circular buffer of one dimensional vector objects +// together with the read and write indices. +struct SpectrumBuffer { + SpectrumBuffer(size_t size, size_t num_channels); + ~SpectrumBuffer(); + + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; + } + + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index > 0 ? index - 1 : size - 1; + } + + int OffsetIndex(int index, int offset) const { + RTC_DCHECK_GE(size, offset); + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + RTC_DCHECK_GE(size + index + offset, 0); + return (size + index + offset) % size; + } + + void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } + void IncWriteIndex() { write = IncIndex(write); } + void DecWriteIndex() { write = DecIndex(write); } + void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); } + void IncReadIndex() { read = IncIndex(read); } + void DecReadIndex() { read = DecIndex(read); } + + const int size; + std::vector>> buffer; + int write = 0; + int read = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SPECTRUM_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/stationarity_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/stationarity_estimator.cc new file mode 100644 index 00000000..b6da56a3 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/stationarity_estimator.cc @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/stationarity_estimator.h" + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +namespace { +constexpr float kMinNoisePower = 10.f; +constexpr int kHangoverBlocks = kNumBlocksPerSecond / 20; +constexpr int kNBlocksAverageInitPhase = 20; +constexpr int kNBlocksInitialPhase = kNumBlocksPerSecond * 2.; +} // namespace + +StationarityEstimator::StationarityEstimator() + : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)) { + Reset(); +} + +StationarityEstimator::~StationarityEstimator() = default; + +void StationarityEstimator::Reset() { + noise_.Reset(); + hangovers_.fill(0); + stationarity_flags_.fill(false); +} + +// Update just the noise estimator. Usefull until the delay is known +void StationarityEstimator::UpdateNoiseEstimator( + ArrayView> spectrum) { + noise_.Update(spectrum); + data_dumper_->DumpRaw("aec3_stationarity_noise_spectrum", noise_.Spectrum()); + data_dumper_->DumpRaw("aec3_stationarity_is_block_stationary", + IsBlockStationary()); +} + +void StationarityEstimator::UpdateStationarityFlags( + const SpectrumBuffer& spectrum_buffer, + ArrayView render_reverb_contribution_spectrum, + int idx_current, + int num_lookahead) { + std::array indexes; + int num_lookahead_bounded = std::min(num_lookahead, kWindowLength - 1); + int idx = idx_current; + + if (num_lookahead_bounded < kWindowLength - 1) { + int num_lookback = (kWindowLength - 1) - num_lookahead_bounded; + idx = spectrum_buffer.OffsetIndex(idx_current, num_lookback); + } + // For estimating the stationarity properties of the current frame, the + // power for each band is accumulated for several consecutive spectra in the + // method EstimateBandStationarity. + // In order to avoid getting the indexes of the spectra for every band with + // its associated overhead, those indexes are stored in an array and then use + // when the estimation is done. + indexes[0] = idx; + for (size_t k = 1; k < indexes.size(); ++k) { + indexes[k] = spectrum_buffer.DecIndex(indexes[k - 1]); + } + RTC_DCHECK_EQ( + spectrum_buffer.DecIndex(indexes[kWindowLength - 1]), + spectrum_buffer.OffsetIndex(idx_current, -(num_lookahead_bounded + 1))); + + for (size_t k = 0; k < stationarity_flags_.size(); ++k) { + stationarity_flags_[k] = EstimateBandStationarity( + spectrum_buffer, render_reverb_contribution_spectrum, indexes, k); + } + UpdateHangover(); + SmoothStationaryPerFreq(); +} + +bool StationarityEstimator::IsBlockStationary() const { + float acum_stationarity = 0.f; + RTC_DCHECK_EQ(stationarity_flags_.size(), kFftLengthBy2Plus1); + for (size_t band = 0; band < stationarity_flags_.size(); ++band) { + bool st = IsBandStationary(band); + acum_stationarity += static_cast(st); + } + return ((acum_stationarity * (1.f / kFftLengthBy2Plus1)) > 0.75f); +} + +bool StationarityEstimator::EstimateBandStationarity( + const SpectrumBuffer& spectrum_buffer, + ArrayView average_reverb, + const std::array& indexes, + size_t band) const { + constexpr float kThrStationarity = 10.f; + float acum_power = 0.f; + const int num_render_channels = + static_cast(spectrum_buffer.buffer[0].size()); + const float one_by_num_channels = 1.f / num_render_channels; + for (auto idx : indexes) { + for (int ch = 0; ch < num_render_channels; ++ch) { + acum_power += spectrum_buffer.buffer[idx][ch][band] * one_by_num_channels; + } + } + acum_power += average_reverb[band]; + float noise = kWindowLength * GetStationarityPowerBand(band); + RTC_CHECK_LT(0.f, noise); + bool stationary = acum_power < kThrStationarity * noise; + data_dumper_->DumpRaw("aec3_stationarity_long_ratio", acum_power / noise); + return stationary; +} + +bool StationarityEstimator::AreAllBandsStationary() { + for (auto b : stationarity_flags_) { + if (!b) + return false; + } + return true; +} + +void StationarityEstimator::UpdateHangover() { + bool reduce_hangover = AreAllBandsStationary(); + for (size_t k = 0; k < stationarity_flags_.size(); ++k) { + if (!stationarity_flags_[k]) { + hangovers_[k] = kHangoverBlocks; + } else if (reduce_hangover) { + hangovers_[k] = std::max(hangovers_[k] - 1, 0); + } + } +} + +void StationarityEstimator::SmoothStationaryPerFreq() { + std::array all_ahead_stationary_smooth; + for (size_t k = 1; k < kFftLengthBy2Plus1 - 1; ++k) { + all_ahead_stationary_smooth[k] = stationarity_flags_[k - 1] && + stationarity_flags_[k] && + stationarity_flags_[k + 1]; + } + + all_ahead_stationary_smooth[0] = all_ahead_stationary_smooth[1]; + all_ahead_stationary_smooth[kFftLengthBy2Plus1 - 1] = + all_ahead_stationary_smooth[kFftLengthBy2Plus1 - 2]; + + stationarity_flags_ = all_ahead_stationary_smooth; +} + +std::atomic StationarityEstimator::instance_count_(0); + +StationarityEstimator::NoiseSpectrum::NoiseSpectrum() { + Reset(); +} + +StationarityEstimator::NoiseSpectrum::~NoiseSpectrum() = default; + +void StationarityEstimator::NoiseSpectrum::Reset() { + block_counter_ = 0; + noise_spectrum_.fill(kMinNoisePower); +} + +void StationarityEstimator::NoiseSpectrum::Update( + ArrayView> spectrum) { + RTC_DCHECK_LE(1, spectrum[0].size()); + const int num_render_channels = static_cast(spectrum.size()); + + std::array avg_spectrum_data; + ArrayView avg_spectrum; + if (num_render_channels == 1) { + avg_spectrum = spectrum[0]; + } else { + // For multiple channels, average the channel spectra before passing to the + // noise spectrum estimator. + avg_spectrum = avg_spectrum_data; + std::copy(spectrum[0].begin(), spectrum[0].end(), + avg_spectrum_data.begin()); + for (int ch = 1; ch < num_render_channels; ++ch) { + for (size_t k = 1; k < kFftLengthBy2Plus1; ++k) { + avg_spectrum_data[k] += spectrum[ch][k]; + } + } + + const float one_by_num_channels = 1.f / num_render_channels; + for (size_t k = 1; k < kFftLengthBy2Plus1; ++k) { + avg_spectrum_data[k] *= one_by_num_channels; + } + } + + ++block_counter_; + float alpha = GetAlpha(); + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + if (block_counter_ <= kNBlocksAverageInitPhase) { + noise_spectrum_[k] += (1.f / kNBlocksAverageInitPhase) * avg_spectrum[k]; + } else { + noise_spectrum_[k] = + UpdateBandBySmoothing(avg_spectrum[k], noise_spectrum_[k], alpha); + } + } +} + +float StationarityEstimator::NoiseSpectrum::GetAlpha() const { + constexpr float kAlpha = 0.004f; + constexpr float kAlphaInit = 0.04f; + constexpr float kTiltAlpha = (kAlphaInit - kAlpha) / kNBlocksInitialPhase; + + if (block_counter_ > (kNBlocksInitialPhase + kNBlocksAverageInitPhase)) { + return kAlpha; + } else { + return kAlphaInit - + kTiltAlpha * (block_counter_ - kNBlocksAverageInitPhase); + } +} + +float StationarityEstimator::NoiseSpectrum::UpdateBandBySmoothing( + float power_band, + float power_band_noise, + float alpha) const { + float power_band_noise_updated = power_band_noise; + if (power_band_noise < power_band) { + RTC_DCHECK_GT(power_band, 0.f); + float alpha_inc = alpha * (power_band_noise / power_band); + if (block_counter_ > kNBlocksInitialPhase) { + if (10.f * power_band_noise < power_band) { + alpha_inc *= 0.1f; + } + } + power_band_noise_updated += alpha_inc * (power_band - power_band_noise); + } else { + power_band_noise_updated += alpha * (power_band - power_band_noise); + power_band_noise_updated = + std::max(power_band_noise_updated, kMinNoisePower); + } + return power_band_noise_updated; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/stationarity_estimator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/stationarity_estimator.h new file mode 100644 index 00000000..6edc195b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/stationarity_estimator.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_STATIONARITY_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_STATIONARITY_ESTIMATOR_H_ + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" // kFftLengthBy2Plus1... +#include "modules/audio_processing/aec3/reverb_model.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +class ApmDataDumper; +struct SpectrumBuffer; + +class StationarityEstimator { + public: + StationarityEstimator(); + ~StationarityEstimator(); + + // Reset the stationarity estimator. + void Reset(); + + // Update just the noise estimator. Usefull until the delay is known + void UpdateNoiseEstimator( + ArrayView> spectrum); + + // Update the flag indicating whether this current frame is stationary. For + // getting a more robust estimation, it looks at future and/or past frames. + void UpdateStationarityFlags( + const SpectrumBuffer& spectrum_buffer, + ArrayView render_reverb_contribution_spectrum, + int idx_current, + int num_lookahead); + + // Returns true if the current band is stationary. + bool IsBandStationary(size_t band) const { + return stationarity_flags_[band] && (hangovers_[band] == 0); + } + + // Returns true if the current block is estimated as stationary. + bool IsBlockStationary() const; + + private: + static constexpr int kWindowLength = 13; + // Returns the power of the stationary noise spectrum at a band. + float GetStationarityPowerBand(size_t k) const { return noise_.Power(k); } + + // Get an estimation of the stationarity for the current band by looking + // at the past/present/future available data. + bool EstimateBandStationarity(const SpectrumBuffer& spectrum_buffer, + ArrayView average_reverb, + const std::array& indexes, + size_t band) const; + + // True if all bands at the current point are stationary. + bool AreAllBandsStationary(); + + // Update the hangover depending on the stationary status of the current + // frame. + void UpdateHangover(); + + // Smooth the stationarity detection by looking at neighbouring frequency + // bands. + void SmoothStationaryPerFreq(); + + class NoiseSpectrum { + public: + NoiseSpectrum(); + ~NoiseSpectrum(); + + // Reset the noise power spectrum estimate state. + void Reset(); + + // Update the noise power spectrum with a new frame. + void Update( + ArrayView> spectrum); + + // Get the noise estimation power spectrum. + ArrayView Spectrum() const { return noise_spectrum_; } + + // Get the noise power spectrum at a certain band. + float Power(size_t band) const { + RTC_DCHECK_LT(band, noise_spectrum_.size()); + return noise_spectrum_[band]; + } + + private: + // Get the update coefficient to be used for the current frame. + float GetAlpha() const; + + // Update the noise power spectrum at a certain band with a new frame. + float UpdateBandBySmoothing(float power_band, + float power_band_noise, + float alpha) const; + std::array noise_spectrum_; + size_t block_counter_; + }; + + static std::atomic instance_count_; + std::unique_ptr data_dumper_; + NoiseSpectrum noise_; + std::array hangovers_; + std::array stationarity_flags_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_STATIONARITY_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/subband_erle_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/subband_erle_estimator.cc new file mode 100644 index 00000000..6208d263 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/subband_erle_estimator.cc @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/subband_erle_estimator.h" + +#include +#include + +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +namespace { + +constexpr float kX2BandEnergyThreshold = 44015068.0f; +constexpr int kBlocksToHoldErle = 100; +constexpr int kBlocksForOnsetDetection = kBlocksToHoldErle + 150; +constexpr int kPointsToAccumulate = 6; + +std::array SetMaxErleBands(float max_erle_l, + float max_erle_h) { + std::array max_erle; + std::fill(max_erle.begin(), max_erle.begin() + kFftLengthBy2 / 2, max_erle_l); + std::fill(max_erle.begin() + kFftLengthBy2 / 2, max_erle.end(), max_erle_h); + return max_erle; +} + +bool EnableMinErleDuringOnsets(const FieldTrialsView& field_trials) { + return !field_trials.IsEnabled("WebRTC-Aec3MinErleDuringOnsetsKillSwitch"); +} + +} // namespace + +SubbandErleEstimator::SubbandErleEstimator(const Environment& env, + const EchoCanceller3Config& config, + size_t num_capture_channels) + : use_onset_detection_(config.erle.onset_detection), + min_erle_(config.erle.min), + max_erle_(SetMaxErleBands(config.erle.max_l, config.erle.max_h)), + use_min_erle_during_onsets_( + EnableMinErleDuringOnsets(env.field_trials())), + accum_spectra_(num_capture_channels), + erle_(num_capture_channels), + erle_onset_compensated_(num_capture_channels), + erle_unbounded_(num_capture_channels), + erle_during_onsets_(num_capture_channels), + coming_onset_(num_capture_channels), + hold_counters_(num_capture_channels) { + Reset(); +} + +SubbandErleEstimator::~SubbandErleEstimator() = default; + +void SubbandErleEstimator::Reset() { + const size_t num_capture_channels = erle_.size(); + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + erle_[ch].fill(min_erle_); + erle_onset_compensated_[ch].fill(min_erle_); + erle_unbounded_[ch].fill(min_erle_); + erle_during_onsets_[ch].fill(min_erle_); + coming_onset_[ch].fill(true); + hold_counters_[ch].fill(0); + } + ResetAccumulatedSpectra(); +} + +void SubbandErleEstimator::Update( + ArrayView X2, + ArrayView> Y2, + ArrayView> E2, + const std::vector& converged_filters) { + UpdateAccumulatedSpectra(X2, Y2, E2, converged_filters); + UpdateBands(converged_filters); + + if (use_onset_detection_) { + DecreaseErlePerBandForLowRenderSignals(); + } + + const size_t num_capture_channels = erle_.size(); + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + auto& erle = erle_[ch]; + erle[0] = erle[1]; + erle[kFftLengthBy2] = erle[kFftLengthBy2 - 1]; + + auto& erle_oc = erle_onset_compensated_[ch]; + erle_oc[0] = erle_oc[1]; + erle_oc[kFftLengthBy2] = erle_oc[kFftLengthBy2 - 1]; + + auto& erle_u = erle_unbounded_[ch]; + erle_u[0] = erle_u[1]; + erle_u[kFftLengthBy2] = erle_u[kFftLengthBy2 - 1]; + } +} + +void SubbandErleEstimator::Dump( + const std::unique_ptr& data_dumper) const { + data_dumper->DumpRaw("aec3_erle_onset", ErleDuringOnsets()[0]); +} + +void SubbandErleEstimator::UpdateBands( + const std::vector& converged_filters) { + const int num_capture_channels = static_cast(accum_spectra_.Y2.size()); + for (int ch = 0; ch < num_capture_channels; ++ch) { + // Note that the use of the converged_filter flag already imposed + // a minimum of the erle that can be estimated as that flag would + // be false if the filter is performing poorly. + if (!converged_filters[ch]) { + continue; + } + + if (accum_spectra_.num_points[ch] != kPointsToAccumulate) { + continue; + } + + std::array new_erle; + std::array is_erle_updated; + is_erle_updated.fill(false); + + for (size_t k = 1; k < kFftLengthBy2; ++k) { + if (accum_spectra_.E2[ch][k] > 0.f) { + new_erle[k] = accum_spectra_.Y2[ch][k] / accum_spectra_.E2[ch][k]; + is_erle_updated[k] = true; + } + } + + if (use_onset_detection_) { + for (size_t k = 1; k < kFftLengthBy2; ++k) { + if (is_erle_updated[k] && !accum_spectra_.low_render_energy[ch][k]) { + if (coming_onset_[ch][k]) { + coming_onset_[ch][k] = false; + if (!use_min_erle_during_onsets_) { + float alpha = + new_erle[k] < erle_during_onsets_[ch][k] ? 0.3f : 0.15f; + erle_during_onsets_[ch][k] = SafeClamp( + erle_during_onsets_[ch][k] + + alpha * (new_erle[k] - erle_during_onsets_[ch][k]), + min_erle_, max_erle_[k]); + } + } + hold_counters_[ch][k] = kBlocksForOnsetDetection; + } + } + } + + auto update_erle_band = [](float& erle, float new_erle, + bool low_render_energy, float min_erle, + float max_erle) { + float alpha = 0.05f; + if (new_erle < erle) { + alpha = low_render_energy ? 0.f : 0.1f; + } + erle = SafeClamp(erle + alpha * (new_erle - erle), min_erle, max_erle); + }; + + for (size_t k = 1; k < kFftLengthBy2; ++k) { + if (is_erle_updated[k]) { + const bool low_render_energy = accum_spectra_.low_render_energy[ch][k]; + update_erle_band(erle_[ch][k], new_erle[k], low_render_energy, + min_erle_, max_erle_[k]); + if (use_onset_detection_) { + update_erle_band(erle_onset_compensated_[ch][k], new_erle[k], + low_render_energy, min_erle_, max_erle_[k]); + } + + // Virtually unbounded ERLE. + constexpr float kUnboundedErleMax = 100000.0f; + update_erle_band(erle_unbounded_[ch][k], new_erle[k], low_render_energy, + min_erle_, kUnboundedErleMax); + } + } + } +} + +void SubbandErleEstimator::DecreaseErlePerBandForLowRenderSignals() { + const int num_capture_channels = static_cast(accum_spectra_.Y2.size()); + for (int ch = 0; ch < num_capture_channels; ++ch) { + for (size_t k = 1; k < kFftLengthBy2; ++k) { + --hold_counters_[ch][k]; + if (hold_counters_[ch][k] <= + (kBlocksForOnsetDetection - kBlocksToHoldErle)) { + if (erle_onset_compensated_[ch][k] > erle_during_onsets_[ch][k]) { + erle_onset_compensated_[ch][k] = + std::max(erle_during_onsets_[ch][k], + 0.97f * erle_onset_compensated_[ch][k]); + RTC_DCHECK_LE(min_erle_, erle_onset_compensated_[ch][k]); + } + if (hold_counters_[ch][k] <= 0) { + coming_onset_[ch][k] = true; + hold_counters_[ch][k] = 0; + } + } + } + } +} + +void SubbandErleEstimator::ResetAccumulatedSpectra() { + for (size_t ch = 0; ch < erle_during_onsets_.size(); ++ch) { + accum_spectra_.Y2[ch].fill(0.f); + accum_spectra_.E2[ch].fill(0.f); + accum_spectra_.num_points[ch] = 0; + accum_spectra_.low_render_energy[ch].fill(false); + } +} + +void SubbandErleEstimator::UpdateAccumulatedSpectra( + ArrayView X2, + ArrayView> Y2, + ArrayView> E2, + const std::vector& converged_filters) { + auto& st = accum_spectra_; + RTC_DCHECK_EQ(st.E2.size(), E2.size()); + RTC_DCHECK_EQ(st.E2.size(), E2.size()); + const int num_capture_channels = static_cast(Y2.size()); + for (int ch = 0; ch < num_capture_channels; ++ch) { + // Note that the use of the converged_filter flag already imposed + // a minimum of the erle that can be estimated as that flag would + // be false if the filter is performing poorly. + if (!converged_filters[ch]) { + continue; + } + + if (st.num_points[ch] == kPointsToAccumulate) { + st.num_points[ch] = 0; + st.Y2[ch].fill(0.f); + st.E2[ch].fill(0.f); + st.low_render_energy[ch].fill(false); + } + + std::transform(Y2[ch].begin(), Y2[ch].end(), st.Y2[ch].begin(), + st.Y2[ch].begin(), std::plus()); + std::transform(E2[ch].begin(), E2[ch].end(), st.E2[ch].begin(), + st.E2[ch].begin(), std::plus()); + + for (size_t k = 0; k < X2.size(); ++k) { + st.low_render_energy[ch][k] = + st.low_render_energy[ch][k] || X2[k] < kX2BandEnergyThreshold; + } + + ++st.num_points[ch]; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/subband_erle_estimator.h b/pkg/apm/webrtc/modules/audio_processing/aec3/subband_erle_estimator.h new file mode 100644 index 00000000..fdfaad88 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/subband_erle_estimator.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_ERLE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_ERLE_ESTIMATOR_H_ + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "api/environment/environment.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +// Estimates the echo return loss enhancement for each frequency subband. +class SubbandErleEstimator { + public: + SubbandErleEstimator(const Environment& env, + const EchoCanceller3Config& config, + size_t num_capture_channels); + ~SubbandErleEstimator(); + + // Resets the ERLE estimator. + void Reset(); + + // Updates the ERLE estimate. + void Update(ArrayView X2, + ArrayView> Y2, + ArrayView> E2, + const std::vector& converged_filters); + + // Returns the ERLE estimate. + ArrayView> Erle( + bool onset_compensated) const { + return onset_compensated && use_onset_detection_ ? erle_onset_compensated_ + : erle_; + } + + // Returns the non-capped ERLE estimate. + ArrayView> ErleUnbounded() const { + return erle_unbounded_; + } + + // Returns the ERLE estimate at onsets (only used for testing). + ArrayView> ErleDuringOnsets() + const { + return erle_during_onsets_; + } + + void Dump(const std::unique_ptr& data_dumper) const; + + private: + struct AccumulatedSpectra { + explicit AccumulatedSpectra(size_t num_capture_channels) + : Y2(num_capture_channels), + E2(num_capture_channels), + low_render_energy(num_capture_channels), + num_points(num_capture_channels) {} + std::vector> Y2; + std::vector> E2; + std::vector> low_render_energy; + std::vector num_points; + }; + + void UpdateAccumulatedSpectra( + ArrayView X2, + ArrayView> Y2, + ArrayView> E2, + const std::vector& converged_filters); + + void ResetAccumulatedSpectra(); + + void UpdateBands(const std::vector& converged_filters); + void DecreaseErlePerBandForLowRenderSignals(); + + const bool use_onset_detection_; + const float min_erle_; + const std::array max_erle_; + const bool use_min_erle_during_onsets_; + AccumulatedSpectra accum_spectra_; + // ERLE without special handling of render onsets. + std::vector> erle_; + // ERLE lowered during render onsets. + std::vector> erle_onset_compensated_; + std::vector> erle_unbounded_; + // Estimation of ERLE during render onsets. + std::vector> erle_during_onsets_; + std::vector> coming_onset_; + std::vector> hold_counters_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_ERLE_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/subband_nearend_detector.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/subband_nearend_detector.cc new file mode 100644 index 00000000..73c7c3c0 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/subband_nearend_detector.cc @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/subband_nearend_detector.h" + +#include + +namespace webrtc { +SubbandNearendDetector::SubbandNearendDetector( + const EchoCanceller3Config::Suppressor::SubbandNearendDetection& config, + size_t num_capture_channels) + : config_(config), + num_capture_channels_(num_capture_channels), + nearend_smoothers_(num_capture_channels_, + aec3::MovingAverage(kFftLengthBy2Plus1, + config_.nearend_average_blocks)), + one_over_subband_length1_( + 1.f / (config_.subband1.high - config_.subband1.low + 1)), + one_over_subband_length2_( + 1.f / (config_.subband2.high - config_.subband2.low + 1)) {} + +void SubbandNearendDetector::Update( + ArrayView> nearend_spectrum, + ArrayView> + /* residual_echo_spectrum */, + ArrayView> + comfort_noise_spectrum, + bool /* initial_state */) { + nearend_state_ = false; + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + const std::array& noise = + comfort_noise_spectrum[ch]; + std::array nearend; + nearend_smoothers_[ch].Average(nearend_spectrum[ch], nearend); + + // Noise power of the first region. + float noise_power = + std::accumulate(noise.begin() + config_.subband1.low, + noise.begin() + config_.subband1.high + 1, 0.f) * + one_over_subband_length1_; + + // Nearend power of the first region. + float nearend_power_subband1 = + std::accumulate(nearend.begin() + config_.subband1.low, + nearend.begin() + config_.subband1.high + 1, 0.f) * + one_over_subband_length1_; + + // Nearend power of the second region. + float nearend_power_subband2 = + std::accumulate(nearend.begin() + config_.subband2.low, + nearend.begin() + config_.subband2.high + 1, 0.f) * + one_over_subband_length2_; + + // One channel is sufficient to trigger nearend state. + nearend_state_ = + nearend_state_ || + (nearend_power_subband1 < + config_.nearend_threshold * nearend_power_subband2 && + (nearend_power_subband1 > config_.snr_threshold * noise_power)); + } +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/subband_nearend_detector.h b/pkg/apm/webrtc/modules/audio_processing/aec3/subband_nearend_detector.h new file mode 100644 index 00000000..846b8345 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/subband_nearend_detector.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_NEAREND_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_NEAREND_DETECTOR_H_ + +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/nearend_detector.h" + +namespace webrtc { +// Class for selecting whether the suppressor is in the nearend or echo state. +class SubbandNearendDetector : public NearendDetector { + public: + SubbandNearendDetector( + const EchoCanceller3Config::Suppressor::SubbandNearendDetection& config, + size_t num_capture_channels); + + // Returns whether the current state is the nearend state. + bool IsNearendState() const override { return nearend_state_; } + + // Updates the state selection based on latest spectral estimates. + void Update( + ArrayView> nearend_spectrum, + ArrayView> + residual_echo_spectrum, + ArrayView> + comfort_noise_spectrum, + bool initial_state) override; + + private: + const EchoCanceller3Config::Suppressor::SubbandNearendDetection config_; + const size_t num_capture_channels_; + std::vector nearend_smoothers_; + const float one_over_subband_length1_; + const float one_over_subband_length2_; + bool nearend_state_ = false; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_NEAREND_DETECTOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor.cc new file mode 100644 index 00000000..4d81a6cc --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor.cc @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/subtractor.h" + +#include +#include + +#include "api/array_view.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "modules/audio_processing/aec3/adaptive_fir_filter_erl.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +namespace { + +bool UseCoarseFilterResetHangover(const FieldTrialsView& field_trials) { + return !field_trials.IsEnabled( + "WebRTC-Aec3CoarseFilterResetHangoverKillSwitch"); +} + +void PredictionError(const Aec3Fft& fft, + const FftData& S, + ArrayView y, + std::array* e, + std::array* s) { + std::array tmp; + fft.Ifft(S, &tmp); + constexpr float kScale = 1.0f / kFftLengthBy2; + std::transform(y.begin(), y.end(), tmp.begin() + kFftLengthBy2, e->begin(), + [&](float a, float b) { return a - b * kScale; }); + + if (s) { + for (size_t k = 0; k < s->size(); ++k) { + (*s)[k] = kScale * tmp[k + kFftLengthBy2]; + } + } +} + +void ScaleFilterOutput(ArrayView y, + float factor, + ArrayView e, + ArrayView s) { + RTC_DCHECK_EQ(y.size(), e.size()); + RTC_DCHECK_EQ(y.size(), s.size()); + for (size_t k = 0; k < y.size(); ++k) { + s[k] *= factor; + e[k] = y[k] - s[k]; + } +} + +} // namespace + +Subtractor::Subtractor(const Environment& env, + const EchoCanceller3Config& config, + size_t num_render_channels, + size_t num_capture_channels, + ApmDataDumper* data_dumper, + Aec3Optimization optimization) + : fft_(), + data_dumper_(data_dumper), + optimization_(optimization), + config_(config), + num_capture_channels_(num_capture_channels), + use_coarse_filter_reset_hangover_( + UseCoarseFilterResetHangover(env.field_trials())), + refined_filters_(num_capture_channels_), + coarse_filter_(num_capture_channels_), + refined_gains_(num_capture_channels_), + coarse_gains_(num_capture_channels_), + filter_misadjustment_estimators_(num_capture_channels_), + poor_coarse_filter_counters_(num_capture_channels_, 0), + coarse_filter_reset_hangover_(num_capture_channels_, 0), + refined_frequency_responses_( + num_capture_channels_, + std::vector>( + std::max(config_.filter.refined_initial.length_blocks, + config_.filter.refined.length_blocks), + std::array())), + refined_impulse_responses_( + num_capture_channels_, + std::vector(GetTimeDomainLength(std::max( + config_.filter.refined_initial.length_blocks, + config_.filter.refined.length_blocks)), + 0.f)), + coarse_impulse_responses_(0) { + // Set up the storing of coarse impulse responses if data dumping is + // available. + if (ApmDataDumper::IsAvailable()) { + coarse_impulse_responses_.resize(num_capture_channels_); + const size_t filter_size = GetTimeDomainLength( + std::max(config_.filter.coarse_initial.length_blocks, + config_.filter.coarse.length_blocks)); + for (std::vector& impulse_response : coarse_impulse_responses_) { + impulse_response.resize(filter_size, 0.f); + } + } + + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + refined_filters_[ch] = std::make_unique( + config_.filter.refined.length_blocks, + config_.filter.refined_initial.length_blocks, + config.filter.config_change_duration_blocks, num_render_channels, + optimization, data_dumper_); + + coarse_filter_[ch] = std::make_unique( + config_.filter.coarse.length_blocks, + config_.filter.coarse_initial.length_blocks, + config.filter.config_change_duration_blocks, num_render_channels, + optimization, data_dumper_); + refined_gains_[ch] = std::make_unique( + config_.filter.refined_initial, + config_.filter.config_change_duration_blocks); + coarse_gains_[ch] = std::make_unique( + config_.filter.coarse_initial, + config.filter.config_change_duration_blocks); + } + + RTC_DCHECK(data_dumper_); + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + for (auto& H2_k : refined_frequency_responses_[ch]) { + H2_k.fill(0.f); + } + } +} + +Subtractor::~Subtractor() = default; + +void Subtractor::HandleEchoPathChange( + const EchoPathVariability& echo_path_variability) { + const auto full_reset = [&]() { + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + refined_filters_[ch]->HandleEchoPathChange(); + coarse_filter_[ch]->HandleEchoPathChange(); + refined_gains_[ch]->HandleEchoPathChange(echo_path_variability); + coarse_gains_[ch]->HandleEchoPathChange(); + refined_gains_[ch]->SetConfig(config_.filter.refined_initial, true); + coarse_gains_[ch]->SetConfig(config_.filter.coarse_initial, true); + refined_filters_[ch]->SetSizePartitions( + config_.filter.refined_initial.length_blocks, true); + coarse_filter_[ch]->SetSizePartitions( + config_.filter.coarse_initial.length_blocks, true); + } + }; + + if (echo_path_variability.delay_change != + EchoPathVariability::DelayAdjustment::kNone) { + full_reset(); + } + + if (echo_path_variability.gain_change) { + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + refined_gains_[ch]->HandleEchoPathChange(echo_path_variability); + } + } +} + +void Subtractor::ExitInitialState() { + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + refined_gains_[ch]->SetConfig(config_.filter.refined, false); + coarse_gains_[ch]->SetConfig(config_.filter.coarse, false); + refined_filters_[ch]->SetSizePartitions( + config_.filter.refined.length_blocks, false); + coarse_filter_[ch]->SetSizePartitions(config_.filter.coarse.length_blocks, + false); + } +} + +void Subtractor::Process(const RenderBuffer& render_buffer, + const Block& capture, + const RenderSignalAnalyzer& render_signal_analyzer, + const AecState& aec_state, + ArrayView outputs) { + RTC_DCHECK_EQ(num_capture_channels_, capture.NumChannels()); + + // Compute the render powers. + const bool same_filter_sizes = refined_filters_[0]->SizePartitions() == + coarse_filter_[0]->SizePartitions(); + std::array X2_refined; + std::array X2_coarse_data; + auto& X2_coarse = same_filter_sizes ? X2_refined : X2_coarse_data; + if (same_filter_sizes) { + render_buffer.SpectralSum(refined_filters_[0]->SizePartitions(), + &X2_refined); + } else if (refined_filters_[0]->SizePartitions() > + coarse_filter_[0]->SizePartitions()) { + render_buffer.SpectralSums(coarse_filter_[0]->SizePartitions(), + refined_filters_[0]->SizePartitions(), + &X2_coarse, &X2_refined); + } else { + render_buffer.SpectralSums(refined_filters_[0]->SizePartitions(), + coarse_filter_[0]->SizePartitions(), &X2_refined, + &X2_coarse); + } + + // Process all capture channels + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + SubtractorOutput& output = outputs[ch]; + ArrayView y = capture.View(/*band=*/0, ch); + FftData& E_refined = output.E_refined; + FftData E_coarse; + std::array& e_refined = output.e_refined; + std::array& e_coarse = output.e_coarse; + + FftData S; + FftData& G = S; + + // Form the outputs of the refined and coarse filters. + refined_filters_[ch]->Filter(render_buffer, &S); + PredictionError(fft_, S, y, &e_refined, &output.s_refined); + + coarse_filter_[ch]->Filter(render_buffer, &S); + PredictionError(fft_, S, y, &e_coarse, &output.s_coarse); + + // Compute the signal powers in the subtractor output. + output.ComputeMetrics(y); + + // Adjust the filter if needed. + bool refined_filters_adjusted = false; + filter_misadjustment_estimators_[ch].Update(output); + if (filter_misadjustment_estimators_[ch].IsAdjustmentNeeded()) { + float scale = filter_misadjustment_estimators_[ch].GetMisadjustment(); + refined_filters_[ch]->ScaleFilter(scale); + for (auto& h_k : refined_impulse_responses_[ch]) { + h_k *= scale; + } + ScaleFilterOutput(y, scale, e_refined, output.s_refined); + filter_misadjustment_estimators_[ch].Reset(); + refined_filters_adjusted = true; + } + + // Compute the FFts of the refined and coarse filter outputs. + fft_.ZeroPaddedFft(e_refined, Aec3Fft::Window::kHanning, &E_refined); + fft_.ZeroPaddedFft(e_coarse, Aec3Fft::Window::kHanning, &E_coarse); + + // Compute spectra for future use. + E_coarse.Spectrum(optimization_, output.E2_coarse); + E_refined.Spectrum(optimization_, output.E2_refined); + + // Update the refined filter. + if (!refined_filters_adjusted) { + // Do not allow the performance of the coarse filter to affect the + // adaptation speed of the refined filter just after the coarse filter has + // been reset. + const bool disallow_leakage_diverged = + coarse_filter_reset_hangover_[ch] > 0 && + use_coarse_filter_reset_hangover_; + + std::array erl; + ComputeErl(optimization_, refined_frequency_responses_[ch], erl); + refined_gains_[ch]->Compute(X2_refined, render_signal_analyzer, output, + erl, refined_filters_[ch]->SizePartitions(), + aec_state.SaturatedCapture(), + disallow_leakage_diverged, &G); + } else { + G.re.fill(0.f); + G.im.fill(0.f); + } + refined_filters_[ch]->Adapt(render_buffer, G, + &refined_impulse_responses_[ch]); + refined_filters_[ch]->ComputeFrequencyResponse( + &refined_frequency_responses_[ch]); + + if (ch == 0) { + data_dumper_->DumpRaw("aec3_subtractor_G_refined", G.re); + data_dumper_->DumpRaw("aec3_subtractor_G_refined", G.im); + } + + // Update the coarse filter. + poor_coarse_filter_counters_[ch] = + output.e2_refined < output.e2_coarse + ? poor_coarse_filter_counters_[ch] + 1 + : 0; + if (poor_coarse_filter_counters_[ch] < 5) { + coarse_gains_[ch]->Compute(X2_coarse, render_signal_analyzer, E_coarse, + coarse_filter_[ch]->SizePartitions(), + aec_state.SaturatedCapture(), &G); + coarse_filter_reset_hangover_[ch] = + std::max(coarse_filter_reset_hangover_[ch] - 1, 0); + } else { + poor_coarse_filter_counters_[ch] = 0; + coarse_filter_[ch]->SetFilter(refined_filters_[ch]->SizePartitions(), + refined_filters_[ch]->GetFilter()); + coarse_gains_[ch]->Compute(X2_coarse, render_signal_analyzer, E_refined, + coarse_filter_[ch]->SizePartitions(), + aec_state.SaturatedCapture(), &G); + coarse_filter_reset_hangover_[ch] = + config_.filter.coarse_reset_hangover_blocks; + } + + if (ApmDataDumper::IsAvailable()) { + RTC_DCHECK_LT(ch, coarse_impulse_responses_.size()); + coarse_filter_[ch]->Adapt(render_buffer, G, + &coarse_impulse_responses_[ch]); + } else { + coarse_filter_[ch]->Adapt(render_buffer, G); + } + + if (ch == 0) { + data_dumper_->DumpRaw("aec3_subtractor_G_coarse", G.re); + data_dumper_->DumpRaw("aec3_subtractor_G_coarse", G.im); + filter_misadjustment_estimators_[ch].Dump(data_dumper_); + DumpFilters(); + } + + std::for_each(e_refined.begin(), e_refined.end(), + [](float& a) { a = SafeClamp(a, -32768.f, 32767.f); }); + + if (ch == 0) { + data_dumper_->DumpWav("aec3_refined_filters_output", kBlockSize, + &e_refined[0], 16000, 1); + data_dumper_->DumpWav("aec3_coarse_filter_output", kBlockSize, + &e_coarse[0], 16000, 1); + } + } +} + +void Subtractor::FilterMisadjustmentEstimator::Update( + const SubtractorOutput& output) { + e2_acum_ += output.e2_refined; + y2_acum_ += output.y2; + if (++n_blocks_acum_ == n_blocks_) { + if (y2_acum_ > n_blocks_ * 200.f * 200.f * kBlockSize) { + float update = (e2_acum_ / y2_acum_); + if (e2_acum_ > n_blocks_ * 7500.f * 7500.f * kBlockSize) { + // Duration equal to blockSizeMs * n_blocks_ * 4. + overhang_ = 4; + } else { + overhang_ = std::max(overhang_ - 1, 0); + } + + if ((update < inv_misadjustment_) || (overhang_ > 0)) { + inv_misadjustment_ += 0.1f * (update - inv_misadjustment_); + } + } + e2_acum_ = 0.f; + y2_acum_ = 0.f; + n_blocks_acum_ = 0; + } +} + +void Subtractor::FilterMisadjustmentEstimator::Reset() { + e2_acum_ = 0.f; + y2_acum_ = 0.f; + n_blocks_acum_ = 0; + inv_misadjustment_ = 0.f; + overhang_ = 0.f; +} + +void Subtractor::FilterMisadjustmentEstimator::Dump( + ApmDataDumper* data_dumper) const { + data_dumper->DumpRaw("aec3_inv_misadjustment_factor", inv_misadjustment_); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor.h b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor.h new file mode 100644 index 00000000..0b8d0471 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_H_ + +#include +#include + +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "api/environment/environment.h" +#include "modules/audio_processing/aec3/adaptive_fir_filter.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec3_fft.h" +#include "modules/audio_processing/aec3/aec_state.h" +#include "modules/audio_processing/aec3/block.h" +#include "modules/audio_processing/aec3/coarse_filter_update_gain.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/refined_filter_update_gain.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/render_signal_analyzer.h" +#include "modules/audio_processing/aec3/subtractor_output.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Proves linear echo cancellation functionality +class Subtractor { + public: + Subtractor(const Environment& env, + const EchoCanceller3Config& config, + size_t num_render_channels, + size_t num_capture_channels, + ApmDataDumper* data_dumper, + Aec3Optimization optimization); + ~Subtractor(); + Subtractor(const Subtractor&) = delete; + Subtractor& operator=(const Subtractor&) = delete; + + // Performs the echo subtraction. + void Process(const RenderBuffer& render_buffer, + const Block& capture, + const RenderSignalAnalyzer& render_signal_analyzer, + const AecState& aec_state, + ArrayView outputs); + + void HandleEchoPathChange(const EchoPathVariability& echo_path_variability); + + // Exits the initial state. + void ExitInitialState(); + + // Returns the block-wise frequency responses for the refined adaptive + // filters. + const std::vector>>& + FilterFrequencyResponses() const { + return refined_frequency_responses_; + } + + // Returns the estimates of the impulse responses for the refined adaptive + // filters. + const std::vector>& FilterImpulseResponses() const { + return refined_impulse_responses_; + } + + void DumpFilters() { + data_dumper_->DumpRaw( + "aec3_subtractor_h_refined", + ArrayView( + refined_impulse_responses_[0].data(), + GetTimeDomainLength( + refined_filters_[0]->max_filter_size_partitions()))); + if (ApmDataDumper::IsAvailable()) { + RTC_DCHECK_GT(coarse_impulse_responses_.size(), 0); + data_dumper_->DumpRaw( + "aec3_subtractor_h_coarse", + ArrayView( + coarse_impulse_responses_[0].data(), + GetTimeDomainLength( + coarse_filter_[0]->max_filter_size_partitions()))); + } + + refined_filters_[0]->DumpFilter("aec3_subtractor_H_refined"); + coarse_filter_[0]->DumpFilter("aec3_subtractor_H_coarse"); + } + + private: + class FilterMisadjustmentEstimator { + public: + FilterMisadjustmentEstimator() = default; + ~FilterMisadjustmentEstimator() = default; + // Update the misadjustment estimator. + void Update(const SubtractorOutput& output); + // GetMisadjustment() Returns a recommended scale for the filter so the + // prediction error energy gets closer to the energy that is seen at the + // microphone input. + float GetMisadjustment() const { + RTC_DCHECK_GT(inv_misadjustment_, 0.0f); + // It is not aiming to adjust all the estimated mismatch. Instead, + // it adjusts half of that estimated mismatch. + return 2.f / sqrtf(inv_misadjustment_); + } + // Returns true if the prediciton error energy is significantly larger + // than the microphone signal energy and, therefore, an adjustment is + // recommended. + bool IsAdjustmentNeeded() const { return inv_misadjustment_ > 10.f; } + void Reset(); + void Dump(ApmDataDumper* data_dumper) const; + + private: + const int n_blocks_ = 4; + int n_blocks_acum_ = 0; + float e2_acum_ = 0.f; + float y2_acum_ = 0.f; + float inv_misadjustment_ = 0.f; + int overhang_ = 0.f; + }; + + const Aec3Fft fft_; + ApmDataDumper* data_dumper_; + const Aec3Optimization optimization_; + const EchoCanceller3Config config_; + const size_t num_capture_channels_; + const bool use_coarse_filter_reset_hangover_; + + std::vector> refined_filters_; + std::vector> coarse_filter_; + std::vector> refined_gains_; + std::vector> coarse_gains_; + std::vector filter_misadjustment_estimators_; + std::vector poor_coarse_filter_counters_; + std::vector coarse_filter_reset_hangover_; + std::vector>> + refined_frequency_responses_; + std::vector> refined_impulse_responses_; + std::vector> coarse_impulse_responses_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output.cc new file mode 100644 index 00000000..0aef6b9a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/subtractor_output.h" + +#include + +namespace webrtc { + +SubtractorOutput::SubtractorOutput() = default; +SubtractorOutput::~SubtractorOutput() = default; + +void SubtractorOutput::Reset() { + s_refined.fill(0.f); + s_coarse.fill(0.f); + e_refined.fill(0.f); + e_coarse.fill(0.f); + E_refined.re.fill(0.f); + E_refined.im.fill(0.f); + E2_refined.fill(0.f); + E2_coarse.fill(0.f); + e2_refined = 0.f; + e2_coarse = 0.f; + s2_refined = 0.f; + s2_coarse = 0.f; + y2 = 0.f; +} + +void SubtractorOutput::ComputeMetrics(ArrayView y) { + const auto sum_of_squares = [](float a, float b) { return a + b * b; }; + y2 = std::accumulate(y.begin(), y.end(), 0.f, sum_of_squares); + e2_refined = + std::accumulate(e_refined.begin(), e_refined.end(), 0.f, sum_of_squares); + e2_coarse = + std::accumulate(e_coarse.begin(), e_coarse.end(), 0.f, sum_of_squares); + s2_refined = + std::accumulate(s_refined.begin(), s_refined.end(), 0.f, sum_of_squares); + s2_coarse = + std::accumulate(s_coarse.begin(), s_coarse.end(), 0.f, sum_of_squares); + + s_refined_max_abs = *std::max_element(s_refined.begin(), s_refined.end()); + s_refined_max_abs = + std::max(s_refined_max_abs, + -(*std::min_element(s_refined.begin(), s_refined.end()))); + + s_coarse_max_abs = *std::max_element(s_coarse.begin(), s_coarse.end()); + s_coarse_max_abs = std::max( + s_coarse_max_abs, -(*std::min_element(s_coarse.begin(), s_coarse.end()))); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output.h b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output.h new file mode 100644 index 00000000..10349f59 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/fft_data.h" + +namespace webrtc { + +// Stores the values being returned from the echo subtractor for a single +// capture channel. +struct SubtractorOutput { + SubtractorOutput(); + ~SubtractorOutput(); + + std::array s_refined; + std::array s_coarse; + std::array e_refined; + std::array e_coarse; + FftData E_refined; + std::array E2_refined; + std::array E2_coarse; + float s2_refined = 0.f; + float s2_coarse = 0.f; + float e2_refined = 0.f; + float e2_coarse = 0.f; + float y2 = 0.f; + float s_refined_max_abs = 0.f; + float s_coarse_max_abs = 0.f; + + // Reset the struct content. + void Reset(); + + // Updates the powers of the signals. + void ComputeMetrics(ArrayView y); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.cc new file mode 100644 index 00000000..b2b5ce0b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/subtractor_output_analyzer.h" + +#include + +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +SubtractorOutputAnalyzer::SubtractorOutputAnalyzer(size_t num_capture_channels) + : filters_converged_(num_capture_channels, false) {} + +void SubtractorOutputAnalyzer::Update( + ArrayView subtractor_output, + bool* any_filter_converged, + bool* any_coarse_filter_converged, + bool* all_filters_diverged) { + RTC_DCHECK(any_filter_converged); + RTC_DCHECK(all_filters_diverged); + RTC_DCHECK_EQ(subtractor_output.size(), filters_converged_.size()); + + *any_filter_converged = false; + *any_coarse_filter_converged = false; + *all_filters_diverged = true; + + for (size_t ch = 0; ch < subtractor_output.size(); ++ch) { + const float y2 = subtractor_output[ch].y2; + const float e2_refined = subtractor_output[ch].e2_refined; + const float e2_coarse = subtractor_output[ch].e2_coarse; + + constexpr float kConvergenceThreshold = 50 * 50 * kBlockSize; + constexpr float kConvergenceThresholdLowLevel = 20 * 20 * kBlockSize; + bool refined_filter_converged = + e2_refined < 0.5f * y2 && y2 > kConvergenceThreshold; + bool coarse_filter_converged_strict = + e2_coarse < 0.05f * y2 && y2 > kConvergenceThreshold; + bool coarse_filter_converged_relaxed = + e2_coarse < 0.3f * y2 && y2 > kConvergenceThresholdLowLevel; + float min_e2 = std::min(e2_refined, e2_coarse); + bool filter_diverged = min_e2 > 1.5f * y2 && y2 > 30.f * 30.f * kBlockSize; + filters_converged_[ch] = + refined_filter_converged || coarse_filter_converged_strict; + + *any_filter_converged = *any_filter_converged || filters_converged_[ch]; + *any_coarse_filter_converged = + *any_coarse_filter_converged || coarse_filter_converged_relaxed; + *all_filters_diverged = *all_filters_diverged && filter_diverged; + } +} + +void SubtractorOutputAnalyzer::HandleEchoPathChange() { + std::fill(filters_converged_.begin(), filters_converged_.end(), false); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.h b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.h new file mode 100644 index 00000000..c9ccafac --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_ANALYZER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_ANALYZER_H_ + +#include + +#include "modules/audio_processing/aec3/subtractor_output.h" + +namespace webrtc { + +// Class for analyzing the properties subtractor output. +class SubtractorOutputAnalyzer { + public: + explicit SubtractorOutputAnalyzer(size_t num_capture_channels); + ~SubtractorOutputAnalyzer() = default; + + // Analyses the subtractor output. + void Update(ArrayView subtractor_output, + bool* any_filter_converged, + bool* any_coarse_filter_converged, + bool* all_filters_diverged); + + const std::vector& ConvergedFilters() const { + return filters_converged_; + } + + // Handle echo path change. + void HandleEchoPathChange(); + + private: + std::vector filters_converged_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_ANALYZER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_filter.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_filter.cc new file mode 100644 index 00000000..ec081ab6 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_filter.cc @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/suppression_filter.h" + +#include +#include +#include +#include +#include + +#include "modules/audio_processing/aec3/vector_math.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +// Hanning window from Matlab command win = sqrt(hanning(128)). +const float kSqrtHanning[kFftLength] = { + 0.00000000000000f, 0.02454122852291f, 0.04906767432742f, 0.07356456359967f, + 0.09801714032956f, 0.12241067519922f, 0.14673047445536f, 0.17096188876030f, + 0.19509032201613f, 0.21910124015687f, 0.24298017990326f, 0.26671275747490f, + 0.29028467725446f, 0.31368174039889f, 0.33688985339222f, 0.35989503653499f, + 0.38268343236509f, 0.40524131400499f, 0.42755509343028f, 0.44961132965461f, + 0.47139673682600f, 0.49289819222978f, 0.51410274419322f, 0.53499761988710f, + 0.55557023301960f, 0.57580819141785f, 0.59569930449243f, 0.61523159058063f, + 0.63439328416365f, 0.65317284295378f, 0.67155895484702f, 0.68954054473707f, + 0.70710678118655f, 0.72424708295147f, 0.74095112535496f, 0.75720884650648f, + 0.77301045336274f, 0.78834642762661f, 0.80320753148064f, 0.81758481315158f, + 0.83146961230255f, 0.84485356524971f, 0.85772861000027f, 0.87008699110871f, + 0.88192126434835f, 0.89322430119552f, 0.90398929312344f, 0.91420975570353f, + 0.92387953251129f, 0.93299279883474f, 0.94154406518302f, 0.94952818059304f, + 0.95694033573221f, 0.96377606579544f, 0.97003125319454f, 0.97570213003853f, + 0.98078528040323f, 0.98527764238894f, 0.98917650996478f, 0.99247953459871f, + 0.99518472667220f, 0.99729045667869f, 0.99879545620517f, 0.99969881869620f, + 1.00000000000000f, 0.99969881869620f, 0.99879545620517f, 0.99729045667869f, + 0.99518472667220f, 0.99247953459871f, 0.98917650996478f, 0.98527764238894f, + 0.98078528040323f, 0.97570213003853f, 0.97003125319454f, 0.96377606579544f, + 0.95694033573221f, 0.94952818059304f, 0.94154406518302f, 0.93299279883474f, + 0.92387953251129f, 0.91420975570353f, 0.90398929312344f, 0.89322430119552f, + 0.88192126434835f, 0.87008699110871f, 0.85772861000027f, 0.84485356524971f, + 0.83146961230255f, 0.81758481315158f, 0.80320753148064f, 0.78834642762661f, + 0.77301045336274f, 0.75720884650648f, 0.74095112535496f, 0.72424708295147f, + 0.70710678118655f, 0.68954054473707f, 0.67155895484702f, 0.65317284295378f, + 0.63439328416365f, 0.61523159058063f, 0.59569930449243f, 0.57580819141785f, + 0.55557023301960f, 0.53499761988710f, 0.51410274419322f, 0.49289819222978f, + 0.47139673682600f, 0.44961132965461f, 0.42755509343028f, 0.40524131400499f, + 0.38268343236509f, 0.35989503653499f, 0.33688985339222f, 0.31368174039889f, + 0.29028467725446f, 0.26671275747490f, 0.24298017990326f, 0.21910124015687f, + 0.19509032201613f, 0.17096188876030f, 0.14673047445536f, 0.12241067519922f, + 0.09801714032956f, 0.07356456359967f, 0.04906767432742f, 0.02454122852291f}; + +} // namespace + +SuppressionFilter::SuppressionFilter(Aec3Optimization optimization, + int sample_rate_hz, + size_t num_capture_channels) + : optimization_(optimization), + sample_rate_hz_(sample_rate_hz), + num_capture_channels_(num_capture_channels), + fft_(), + e_output_old_(NumBandsForRate(sample_rate_hz_), + std::vector>( + num_capture_channels_)) { + RTC_DCHECK(ValidFullBandRate(sample_rate_hz_)); + for (size_t b = 0; b < e_output_old_.size(); ++b) { + for (size_t ch = 0; ch < e_output_old_[b].size(); ++ch) { + e_output_old_[b][ch].fill(0.f); + } + } +} + +SuppressionFilter::~SuppressionFilter() = default; + +void SuppressionFilter::ApplyGain( + ArrayView comfort_noise, + ArrayView comfort_noise_high_band, + const std::array& suppression_gain, + float high_bands_gain, + ArrayView E_lowest_band, + Block* e) { + RTC_DCHECK(e); + RTC_DCHECK_EQ(e->NumBands(), NumBandsForRate(sample_rate_hz_)); + + // Comfort noise gain is sqrt(1-g^2), where g is the suppression gain. + std::array noise_gain; + for (size_t i = 0; i < kFftLengthBy2Plus1; ++i) { + noise_gain[i] = 1.f - suppression_gain[i] * suppression_gain[i]; + } + aec3::VectorMath(optimization_).Sqrt(noise_gain); + + const float high_bands_noise_scaling = + 0.4f * std::sqrt(1.f - high_bands_gain * high_bands_gain); + + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + FftData E; + + // Analysis filterbank. + E.Assign(E_lowest_band[ch]); + + for (size_t i = 0; i < kFftLengthBy2Plus1; ++i) { + // Apply suppression gains. + float E_real = E.re[i] * suppression_gain[i]; + float E_imag = E.im[i] * suppression_gain[i]; + + // Scale and add the comfort noise. + E.re[i] = E_real + noise_gain[i] * comfort_noise[ch].re[i]; + E.im[i] = E_imag + noise_gain[i] * comfort_noise[ch].im[i]; + } + + // Synthesis filterbank. + std::array e_extended; + constexpr float kIfftNormalization = 2.f / kFftLength; + fft_.Ifft(E, &e_extended); + + auto e0 = e->View(/*band=*/0, ch); + float* e0_old = e_output_old_[0][ch].data(); + + // Window and add the first half of e_extended with the second half of + // e_extended from the previous block. + for (size_t i = 0; i < kFftLengthBy2; ++i) { + float e0_i = e0_old[i] * kSqrtHanning[kFftLengthBy2 + i]; + e0_i += e_extended[i] * kSqrtHanning[i]; + e0[i] = e0_i * kIfftNormalization; + } + + // The second half of e_extended is stored for the succeeding frame. + std::copy(e_extended.begin() + kFftLengthBy2, + e_extended.begin() + kFftLength, + std::begin(e_output_old_[0][ch])); + + // Apply suppression gain to upper bands. + for (int b = 1; b < e->NumBands(); ++b) { + auto e_band = e->View(b, ch); + for (size_t i = 0; i < kFftLengthBy2; ++i) { + e_band[i] *= high_bands_gain; + } + } + + // Add comfort noise to band 1. + if (e->NumBands() > 1) { + E.Assign(comfort_noise_high_band[ch]); + std::array time_domain_high_band_noise; + fft_.Ifft(E, &time_domain_high_band_noise); + + auto e1 = e->View(/*band=*/1, ch); + const float gain = high_bands_noise_scaling * kIfftNormalization; + for (size_t i = 0; i < kFftLengthBy2; ++i) { + e1[i] += time_domain_high_band_noise[i] * gain; + } + } + + // Delay upper bands to match the delay of the filter bank. + for (int b = 1; b < e->NumBands(); ++b) { + auto e_band = e->View(b, ch); + float* e_band_old = e_output_old_[b][ch].data(); + for (size_t i = 0; i < kFftLengthBy2; ++i) { + std::swap(e_band[i], e_band_old[i]); + } + } + + // Clamp output of all bands. + for (int b = 0; b < e->NumBands(); ++b) { + auto e_band = e->View(b, ch); + for (size_t i = 0; i < kFftLengthBy2; ++i) { + e_band[i] = SafeClamp(e_band[i], -32768.f, 32767.f); + } + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_filter.h b/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_filter.h new file mode 100644 index 00000000..e8082845 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_filter.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_FILTER_H_ + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec3_fft.h" +#include "modules/audio_processing/aec3/block.h" +#include "modules/audio_processing/aec3/fft_data.h" + +namespace webrtc { + +class SuppressionFilter { + public: + SuppressionFilter(Aec3Optimization optimization, + int sample_rate_hz, + size_t num_capture_channels_); + ~SuppressionFilter(); + + SuppressionFilter(const SuppressionFilter&) = delete; + SuppressionFilter& operator=(const SuppressionFilter&) = delete; + + void ApplyGain(ArrayView comfort_noise, + ArrayView comfort_noise_high_bands, + const std::array& suppression_gain, + float high_bands_gain, + ArrayView E_lowest_band, + Block* e); + + private: + const Aec3Optimization optimization_; + const int sample_rate_hz_; + const size_t num_capture_channels_; + const Aec3Fft fft_; + std::vector>> e_output_old_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_FILTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_gain.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_gain.cc new file mode 100644 index 00000000..6ae9bfc1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_gain.cc @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/suppression_gain.h" + +#include +#include + +#include +#include + +#include "modules/audio_processing/aec3/dominant_nearend_detector.h" +#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/subband_nearend_detector.h" +#include "modules/audio_processing/aec3/vector_math.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +void LimitLowFrequencyGains(std::array* gain) { + // Limit the low frequency gains to avoid the impact of the high-pass filter + // on the lower-frequency gain influencing the overall achieved gain. + (*gain)[0] = (*gain)[1] = std::min((*gain)[1], (*gain)[2]); +} + +void LimitHighFrequencyGains(bool conservative_hf_suppression, + std::array* gain) { + // Limit the high frequency gains to avoid echo leakage due to an imperfect + // filter. + constexpr size_t kFirstBandToLimit = (64 * 2000) / 8000; + const float min_upper_gain = (*gain)[kFirstBandToLimit]; + std::for_each( + gain->begin() + kFirstBandToLimit + 1, gain->end(), + [min_upper_gain](float& a) { a = std::min(a, min_upper_gain); }); + (*gain)[kFftLengthBy2] = (*gain)[kFftLengthBy2Minus1]; + + if (conservative_hf_suppression) { + // Limits the gain in the frequencies for which the adaptive filter has not + // converged. + // TODO(peah): Make adaptive to take the actual filter error into account. + constexpr size_t kUpperAccurateBandPlus1 = 29; + + constexpr float oneByBandsInSum = + 1 / static_cast(kUpperAccurateBandPlus1 - 20); + const float hf_gain_bound = + std::accumulate(gain->begin() + 20, + gain->begin() + kUpperAccurateBandPlus1, 0.f) * + oneByBandsInSum; + + std::for_each( + gain->begin() + kUpperAccurateBandPlus1, gain->end(), + [hf_gain_bound](float& a) { a = std::min(a, hf_gain_bound); }); + } +} + +// Scales the echo according to assessed audibility at the other end. +void WeightEchoForAudibility(const EchoCanceller3Config& config, + ArrayView echo, + ArrayView weighted_echo) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, echo.size()); + RTC_DCHECK_EQ(kFftLengthBy2Plus1, weighted_echo.size()); + + auto weigh = [](float threshold, float normalizer, size_t begin, size_t end, + ArrayView echo, ArrayView weighted_echo) { + for (size_t k = begin; k < end; ++k) { + if (echo[k] < threshold) { + float tmp = (threshold - echo[k]) * normalizer; + weighted_echo[k] = echo[k] * std::max(0.f, 1.f - tmp * tmp); + } else { + weighted_echo[k] = echo[k]; + } + } + }; + + float threshold = config.echo_audibility.floor_power * + config.echo_audibility.audibility_threshold_lf; + float normalizer = 1.f / (threshold - config.echo_audibility.floor_power); + weigh(threshold, normalizer, 0, 3, echo, weighted_echo); + + threshold = config.echo_audibility.floor_power * + config.echo_audibility.audibility_threshold_mf; + normalizer = 1.f / (threshold - config.echo_audibility.floor_power); + weigh(threshold, normalizer, 3, 7, echo, weighted_echo); + + threshold = config.echo_audibility.floor_power * + config.echo_audibility.audibility_threshold_hf; + normalizer = 1.f / (threshold - config.echo_audibility.floor_power); + weigh(threshold, normalizer, 7, kFftLengthBy2Plus1, echo, weighted_echo); +} + +} // namespace + +std::atomic SuppressionGain::instance_count_(0); + +float SuppressionGain::UpperBandsGain( + ArrayView> echo_spectrum, + ArrayView> + comfort_noise_spectrum, + const std::optional& narrow_peak_band, + bool saturated_echo, + const Block& render, + const std::array& low_band_gain) const { + RTC_DCHECK_LT(0, render.NumBands()); + if (render.NumBands() == 1) { + return 1.f; + } + const int num_render_channels = render.NumChannels(); + + if (narrow_peak_band && + (*narrow_peak_band > static_cast(kFftLengthBy2Plus1 - 10))) { + return 0.001f; + } + + constexpr size_t kLowBandGainLimit = kFftLengthBy2 / 2; + const float gain_below_8_khz = *std::min_element( + low_band_gain.begin() + kLowBandGainLimit, low_band_gain.end()); + + // Always attenuate the upper bands when there is saturated echo. + if (saturated_echo) { + return std::min(0.001f, gain_below_8_khz); + } + + // Compute the upper and lower band energies. + const auto sum_of_squares = [](float a, float b) { return a + b * b; }; + float low_band_energy = 0.f; + for (int ch = 0; ch < num_render_channels; ++ch) { + const float channel_energy = + std::accumulate(render.begin(/*band=*/0, ch), + render.end(/*band=*/0, ch), 0.0f, sum_of_squares); + low_band_energy = std::max(low_band_energy, channel_energy); + } + float high_band_energy = 0.f; + for (int k = 1; k < render.NumBands(); ++k) { + for (int ch = 0; ch < num_render_channels; ++ch) { + const float energy = std::accumulate( + render.begin(k, ch), render.end(k, ch), 0.f, sum_of_squares); + high_band_energy = std::max(high_band_energy, energy); + } + } + + // If there is more power in the lower frequencies than the upper frequencies, + // or if the power in upper frequencies is low, do not bound the gain in the + // upper bands. + float anti_howling_gain; + const float activation_threshold = + kBlockSize * config_.suppressor.high_bands_suppression + .anti_howling_activation_threshold; + if (high_band_energy < std::max(low_band_energy, activation_threshold)) { + anti_howling_gain = 1.f; + } else { + // In all other cases, bound the gain for upper frequencies. + RTC_DCHECK_LE(low_band_energy, high_band_energy); + RTC_DCHECK_NE(0.f, high_band_energy); + anti_howling_gain = + config_.suppressor.high_bands_suppression.anti_howling_gain * + sqrtf(low_band_energy / high_band_energy); + } + + float gain_bound = 1.f; + if (!dominant_nearend_detector_->IsNearendState()) { + // Bound the upper gain during significant echo activity. + const auto& cfg = config_.suppressor.high_bands_suppression; + auto low_frequency_energy = [](ArrayView spectrum) { + RTC_DCHECK_LE(16, spectrum.size()); + return std::accumulate(spectrum.begin() + 1, spectrum.begin() + 16, 0.f); + }; + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + const float echo_sum = low_frequency_energy(echo_spectrum[ch]); + const float noise_sum = low_frequency_energy(comfort_noise_spectrum[ch]); + if (echo_sum > cfg.enr_threshold * noise_sum) { + gain_bound = cfg.max_gain_during_echo; + break; + } + } + } + + // Choose the gain as the minimum of the lower and upper gains. + return std::min(std::min(gain_below_8_khz, anti_howling_gain), gain_bound); +} + +// Computes the gain to reduce the echo to a non audible level. +void SuppressionGain::GainToNoAudibleEcho( + const std::array& nearend, + const std::array& echo, + const std::array& masker, + std::array* gain) const { + const auto& p = dominant_nearend_detector_->IsNearendState() ? nearend_params_ + : normal_params_; + for (size_t k = 0; k < gain->size(); ++k) { + float enr = echo[k] / (nearend[k] + 1.f); // Echo-to-nearend ratio. + float emr = echo[k] / (masker[k] + 1.f); // Echo-to-masker (noise) ratio. + float g = 1.0f; + if (enr > p.enr_transparent_[k] && emr > p.emr_transparent_[k]) { + g = (p.enr_suppress_[k] - enr) / + (p.enr_suppress_[k] - p.enr_transparent_[k]); + g = std::max(g, p.emr_transparent_[k] / emr); + } + (*gain)[k] = g; + } +} + +// Compute the minimum gain as the attenuating gain to put the signal just +// above the zero sample values. +void SuppressionGain::GetMinGain(ArrayView weighted_residual_echo, + ArrayView last_nearend, + ArrayView last_echo, + bool low_noise_render, + bool saturated_echo, + ArrayView min_gain) const { + if (!saturated_echo) { + const float min_echo_power = + low_noise_render ? config_.echo_audibility.low_render_limit + : config_.echo_audibility.normal_render_limit; + + for (size_t k = 0; k < min_gain.size(); ++k) { + min_gain[k] = weighted_residual_echo[k] > 0.f + ? min_echo_power / weighted_residual_echo[k] + : 1.f; + min_gain[k] = std::min(min_gain[k], 1.f); + } + + if (!initial_state_ || + config_.suppressor.lf_smoothing_during_initial_phase) { + const float& dec = dominant_nearend_detector_->IsNearendState() + ? nearend_params_.max_dec_factor_lf + : normal_params_.max_dec_factor_lf; + + for (int k = 0; k <= config_.suppressor.last_lf_smoothing_band; ++k) { + // Make sure the gains of the low frequencies do not decrease too + // quickly after strong nearend. + if (last_nearend[k] > last_echo[k] || + k <= config_.suppressor.last_permanent_lf_smoothing_band) { + min_gain[k] = std::max(min_gain[k], last_gain_[k] * dec); + min_gain[k] = std::min(min_gain[k], 1.f); + } + } + } + } else { + std::fill(min_gain.begin(), min_gain.end(), 0.f); + } +} + +// Compute the maximum gain by limiting the gain increase from the previous +// gain. +void SuppressionGain::GetMaxGain(ArrayView max_gain) const { + const auto& inc = dominant_nearend_detector_->IsNearendState() + ? nearend_params_.max_inc_factor + : normal_params_.max_inc_factor; + const auto& floor = config_.suppressor.floor_first_increase; + for (size_t k = 0; k < max_gain.size(); ++k) { + max_gain[k] = std::min(std::max(last_gain_[k] * inc, floor), 1.f); + } +} + +void SuppressionGain::LowerBandGain( + bool low_noise_render, + const AecState& aec_state, + ArrayView> suppressor_input, + ArrayView> residual_echo, + ArrayView> comfort_noise, + bool clock_drift, + std::array* gain) { + gain->fill(1.f); + const bool saturated_echo = aec_state.SaturatedEcho(); + std::array max_gain; + GetMaxGain(max_gain); + + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + std::array G; + std::array nearend; + nearend_smoothers_[ch].Average(suppressor_input[ch], nearend); + + // Weight echo power in terms of audibility. + std::array weighted_residual_echo; + WeightEchoForAudibility(config_, residual_echo[ch], weighted_residual_echo); + + std::array min_gain; + GetMinGain(weighted_residual_echo, last_nearend_[ch], last_echo_[ch], + low_noise_render, saturated_echo, min_gain); + + GainToNoAudibleEcho(nearend, weighted_residual_echo, comfort_noise[0], &G); + + // Clamp gains. + for (size_t k = 0; k < gain->size(); ++k) { + G[k] = std::max(std::min(G[k], max_gain[k]), min_gain[k]); + (*gain)[k] = std::min((*gain)[k], G[k]); + } + + // Store data required for the gain computation of the next block. + std::copy(nearend.begin(), nearend.end(), last_nearend_[ch].begin()); + std::copy(weighted_residual_echo.begin(), weighted_residual_echo.end(), + last_echo_[ch].begin()); + } + + LimitLowFrequencyGains(gain); + // Use conservative high-frequency gains during clock-drift or when not in + // dominant nearend. + if (!dominant_nearend_detector_->IsNearendState() || clock_drift || + config_.suppressor.conservative_hf_suppression) { + LimitHighFrequencyGains(config_.suppressor.conservative_hf_suppression, + gain); + } + + // Store computed gains. + std::copy(gain->begin(), gain->end(), last_gain_.begin()); + + // Transform gains to amplitude domain. + aec3::VectorMath(optimization_).Sqrt(*gain); +} + +SuppressionGain::SuppressionGain(const EchoCanceller3Config& config, + Aec3Optimization optimization, + int /* sample_rate_hz */, + size_t num_capture_channels) + : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), + optimization_(optimization), + config_(config), + num_capture_channels_(num_capture_channels), + state_change_duration_blocks_( + static_cast(config_.filter.config_change_duration_blocks)), + last_nearend_(num_capture_channels_, {0}), + last_echo_(num_capture_channels_, {0}), + nearend_smoothers_( + num_capture_channels_, + aec3::MovingAverage(kFftLengthBy2Plus1, + config.suppressor.nearend_average_blocks)), + nearend_params_(config_.suppressor.last_lf_band, + config_.suppressor.first_hf_band, + config_.suppressor.nearend_tuning), + normal_params_(config_.suppressor.last_lf_band, + config_.suppressor.first_hf_band, + config_.suppressor.normal_tuning), + use_unbounded_echo_spectrum_(config.suppressor.dominant_nearend_detection + .use_unbounded_echo_spectrum) { + RTC_DCHECK_LT(0, state_change_duration_blocks_); + last_gain_.fill(1.f); + if (config_.suppressor.use_subband_nearend_detection) { + dominant_nearend_detector_ = std::make_unique( + config_.suppressor.subband_nearend_detection, num_capture_channels_); + } else { + dominant_nearend_detector_ = std::make_unique( + config_.suppressor.dominant_nearend_detection, num_capture_channels_); + } + RTC_DCHECK(dominant_nearend_detector_); +} + +SuppressionGain::~SuppressionGain() = default; + +void SuppressionGain::GetGain( + ArrayView> nearend_spectrum, + ArrayView> echo_spectrum, + ArrayView> + residual_echo_spectrum, + ArrayView> + residual_echo_spectrum_unbounded, + ArrayView> + comfort_noise_spectrum, + const RenderSignalAnalyzer& render_signal_analyzer, + const AecState& aec_state, + const Block& render, + bool clock_drift, + float* high_bands_gain, + std::array* low_band_gain) { + RTC_DCHECK(high_bands_gain); + RTC_DCHECK(low_band_gain); + + // Choose residual echo spectrum for dominant nearend detection. + const auto echo = use_unbounded_echo_spectrum_ + ? residual_echo_spectrum_unbounded + : residual_echo_spectrum; + + // Update the nearend state selection. + dominant_nearend_detector_->Update(nearend_spectrum, echo, + comfort_noise_spectrum, initial_state_); + + // Compute gain for the lower band. + bool low_noise_render = low_render_detector_.Detect(render); + LowerBandGain(low_noise_render, aec_state, nearend_spectrum, + residual_echo_spectrum, comfort_noise_spectrum, clock_drift, + low_band_gain); + + // Compute the gain for the upper bands. + const std::optional narrow_peak_band = + render_signal_analyzer.NarrowPeakBand(); + + *high_bands_gain = + UpperBandsGain(echo_spectrum, comfort_noise_spectrum, narrow_peak_band, + aec_state.SaturatedEcho(), render, *low_band_gain); + + data_dumper_->DumpRaw("aec3_dominant_nearend", + dominant_nearend_detector_->IsNearendState()); +} + +void SuppressionGain::SetInitialState(bool state) { + initial_state_ = state; + if (state) { + initial_state_change_counter_ = state_change_duration_blocks_; + } else { + initial_state_change_counter_ = 0; + } +} + +// Detects when the render signal can be considered to have low power and +// consist of stationary noise. +bool SuppressionGain::LowNoiseRenderDetector::Detect(const Block& render) { + float x2_sum = 0.f; + float x2_max = 0.f; + for (int ch = 0; ch < render.NumChannels(); ++ch) { + for (float x_k : render.View(/*band=*/0, ch)) { + const float x2 = x_k * x_k; + x2_sum += x2; + x2_max = std::max(x2_max, x2); + } + } + x2_sum = x2_sum / render.NumChannels(); + + constexpr float kThreshold = 50.f * 50.f * 64.f; + const bool low_noise_render = + average_power_ < kThreshold && x2_max < 3 * average_power_; + average_power_ = average_power_ * 0.9f + x2_sum * 0.1f; + return low_noise_render; +} + +SuppressionGain::GainParameters::GainParameters( + int last_lf_band, + int first_hf_band, + const EchoCanceller3Config::Suppressor::Tuning& tuning) + : max_inc_factor(tuning.max_inc_factor), + max_dec_factor_lf(tuning.max_dec_factor_lf) { + // Compute per-band masking thresholds. + RTC_DCHECK_LT(last_lf_band, first_hf_band); + auto& lf = tuning.mask_lf; + auto& hf = tuning.mask_hf; + RTC_DCHECK_LT(lf.enr_transparent, lf.enr_suppress); + RTC_DCHECK_LT(hf.enr_transparent, hf.enr_suppress); + for (int k = 0; k < static_cast(kFftLengthBy2Plus1); k++) { + float a; + if (k <= last_lf_band) { + a = 0.f; + } else if (k < first_hf_band) { + a = (k - last_lf_band) / static_cast(first_hf_band - last_lf_band); + } else { + a = 1.f; + } + enr_transparent_[k] = (1 - a) * lf.enr_transparent + a * hf.enr_transparent; + enr_suppress_[k] = (1 - a) * lf.enr_suppress + a * hf.enr_suppress; + emr_transparent_[k] = (1 - a) * lf.emr_transparent + a * hf.emr_transparent; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_gain.h b/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_gain.h new file mode 100644 index 00000000..65d7985f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/suppression_gain.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_GAIN_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_GAIN_H_ + +#include +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec_state.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/nearend_detector.h" +#include "modules/audio_processing/aec3/render_signal_analyzer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +class SuppressionGain { + public: + SuppressionGain(const EchoCanceller3Config& config, + Aec3Optimization optimization, + int sample_rate_hz, + size_t num_capture_channels); + ~SuppressionGain(); + + SuppressionGain(const SuppressionGain&) = delete; + SuppressionGain& operator=(const SuppressionGain&) = delete; + + void GetGain( + ArrayView> nearend_spectrum, + ArrayView> echo_spectrum, + ArrayView> + residual_echo_spectrum, + ArrayView> + residual_echo_spectrum_unbounded, + ArrayView> + comfort_noise_spectrum, + const RenderSignalAnalyzer& render_signal_analyzer, + const AecState& aec_state, + const Block& render, + bool clock_drift, + float* high_bands_gain, + std::array* low_band_gain); + + bool IsDominantNearend() { + return dominant_nearend_detector_->IsNearendState(); + } + + // Toggles the usage of the initial state. + void SetInitialState(bool state); + + private: + // Computes the gain to apply for the bands beyond the first band. + float UpperBandsGain( + ArrayView> echo_spectrum, + ArrayView> + comfort_noise_spectrum, + const std::optional& narrow_peak_band, + bool saturated_echo, + const Block& render, + const std::array& low_band_gain) const; + + void GainToNoAudibleEcho(const std::array& nearend, + const std::array& echo, + const std::array& masker, + std::array* gain) const; + + void LowerBandGain( + bool stationary_with_low_power, + const AecState& aec_state, + ArrayView> suppressor_input, + ArrayView> residual_echo, + ArrayView> comfort_noise, + bool clock_drift, + std::array* gain); + + void GetMinGain(ArrayView weighted_residual_echo, + ArrayView last_nearend, + ArrayView last_echo, + bool low_noise_render, + bool saturated_echo, + ArrayView min_gain) const; + + void GetMaxGain(ArrayView max_gain) const; + + class LowNoiseRenderDetector { + public: + bool Detect(const Block& render); + + private: + float average_power_ = 32768.f * 32768.f; + }; + + struct GainParameters { + explicit GainParameters( + int last_lf_band, + int first_hf_band, + const EchoCanceller3Config::Suppressor::Tuning& tuning); + const float max_inc_factor; + const float max_dec_factor_lf; + std::array enr_transparent_; + std::array enr_suppress_; + std::array emr_transparent_; + }; + + static std::atomic instance_count_; + std::unique_ptr data_dumper_; + const Aec3Optimization optimization_; + const EchoCanceller3Config config_; + const size_t num_capture_channels_; + const int state_change_duration_blocks_; + std::array last_gain_; + std::vector> last_nearend_; + std::vector> last_echo_; + LowNoiseRenderDetector low_render_detector_; + bool initial_state_ = true; + int initial_state_change_counter_ = 0; + std::vector nearend_smoothers_; + const GainParameters nearend_params_; + const GainParameters normal_params_; + // Determines if the dominant nearend detector uses the unbounded residual + // echo spectrum. + const bool use_unbounded_echo_spectrum_; + std::unique_ptr dominant_nearend_detector_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_GAIN_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/transparent_mode.cc b/pkg/apm/webrtc/modules/audio_processing/aec3/transparent_mode.cc new file mode 100644 index 00000000..1349d0d3 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/transparent_mode.cc @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aec3/transparent_mode.h" + +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace { + +constexpr size_t kBlocksSinceConvergencedFilterInit = 10000; +constexpr size_t kBlocksSinceConsistentEstimateInit = 10000; +constexpr float kInitialTransparentStateProbability = 0.2f; + +bool DeactivateTransparentMode(const FieldTrialsView& field_trials) { + return field_trials.IsEnabled("WebRTC-Aec3TransparentModeKillSwitch"); +} + +bool ActivateTransparentModeHmm(const FieldTrialsView& field_trials) { + return field_trials.IsEnabled("WebRTC-Aec3TransparentModeHmm"); +} + +} // namespace + +// Classifier that toggles transparent mode which reduces echo suppression when +// headsets are used. +class TransparentModeImpl : public TransparentMode { + public: + bool Active() const override { return transparency_activated_; } + + void Reset() override { + // Determines if transparent mode is used. + transparency_activated_ = false; + + // The estimated probability of being transparent mode. + prob_transparent_state_ = kInitialTransparentStateProbability; + } + + void Update(int /* filter_delay_blocks */, + bool /* any_filter_consistent */, + bool /* any_filter_converged */, + bool any_coarse_filter_converged, + bool /* all_filters_diverged */, + bool active_render, + bool /* saturated_capture */) override { + // The classifier is implemented as a Hidden Markov Model (HMM) with two + // hidden states: "normal" and "transparent". The estimated probabilities of + // the two states are updated by observing filter convergence during active + // render. The filters are less likely to be reported as converged when + // there is no echo present in the microphone signal. + + // The constants have been obtained by observing active_render and + // any_coarse_filter_converged under varying call scenarios. They + // have further been hand tuned to prefer normal state during uncertain + // regions (to avoid echo leaks). + + // The model is only updated during active render. + if (!active_render) + return; + + // Probability of switching from one state to the other. + constexpr float kSwitch = 0.000001f; + + // Probability of observing converged filters in states "normal" and + // "transparent" during active render. + constexpr float kConvergedNormal = 0.01f; + constexpr float kConvergedTransparent = 0.001f; + + // Probability of transitioning to transparent state from normal state and + // transparent state respectively. + constexpr float kA[2] = {kSwitch, 1.f - kSwitch}; + + // Probability of the two observations (converged filter or not converged + // filter) in normal state and transparent state respectively. + constexpr float kB[2][2] = { + {1.f - kConvergedNormal, kConvergedNormal}, + {1.f - kConvergedTransparent, kConvergedTransparent}}; + + // Probability of the two states before the update. + const float prob_transparent = prob_transparent_state_; + const float prob_normal = 1.f - prob_transparent; + + // Probability of transitioning to transparent state. + const float prob_transition_transparent = + prob_normal * kA[0] + prob_transparent * kA[1]; + const float prob_transition_normal = 1.f - prob_transition_transparent; + + // Observed output. + const int out = static_cast(any_coarse_filter_converged); + + // Joint probabilites of the observed output and respective states. + const float prob_joint_normal = prob_transition_normal * kB[0][out]; + const float prob_joint_transparent = + prob_transition_transparent * kB[1][out]; + + // Conditional probability of transparent state and the observed output. + RTC_DCHECK_GT(prob_joint_normal + prob_joint_transparent, 0.f); + prob_transparent_state_ = + prob_joint_transparent / (prob_joint_normal + prob_joint_transparent); + + // Transparent mode is only activated when its state probability is high. + // Dead zone between activation/deactivation thresholds to avoid switching + // back and forth. + if (prob_transparent_state_ > 0.95f) { + transparency_activated_ = true; + } else if (prob_transparent_state_ < 0.5f) { + transparency_activated_ = false; + } + } + + private: + bool transparency_activated_ = false; + float prob_transparent_state_ = kInitialTransparentStateProbability; +}; + +// Legacy classifier for toggling transparent mode. +class LegacyTransparentModeImpl : public TransparentMode { + public: + explicit LegacyTransparentModeImpl(const EchoCanceller3Config& config) + : linear_and_stable_echo_path_( + config.echo_removal_control.linear_and_stable_echo_path), + active_blocks_since_sane_filter_(kBlocksSinceConsistentEstimateInit), + non_converged_sequence_size_(kBlocksSinceConvergencedFilterInit) {} + + bool Active() const override { return transparency_activated_; } + + void Reset() override { + non_converged_sequence_size_ = kBlocksSinceConvergencedFilterInit; + diverged_sequence_size_ = 0; + strong_not_saturated_render_blocks_ = 0; + if (linear_and_stable_echo_path_) { + recent_convergence_during_activity_ = false; + } + } + + void Update(int filter_delay_blocks, + bool any_filter_consistent, + bool any_filter_converged, + bool /* any_coarse_filter_converged */, + bool all_filters_diverged, + bool active_render, + bool saturated_capture) override { + ++capture_block_counter_; + strong_not_saturated_render_blocks_ += + active_render && !saturated_capture ? 1 : 0; + + if (any_filter_consistent && filter_delay_blocks < 5) { + sane_filter_observed_ = true; + active_blocks_since_sane_filter_ = 0; + } else if (active_render) { + ++active_blocks_since_sane_filter_; + } + + bool sane_filter_recently_seen; + if (!sane_filter_observed_) { + sane_filter_recently_seen = + capture_block_counter_ <= 5 * kNumBlocksPerSecond; + } else { + sane_filter_recently_seen = + active_blocks_since_sane_filter_ <= 30 * kNumBlocksPerSecond; + } + + if (any_filter_converged) { + recent_convergence_during_activity_ = true; + active_non_converged_sequence_size_ = 0; + non_converged_sequence_size_ = 0; + ++num_converged_blocks_; + } else { + if (++non_converged_sequence_size_ > 20 * kNumBlocksPerSecond) { + num_converged_blocks_ = 0; + } + + if (active_render && + ++active_non_converged_sequence_size_ > 60 * kNumBlocksPerSecond) { + recent_convergence_during_activity_ = false; + } + } + + if (!all_filters_diverged) { + diverged_sequence_size_ = 0; + } else if (++diverged_sequence_size_ >= 60) { + // TODO(peah): Change these lines to ensure proper triggering of usable + // filter. + non_converged_sequence_size_ = kBlocksSinceConvergencedFilterInit; + } + + if (active_non_converged_sequence_size_ > 60 * kNumBlocksPerSecond) { + finite_erl_recently_detected_ = false; + } + if (num_converged_blocks_ > 50) { + finite_erl_recently_detected_ = true; + } + + if (finite_erl_recently_detected_) { + transparency_activated_ = false; + } else if (sane_filter_recently_seen && + recent_convergence_during_activity_) { + transparency_activated_ = false; + } else { + const bool filter_should_have_converged = + strong_not_saturated_render_blocks_ > 6 * kNumBlocksPerSecond; + transparency_activated_ = filter_should_have_converged; + } + } + + private: + const bool linear_and_stable_echo_path_; + size_t capture_block_counter_ = 0; + bool transparency_activated_ = false; + size_t active_blocks_since_sane_filter_; + bool sane_filter_observed_ = false; + bool finite_erl_recently_detected_ = false; + size_t non_converged_sequence_size_; + size_t diverged_sequence_size_ = 0; + size_t active_non_converged_sequence_size_ = 0; + size_t num_converged_blocks_ = 0; + bool recent_convergence_during_activity_ = false; + size_t strong_not_saturated_render_blocks_ = 0; +}; + +std::unique_ptr TransparentMode::Create( + const Environment& env, + const EchoCanceller3Config& config) { + if (config.ep_strength.bounded_erl || + DeactivateTransparentMode(env.field_trials())) { + RTC_LOG(LS_INFO) << "AEC3 Transparent Mode: Disabled"; + return nullptr; + } + if (ActivateTransparentModeHmm(env.field_trials())) { + RTC_LOG(LS_INFO) << "AEC3 Transparent Mode: HMM"; + return std::make_unique(); + } + RTC_LOG(LS_INFO) << "AEC3 Transparent Mode: Legacy"; + return std::make_unique(config); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/transparent_mode.h b/pkg/apm/webrtc/modules/audio_processing/aec3/transparent_mode.h new file mode 100644 index 00000000..7b6148f9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/transparent_mode.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_TRANSPARENT_MODE_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_TRANSPARENT_MODE_H_ + +#include + +#include "api/audio/echo_canceller3_config.h" +#include "api/environment/environment.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// Class for detecting and toggling the transparent mode which causes the +// suppressor to apply less suppression. +class TransparentMode { + public: + static std::unique_ptr Create( + const Environment& env, + const EchoCanceller3Config& config); + + virtual ~TransparentMode() {} + + // Returns whether the transparent mode should be active. + virtual bool Active() const = 0; + + // Resets the state of the detector. + virtual void Reset() = 0; + + // Updates the detection decision based on new data. + virtual void Update(int filter_delay_blocks, + bool any_filter_consistent, + bool any_filter_converged, + bool any_coarse_filter_converged, + bool all_filters_diverged, + bool active_render, + bool saturated_capture) = 0; +}; + +} // namespace webrtc +#endif // MODULES_AUDIO_PROCESSING_AEC3_TRANSPARENT_MODE_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec3/vector_math.h b/pkg/apm/webrtc/modules/audio_processing/aec3/vector_math.h new file mode 100644 index 00000000..e6ccf6e8 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec3/vector_math.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_VECTOR_MATH_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_VECTOR_MATH_H_ + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_HAS_NEON) +#include +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace aec3 { + +// Provides optimizations for mathematical operations based on vectors. +class VectorMath { + public: + explicit VectorMath(Aec3Optimization optimization) + : optimization_(optimization) {} + + // Elementwise square root. + void SqrtAVX2(ArrayView x); + void Sqrt(ArrayView x) { + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + __m128 g = _mm_loadu_ps(&x[j]); + g = _mm_sqrt_ps(g); + _mm_storeu_ps(&x[j], g); + } + + for (; j < x_size; ++j) { + x[j] = sqrtf(x[j]); + } + } break; + case Aec3Optimization::kAvx2: + SqrtAVX2(x); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + float32x4_t g = vld1q_f32(&x[j]); +#if !defined(WEBRTC_ARCH_ARM64) + float32x4_t y = vrsqrteq_f32(g); + + // Code to handle sqrt(0). + // If the input to sqrtf() is zero, a zero will be returned. + // If the input to vrsqrteq_f32() is zero, positive infinity is + // returned. + const uint32x4_t vec_p_inf = vdupq_n_u32(0x7F800000); + // check for divide by zero + const uint32x4_t div_by_zero = + vceqq_u32(vec_p_inf, vreinterpretq_u32_f32(y)); + // zero out the positive infinity results + y = vreinterpretq_f32_u32( + vandq_u32(vmvnq_u32(div_by_zero), vreinterpretq_u32_f32(y))); + // from arm documentation + // The Newton-Raphson iteration: + // y[n+1] = y[n] * (3 - d * (y[n] * y[n])) / 2) + // converges to (1/√d) if y0 is the result of VRSQRTE applied to d. + // + // Note: The precision did not improve after 2 iterations. + for (int i = 0; i < 2; i++) { + y = vmulq_f32(vrsqrtsq_f32(vmulq_f32(y, y), g), y); + } + // sqrt(g) = g * 1/sqrt(g) + g = vmulq_f32(g, y); +#else + g = vsqrtq_f32(g); +#endif + vst1q_f32(&x[j], g); + } + + for (; j < x_size; ++j) { + x[j] = sqrtf(x[j]); + } + } +#endif + break; + default: + std::for_each(x.begin(), x.end(), [](float& a) { a = sqrtf(a); }); + } + } + + // Elementwise vector multiplication z = x * y. + void MultiplyAVX2(ArrayView x, + ArrayView y, + ArrayView z); + void Multiply(ArrayView x, + ArrayView y, + ArrayView z) { + RTC_DCHECK_EQ(z.size(), x.size()); + RTC_DCHECK_EQ(z.size(), y.size()); + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + const __m128 x_j = _mm_loadu_ps(&x[j]); + const __m128 y_j = _mm_loadu_ps(&y[j]); + const __m128 z_j = _mm_mul_ps(x_j, y_j); + _mm_storeu_ps(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] = x[j] * y[j]; + } + } break; + case Aec3Optimization::kAvx2: + MultiplyAVX2(x, y, z); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + const float32x4_t x_j = vld1q_f32(&x[j]); + const float32x4_t y_j = vld1q_f32(&y[j]); + const float32x4_t z_j = vmulq_f32(x_j, y_j); + vst1q_f32(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] = x[j] * y[j]; + } + } break; +#endif + default: + std::transform(x.begin(), x.end(), y.begin(), z.begin(), + std::multiplies()); + } + } + + // Elementwise vector accumulation z += x. + void AccumulateAVX2(ArrayView x, ArrayView z); + void Accumulate(ArrayView x, ArrayView z) { + RTC_DCHECK_EQ(z.size(), x.size()); + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + const __m128 x_j = _mm_loadu_ps(&x[j]); + __m128 z_j = _mm_loadu_ps(&z[j]); + z_j = _mm_add_ps(x_j, z_j); + _mm_storeu_ps(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] += x[j]; + } + } break; + case Aec3Optimization::kAvx2: + AccumulateAVX2(x, z); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + const float32x4_t x_j = vld1q_f32(&x[j]); + float32x4_t z_j = vld1q_f32(&z[j]); + z_j = vaddq_f32(z_j, x_j); + vst1q_f32(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] += x[j]; + } + } break; +#endif + default: + std::transform(x.begin(), x.end(), z.begin(), z.begin(), + std::plus()); + } + } + + private: + Aec3Optimization optimization_; +}; + +} // namespace aec3 + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_VECTOR_MATH_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump.go b/pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump.go new file mode 100644 index 00000000..24b5af8b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump.go @@ -0,0 +1,10 @@ +//go:build console + +package aec_dump + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump_factory.h b/pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump_factory.h new file mode 100644 index 00000000..9a68691f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump_factory.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC_DUMP_AEC_DUMP_FACTORY_H_ +#define MODULES_AUDIO_PROCESSING_AEC_DUMP_AEC_DUMP_FACTORY_H_ + +#include + +#include "absl/base/nullability.h" +#include "absl/strings/string_view.h" +#include "api/task_queue/task_queue_base.h" +#include "modules/audio_processing/include/aec_dump.h" +#include "rtc_base/system/file_wrapper.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +class RTC_EXPORT AecDumpFactory { + public: + // The `worker_queue` must outlive the created AecDump instance. + // `max_log_size_bytes == -1` means the log size will be unlimited. + // The AecDump takes responsibility for `handle` and closes it in the + // destructor. A non-null return value indicates that the file has been + // sucessfully opened. + static absl_nullable std::unique_ptr Create( + FileWrapper file, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull worker_queue); + static absl_nullable std::unique_ptr Create( + absl::string_view file_name, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull worker_queue); + static absl_nullable std::unique_ptr Create( + FILE* absl_nonnull handle, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull worker_queue); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC_DUMP_AEC_DUMP_FACTORY_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump_impl.h b/pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump_impl.h new file mode 100644 index 00000000..7638758d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec_dump/aec_dump_impl.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC_DUMP_AEC_DUMP_IMPL_H_ +#define MODULES_AUDIO_PROCESSING_AEC_DUMP_AEC_DUMP_IMPL_H_ + +#include +#include +#include + +#include "api/task_queue/task_queue_base.h" +#include "modules/audio_processing/aec_dump/capture_stream_info.h" +#include "modules/audio_processing/include/aec_dump.h" +#include "rtc_base/race_checker.h" +#include "rtc_base/system/file_wrapper.h" +#include "rtc_base/thread_annotations.h" + +// Files generated at build-time by the protobuf compiler. +#ifdef WEBRTC_ANDROID_PLATFORM_BUILD +#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h" +#else +#include "modules/audio_processing/debug.pb.h" +#endif + +namespace webrtc { + +// Task-queue based implementation of AecDump. It is thread safe by +// relying on locks in TaskQueue. +class AecDumpImpl : public AecDump { + public: + // `max_log_size_bytes` - maximum number of bytes to write to the debug file, + // `max_log_size_bytes == -1` means the log size will be unlimited. + AecDumpImpl(FileWrapper debug_file, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull worker_queue); + AecDumpImpl(const AecDumpImpl&) = delete; + AecDumpImpl& operator=(const AecDumpImpl&) = delete; + ~AecDumpImpl() override; + + void WriteInitMessage(const ProcessingConfig& api_format, + int64_t time_now_ms) override; + void AddCaptureStreamInput(const AudioFrameView& src) override; + void AddCaptureStreamOutput(const AudioFrameView& src) override; + void AddCaptureStreamInput(const int16_t* const data, + int num_channels, + int samples_per_channel) override; + void AddCaptureStreamOutput(const int16_t* const data, + int num_channels, + int samples_per_channel) override; + void AddAudioProcessingState(const AudioProcessingState& state) override; + void WriteCaptureStreamMessage() override; + + void WriteRenderStreamMessage(const int16_t* const data, + int num_channels, + int samples_per_channel) override; + void WriteRenderStreamMessage( + const AudioFrameView& src) override; + + void WriteConfig(const InternalAPMConfig& config) override; + + void WriteRuntimeSetting( + const AudioProcessing::RuntimeSetting& runtime_setting) override; + + private: + void PostWriteToFileTask(std::unique_ptr event); + + FileWrapper debug_file_; + int64_t num_bytes_left_for_log_ = 0; + RaceChecker race_checker_; + TaskQueueBase* absl_nonnull worker_queue_; + CaptureStreamInfo capture_stream_info_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC_DUMP_AEC_DUMP_IMPL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec_dump/capture_stream_info.h b/pkg/apm/webrtc/modules/audio_processing/aec_dump/capture_stream_info.h new file mode 100644 index 00000000..572990c1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec_dump/capture_stream_info.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC_DUMP_CAPTURE_STREAM_INFO_H_ +#define MODULES_AUDIO_PROCESSING_AEC_DUMP_CAPTURE_STREAM_INFO_H_ + +#include +#include + +#include "modules/audio_processing/include/aec_dump.h" + +// Files generated at build-time by the protobuf compiler. +#ifdef WEBRTC_ANDROID_PLATFORM_BUILD +#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h" +#else +#include "modules/audio_processing/debug.pb.h" +#endif + +namespace webrtc { + +class CaptureStreamInfo { + public: + CaptureStreamInfo() { CreateNewEvent(); } + CaptureStreamInfo(const CaptureStreamInfo&) = delete; + CaptureStreamInfo& operator=(const CaptureStreamInfo&) = delete; + ~CaptureStreamInfo() = default; + + void AddInput(const AudioFrameView& src); + void AddOutput(const AudioFrameView& src); + + void AddInput(const int16_t* const data, + int num_channels, + int samples_per_channel); + void AddOutput(const int16_t* const data, + int num_channels, + int samples_per_channel); + + void AddAudioProcessingState(const AecDump::AudioProcessingState& state); + + std::unique_ptr FetchEvent() { + std::unique_ptr result = std::move(event_); + CreateNewEvent(); + return result; + } + + private: + void CreateNewEvent() { + event_ = std::make_unique(); + event_->set_type(audioproc::Event::STREAM); + } + std::unique_ptr event_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC_DUMP_CAPTURE_STREAM_INFO_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/aec_dump/null_aec_dump_factory.cc b/pkg/apm/webrtc/modules/audio_processing/aec_dump/null_aec_dump_factory.cc new file mode 100644 index 00000000..9ab5d140 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aec_dump/null_aec_dump_factory.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "absl/base/nullability.h" +#include "absl/strings/string_view.h" +#include "api/task_queue/task_queue_base.h" +#include "modules/audio_processing/aec_dump/aec_dump_factory.h" +#include "modules/audio_processing/include/aec_dump.h" + +namespace webrtc { + +absl_nullable std::unique_ptr AecDumpFactory::Create( + FileWrapper file, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull worker_queue) { + return nullptr; +} + +absl_nullable std::unique_ptr AecDumpFactory::Create( + absl::string_view file_name, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull worker_queue) { + return nullptr; +} + +absl_nullable std::unique_ptr AecDumpFactory::Create( + FILE* absl_nonnull handle, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull worker_queue) { + return nullptr; +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aecm/aecm.go b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm.go new file mode 100644 index 00000000..8b452a03 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm.go @@ -0,0 +1,10 @@ +//go:build console + +package aecm + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core.cc b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core.cc new file mode 100644 index 00000000..7d3cd9df --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core.cc @@ -0,0 +1,1124 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aecm/aecm_core.h" + +#include +#include +#include + +extern "C" { +#include "common_audio/ring_buffer.h" +#include "common_audio/signal_processing/include/real_fft.h" +} +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "modules/audio_processing/aecm/echo_control_mobile.h" +#include "modules/audio_processing/utility/delay_estimator_wrapper.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { + +namespace { + +#ifdef AEC_DEBUG +FILE* dfile; +FILE* testfile; +#endif + +// Initialization table for echo channel in 8 kHz +static const int16_t kChannelStored8kHz[PART_LEN1] = { + 2040, 1815, 1590, 1498, 1405, 1395, 1385, 1418, 1451, 1506, 1562, + 1644, 1726, 1804, 1882, 1918, 1953, 1982, 2010, 2025, 2040, 2034, + 2027, 2021, 2014, 1997, 1980, 1925, 1869, 1800, 1732, 1683, 1635, + 1604, 1572, 1545, 1517, 1481, 1444, 1405, 1367, 1331, 1294, 1270, + 1245, 1239, 1233, 1247, 1260, 1282, 1303, 1338, 1373, 1407, 1441, + 1470, 1499, 1524, 1549, 1565, 1582, 1601, 1621, 1649, 1676}; + +// Initialization table for echo channel in 16 kHz +static const int16_t kChannelStored16kHz[PART_LEN1] = { + 2040, 1590, 1405, 1385, 1451, 1562, 1726, 1882, 1953, 2010, 2040, + 2027, 2014, 1980, 1869, 1732, 1635, 1572, 1517, 1444, 1367, 1294, + 1245, 1233, 1260, 1303, 1373, 1441, 1499, 1549, 1582, 1621, 1676, + 1741, 1802, 1861, 1921, 1983, 2040, 2102, 2170, 2265, 2375, 2515, + 2651, 2781, 2922, 3075, 3253, 3471, 3738, 3976, 4151, 4258, 4308, + 4288, 4270, 4253, 4237, 4179, 4086, 3947, 3757, 3484, 3153}; + +} // namespace + +const int16_t WebRtcAecm_kCosTable[] = { + 8192, 8190, 8187, 8180, 8172, 8160, 8147, 8130, 8112, 8091, 8067, + 8041, 8012, 7982, 7948, 7912, 7874, 7834, 7791, 7745, 7697, 7647, + 7595, 7540, 7483, 7424, 7362, 7299, 7233, 7164, 7094, 7021, 6947, + 6870, 6791, 6710, 6627, 6542, 6455, 6366, 6275, 6182, 6087, 5991, + 5892, 5792, 5690, 5586, 5481, 5374, 5265, 5155, 5043, 4930, 4815, + 4698, 4580, 4461, 4341, 4219, 4096, 3971, 3845, 3719, 3591, 3462, + 3331, 3200, 3068, 2935, 2801, 2667, 2531, 2395, 2258, 2120, 1981, + 1842, 1703, 1563, 1422, 1281, 1140, 998, 856, 713, 571, 428, + 285, 142, 0, -142, -285, -428, -571, -713, -856, -998, -1140, + -1281, -1422, -1563, -1703, -1842, -1981, -2120, -2258, -2395, -2531, -2667, + -2801, -2935, -3068, -3200, -3331, -3462, -3591, -3719, -3845, -3971, -4095, + -4219, -4341, -4461, -4580, -4698, -4815, -4930, -5043, -5155, -5265, -5374, + -5481, -5586, -5690, -5792, -5892, -5991, -6087, -6182, -6275, -6366, -6455, + -6542, -6627, -6710, -6791, -6870, -6947, -7021, -7094, -7164, -7233, -7299, + -7362, -7424, -7483, -7540, -7595, -7647, -7697, -7745, -7791, -7834, -7874, + -7912, -7948, -7982, -8012, -8041, -8067, -8091, -8112, -8130, -8147, -8160, + -8172, -8180, -8187, -8190, -8191, -8190, -8187, -8180, -8172, -8160, -8147, + -8130, -8112, -8091, -8067, -8041, -8012, -7982, -7948, -7912, -7874, -7834, + -7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424, -7362, -7299, -7233, + -7164, -7094, -7021, -6947, -6870, -6791, -6710, -6627, -6542, -6455, -6366, + -6275, -6182, -6087, -5991, -5892, -5792, -5690, -5586, -5481, -5374, -5265, + -5155, -5043, -4930, -4815, -4698, -4580, -4461, -4341, -4219, -4096, -3971, + -3845, -3719, -3591, -3462, -3331, -3200, -3068, -2935, -2801, -2667, -2531, + -2395, -2258, -2120, -1981, -1842, -1703, -1563, -1422, -1281, -1140, -998, + -856, -713, -571, -428, -285, -142, 0, 142, 285, 428, 571, + 713, 856, 998, 1140, 1281, 1422, 1563, 1703, 1842, 1981, 2120, + 2258, 2395, 2531, 2667, 2801, 2935, 3068, 3200, 3331, 3462, 3591, + 3719, 3845, 3971, 4095, 4219, 4341, 4461, 4580, 4698, 4815, 4930, + 5043, 5155, 5265, 5374, 5481, 5586, 5690, 5792, 5892, 5991, 6087, + 6182, 6275, 6366, 6455, 6542, 6627, 6710, 6791, 6870, 6947, 7021, + 7094, 7164, 7233, 7299, 7362, 7424, 7483, 7540, 7595, 7647, 7697, + 7745, 7791, 7834, 7874, 7912, 7948, 7982, 8012, 8041, 8067, 8091, + 8112, 8130, 8147, 8160, 8172, 8180, 8187, 8190}; + +const int16_t WebRtcAecm_kSinTable[] = { + 0, 142, 285, 428, 571, 713, 856, 998, 1140, 1281, 1422, + 1563, 1703, 1842, 1981, 2120, 2258, 2395, 2531, 2667, 2801, 2935, + 3068, 3200, 3331, 3462, 3591, 3719, 3845, 3971, 4095, 4219, 4341, + 4461, 4580, 4698, 4815, 4930, 5043, 5155, 5265, 5374, 5481, 5586, + 5690, 5792, 5892, 5991, 6087, 6182, 6275, 6366, 6455, 6542, 6627, + 6710, 6791, 6870, 6947, 7021, 7094, 7164, 7233, 7299, 7362, 7424, + 7483, 7540, 7595, 7647, 7697, 7745, 7791, 7834, 7874, 7912, 7948, + 7982, 8012, 8041, 8067, 8091, 8112, 8130, 8147, 8160, 8172, 8180, + 8187, 8190, 8191, 8190, 8187, 8180, 8172, 8160, 8147, 8130, 8112, + 8091, 8067, 8041, 8012, 7982, 7948, 7912, 7874, 7834, 7791, 7745, + 7697, 7647, 7595, 7540, 7483, 7424, 7362, 7299, 7233, 7164, 7094, + 7021, 6947, 6870, 6791, 6710, 6627, 6542, 6455, 6366, 6275, 6182, + 6087, 5991, 5892, 5792, 5690, 5586, 5481, 5374, 5265, 5155, 5043, + 4930, 4815, 4698, 4580, 4461, 4341, 4219, 4096, 3971, 3845, 3719, + 3591, 3462, 3331, 3200, 3068, 2935, 2801, 2667, 2531, 2395, 2258, + 2120, 1981, 1842, 1703, 1563, 1422, 1281, 1140, 998, 856, 713, + 571, 428, 285, 142, 0, -142, -285, -428, -571, -713, -856, + -998, -1140, -1281, -1422, -1563, -1703, -1842, -1981, -2120, -2258, -2395, + -2531, -2667, -2801, -2935, -3068, -3200, -3331, -3462, -3591, -3719, -3845, + -3971, -4095, -4219, -4341, -4461, -4580, -4698, -4815, -4930, -5043, -5155, + -5265, -5374, -5481, -5586, -5690, -5792, -5892, -5991, -6087, -6182, -6275, + -6366, -6455, -6542, -6627, -6710, -6791, -6870, -6947, -7021, -7094, -7164, + -7233, -7299, -7362, -7424, -7483, -7540, -7595, -7647, -7697, -7745, -7791, + -7834, -7874, -7912, -7948, -7982, -8012, -8041, -8067, -8091, -8112, -8130, + -8147, -8160, -8172, -8180, -8187, -8190, -8191, -8190, -8187, -8180, -8172, + -8160, -8147, -8130, -8112, -8091, -8067, -8041, -8012, -7982, -7948, -7912, + -7874, -7834, -7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424, -7362, + -7299, -7233, -7164, -7094, -7021, -6947, -6870, -6791, -6710, -6627, -6542, + -6455, -6366, -6275, -6182, -6087, -5991, -5892, -5792, -5690, -5586, -5481, + -5374, -5265, -5155, -5043, -4930, -4815, -4698, -4580, -4461, -4341, -4219, + -4096, -3971, -3845, -3719, -3591, -3462, -3331, -3200, -3068, -2935, -2801, + -2667, -2531, -2395, -2258, -2120, -1981, -1842, -1703, -1563, -1422, -1281, + -1140, -998, -856, -713, -571, -428, -285, -142}; + +// Moves the pointer to the next entry and inserts `far_spectrum` and +// corresponding Q-domain in its buffer. +// +// Inputs: +// - self : Pointer to the delay estimation instance +// - far_spectrum : Pointer to the far end spectrum +// - far_q : Q-domain of far end spectrum +// +void WebRtcAecm_UpdateFarHistory(AecmCore* self, + uint16_t* far_spectrum, + int far_q) { + // Get new buffer position + self->far_history_pos++; + if (self->far_history_pos >= MAX_DELAY) { + self->far_history_pos = 0; + } + // Update Q-domain buffer + self->far_q_domains[self->far_history_pos] = far_q; + // Update far end spectrum buffer + memcpy(&(self->far_history[self->far_history_pos * PART_LEN1]), far_spectrum, + sizeof(uint16_t) * PART_LEN1); +} + +// Returns a pointer to the far end spectrum aligned to current near end +// spectrum. The function WebRtc_DelayEstimatorProcessFix(...) should have been +// called before AlignedFarend(...). Otherwise, you get the pointer to the +// previous frame. The memory is only valid until the next call of +// WebRtc_DelayEstimatorProcessFix(...). +// +// Inputs: +// - self : Pointer to the AECM instance. +// - delay : Current delay estimate. +// +// Output: +// - far_q : The Q-domain of the aligned far end spectrum +// +// Return value: +// - far_spectrum : Pointer to the aligned far end spectrum +// NULL - Error +// +const uint16_t* WebRtcAecm_AlignedFarend(AecmCore* self, + int* far_q, + int delay) { + int buffer_position = 0; + RTC_DCHECK(self); + buffer_position = self->far_history_pos - delay; + + // Check buffer position + if (buffer_position < 0) { + buffer_position += MAX_DELAY; + } + // Get Q-domain + *far_q = self->far_q_domains[buffer_position]; + // Return far end spectrum + return &(self->far_history[buffer_position * PART_LEN1]); +} + +// Declare function pointers. +CalcLinearEnergies WebRtcAecm_CalcLinearEnergies; +StoreAdaptiveChannel WebRtcAecm_StoreAdaptiveChannel; +ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel; + +AecmCore* WebRtcAecm_CreateCore() { + // Allocate zero-filled memory. + AecmCore* aecm = static_cast(calloc(1, sizeof(AecmCore))); + + aecm->farFrameBuf = + WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); + if (!aecm->farFrameBuf) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + + aecm->nearNoisyFrameBuf = + WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); + if (!aecm->nearNoisyFrameBuf) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + + aecm->nearCleanFrameBuf = + WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); + if (!aecm->nearCleanFrameBuf) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + + aecm->outFrameBuf = + WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); + if (!aecm->outFrameBuf) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + + aecm->delay_estimator_farend = + WebRtc_CreateDelayEstimatorFarend(PART_LEN1, MAX_DELAY); + if (aecm->delay_estimator_farend == NULL) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + aecm->delay_estimator = + WebRtc_CreateDelayEstimator(aecm->delay_estimator_farend, 0); + if (aecm->delay_estimator == NULL) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + // TODO(bjornv): Explicitly disable robust delay validation until no + // performance regression has been established. Then remove the line. + WebRtc_enable_robust_validation(aecm->delay_estimator, 0); + + aecm->real_fft = WebRtcSpl_CreateRealFFT(PART_LEN_SHIFT); + if (aecm->real_fft == NULL) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + + // Init some aecm pointers. 16 and 32 byte alignment is only necessary + // for Neon code currently. + aecm->xBuf = (int16_t*)(((uintptr_t)aecm->xBuf_buf + 31) & ~31); + aecm->dBufClean = (int16_t*)(((uintptr_t)aecm->dBufClean_buf + 31) & ~31); + aecm->dBufNoisy = (int16_t*)(((uintptr_t)aecm->dBufNoisy_buf + 31) & ~31); + aecm->outBuf = (int16_t*)(((uintptr_t)aecm->outBuf_buf + 15) & ~15); + aecm->channelStored = + (int16_t*)(((uintptr_t)aecm->channelStored_buf + 15) & ~15); + aecm->channelAdapt16 = + (int16_t*)(((uintptr_t)aecm->channelAdapt16_buf + 15) & ~15); + aecm->channelAdapt32 = + (int32_t*)(((uintptr_t)aecm->channelAdapt32_buf + 31) & ~31); + + return aecm; +} + +void WebRtcAecm_InitEchoPathCore(AecmCore* aecm, const int16_t* echo_path) { + int i = 0; + + // Reset the stored channel + memcpy(aecm->channelStored, echo_path, sizeof(int16_t) * PART_LEN1); + // Reset the adapted channels + memcpy(aecm->channelAdapt16, echo_path, sizeof(int16_t) * PART_LEN1); + for (i = 0; i < PART_LEN1; i++) { + aecm->channelAdapt32[i] = (int32_t)aecm->channelAdapt16[i] << 16; + } + + // Reset channel storing variables + aecm->mseAdaptOld = 1000; + aecm->mseStoredOld = 1000; + aecm->mseThreshold = WEBRTC_SPL_WORD32_MAX; + aecm->mseChannelCount = 0; +} + +static void CalcLinearEnergiesC(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est, + uint32_t* far_energy, + uint32_t* echo_energy_adapt, + uint32_t* echo_energy_stored) { + int i; + + // Get energy for the delayed far end signal and estimated + // echo using both stored and adapted channels. + for (i = 0; i < PART_LEN1; i++) { + echo_est[i] = + WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); + (*far_energy) += (uint32_t)(far_spectrum[i]); + *echo_energy_adapt += aecm->channelAdapt16[i] * far_spectrum[i]; + (*echo_energy_stored) += (uint32_t)echo_est[i]; + } +} + +static void StoreAdaptiveChannelC(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est) { + int i; + + // During startup we store the channel every block. + memcpy(aecm->channelStored, aecm->channelAdapt16, + sizeof(int16_t) * PART_LEN1); + // Recalculate echo estimate + for (i = 0; i < PART_LEN; i += 4) { + echo_est[i] = + WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); + echo_est[i + 1] = + WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 1], far_spectrum[i + 1]); + echo_est[i + 2] = + WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 2], far_spectrum[i + 2]); + echo_est[i + 3] = + WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 3], far_spectrum[i + 3]); + } + echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); +} + +static void ResetAdaptiveChannelC(AecmCore* aecm) { + int i; + + // The stored channel has a significantly lower MSE than the adaptive one for + // two consecutive calculations. Reset the adaptive channel. + memcpy(aecm->channelAdapt16, aecm->channelStored, + sizeof(int16_t) * PART_LEN1); + // Restore the W32 channel + for (i = 0; i < PART_LEN; i += 4) { + aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; + aecm->channelAdapt32[i + 1] = (int32_t)aecm->channelStored[i + 1] << 16; + aecm->channelAdapt32[i + 2] = (int32_t)aecm->channelStored[i + 2] << 16; + aecm->channelAdapt32[i + 3] = (int32_t)aecm->channelStored[i + 3] << 16; + } + aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; +} + +// Initialize function pointers for ARM Neon platform. +#if defined(WEBRTC_HAS_NEON) +static void WebRtcAecm_InitNeon(void) { + WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannelNeon; + WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannelNeon; + WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergiesNeon; +} +#endif + +// Initialize function pointers for MIPS platform. +#if defined(MIPS32_LE) +static void WebRtcAecm_InitMips(void) { +#if defined(MIPS_DSP_R1_LE) + WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannel_mips; + WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannel_mips; +#endif + WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergies_mips; +} +#endif + +// WebRtcAecm_InitCore(...) +// +// This function initializes the AECM instant created with +// WebRtcAecm_CreateCore(...) Input: +// - aecm : Pointer to the Echo Suppression instance +// - samplingFreq : Sampling Frequency +// +// Output: +// - aecm : Initialized instance +// +// Return value : 0 - Ok +// -1 - Error +// +int WebRtcAecm_InitCore(AecmCore* const aecm, int samplingFreq) { + int i = 0; + int32_t tmp32 = PART_LEN1 * PART_LEN1; + int16_t tmp16 = PART_LEN1; + + if (samplingFreq != 8000 && samplingFreq != 16000) { + samplingFreq = 8000; + return -1; + } + // sanity check of sampling frequency + aecm->mult = (int16_t)samplingFreq / 8000; + + aecm->farBufWritePos = 0; + aecm->farBufReadPos = 0; + aecm->knownDelay = 0; + aecm->lastKnownDelay = 0; + + WebRtc_InitBuffer(aecm->farFrameBuf); + WebRtc_InitBuffer(aecm->nearNoisyFrameBuf); + WebRtc_InitBuffer(aecm->nearCleanFrameBuf); + WebRtc_InitBuffer(aecm->outFrameBuf); + + memset(aecm->xBuf_buf, 0, sizeof(aecm->xBuf_buf)); + memset(aecm->dBufClean_buf, 0, sizeof(aecm->dBufClean_buf)); + memset(aecm->dBufNoisy_buf, 0, sizeof(aecm->dBufNoisy_buf)); + memset(aecm->outBuf_buf, 0, sizeof(aecm->outBuf_buf)); + + aecm->seed = 666; + aecm->totCount = 0; + + if (WebRtc_InitDelayEstimatorFarend(aecm->delay_estimator_farend) != 0) { + return -1; + } + if (WebRtc_InitDelayEstimator(aecm->delay_estimator) != 0) { + return -1; + } + // Set far end histories to zero + memset(aecm->far_history, 0, sizeof(uint16_t) * PART_LEN1 * MAX_DELAY); + memset(aecm->far_q_domains, 0, sizeof(int) * MAX_DELAY); + aecm->far_history_pos = MAX_DELAY; + + aecm->nlpFlag = 1; + aecm->fixedDelay = -1; + + aecm->dfaCleanQDomain = 0; + aecm->dfaCleanQDomainOld = 0; + aecm->dfaNoisyQDomain = 0; + aecm->dfaNoisyQDomainOld = 0; + + memset(aecm->nearLogEnergy, 0, sizeof(aecm->nearLogEnergy)); + aecm->farLogEnergy = 0; + memset(aecm->echoAdaptLogEnergy, 0, sizeof(aecm->echoAdaptLogEnergy)); + memset(aecm->echoStoredLogEnergy, 0, sizeof(aecm->echoStoredLogEnergy)); + + // Initialize the echo channels with a stored shape. + if (samplingFreq == 8000) { + WebRtcAecm_InitEchoPathCore(aecm, kChannelStored8kHz); + } else { + WebRtcAecm_InitEchoPathCore(aecm, kChannelStored16kHz); + } + + memset(aecm->echoFilt, 0, sizeof(aecm->echoFilt)); + memset(aecm->nearFilt, 0, sizeof(aecm->nearFilt)); + aecm->noiseEstCtr = 0; + + aecm->cngMode = AecmTrue; + + memset(aecm->noiseEstTooLowCtr, 0, sizeof(aecm->noiseEstTooLowCtr)); + memset(aecm->noiseEstTooHighCtr, 0, sizeof(aecm->noiseEstTooHighCtr)); + // Shape the initial noise level to an approximate pink noise. + for (i = 0; i < (PART_LEN1 >> 1) - 1; i++) { + aecm->noiseEst[i] = (tmp32 << 8); + tmp16--; + tmp32 -= (int32_t)((tmp16 << 1) + 1); + } + for (; i < PART_LEN1; i++) { + aecm->noiseEst[i] = (tmp32 << 8); + } + + aecm->farEnergyMin = WEBRTC_SPL_WORD16_MAX; + aecm->farEnergyMax = WEBRTC_SPL_WORD16_MIN; + aecm->farEnergyMaxMin = 0; + aecm->farEnergyVAD = FAR_ENERGY_MIN; // This prevents false speech detection + // at the beginning. + aecm->farEnergyMSE = 0; + aecm->currentVADValue = 0; + aecm->vadUpdateCount = 0; + aecm->firstVAD = 1; + + aecm->startupState = 0; + aecm->supGain = SUPGAIN_DEFAULT; + aecm->supGainOld = SUPGAIN_DEFAULT; + + aecm->supGainErrParamA = SUPGAIN_ERROR_PARAM_A; + aecm->supGainErrParamD = SUPGAIN_ERROR_PARAM_D; + aecm->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B; + aecm->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D; + + // Assert a preprocessor definition at compile-time. It's an assumption + // used in assembly code, so check the assembly files before any change. + static_assert(PART_LEN % 16 == 0, "PART_LEN is not a multiple of 16"); + + // Initialize function pointers. + WebRtcAecm_CalcLinearEnergies = CalcLinearEnergiesC; + WebRtcAecm_StoreAdaptiveChannel = StoreAdaptiveChannelC; + WebRtcAecm_ResetAdaptiveChannel = ResetAdaptiveChannelC; + +#if defined(WEBRTC_HAS_NEON) + WebRtcAecm_InitNeon(); +#endif + +#if defined(MIPS32_LE) + WebRtcAecm_InitMips(); +#endif + return 0; +} + +// TODO(bjornv): This function is currently not used. Add support for these +// parameters from a higher level +int WebRtcAecm_Control(AecmCore* aecm, int delay, int nlpFlag) { + aecm->nlpFlag = nlpFlag; + aecm->fixedDelay = delay; + + return 0; +} + +void WebRtcAecm_FreeCore(AecmCore* aecm) { + if (aecm == NULL) { + return; + } + + WebRtc_FreeBuffer(aecm->farFrameBuf); + WebRtc_FreeBuffer(aecm->nearNoisyFrameBuf); + WebRtc_FreeBuffer(aecm->nearCleanFrameBuf); + WebRtc_FreeBuffer(aecm->outFrameBuf); + + WebRtc_FreeDelayEstimator(aecm->delay_estimator); + WebRtc_FreeDelayEstimatorFarend(aecm->delay_estimator_farend); + WebRtcSpl_FreeRealFFT(aecm->real_fft); + + free(aecm); +} + +int WebRtcAecm_ProcessFrame(AecmCore* aecm, + const int16_t* farend, + const int16_t* nearendNoisy, + const int16_t* nearendClean, + int16_t* out) { + int16_t outBlock_buf[PART_LEN + 8]; // Align buffer to 8-byte boundary. + int16_t* outBlock = (int16_t*)(((uintptr_t)outBlock_buf + 15) & ~15); + + int16_t farFrame[FRAME_LEN]; + const int16_t* out_ptr = NULL; + int size = 0; + + // Buffer the current frame. + // Fetch an older one corresponding to the delay. + WebRtcAecm_BufferFarFrame(aecm, farend, FRAME_LEN); + WebRtcAecm_FetchFarFrame(aecm, farFrame, FRAME_LEN, aecm->knownDelay); + + // Buffer the synchronized far and near frames, + // to pass the smaller blocks individually. + WebRtc_WriteBuffer(aecm->farFrameBuf, farFrame, FRAME_LEN); + WebRtc_WriteBuffer(aecm->nearNoisyFrameBuf, nearendNoisy, FRAME_LEN); + if (nearendClean != NULL) { + WebRtc_WriteBuffer(aecm->nearCleanFrameBuf, nearendClean, FRAME_LEN); + } + + // Process as many blocks as possible. + while (WebRtc_available_read(aecm->farFrameBuf) >= PART_LEN) { + int16_t far_block[PART_LEN]; + const int16_t* far_block_ptr = NULL; + int16_t near_noisy_block[PART_LEN]; + const int16_t* near_noisy_block_ptr = NULL; + + WebRtc_ReadBuffer(aecm->farFrameBuf, (void**)&far_block_ptr, far_block, + PART_LEN); + WebRtc_ReadBuffer(aecm->nearNoisyFrameBuf, (void**)&near_noisy_block_ptr, + near_noisy_block, PART_LEN); + if (nearendClean != NULL) { + int16_t near_clean_block[PART_LEN]; + const int16_t* near_clean_block_ptr = NULL; + + WebRtc_ReadBuffer(aecm->nearCleanFrameBuf, (void**)&near_clean_block_ptr, + near_clean_block, PART_LEN); + if (WebRtcAecm_ProcessBlock(aecm, far_block_ptr, near_noisy_block_ptr, + near_clean_block_ptr, outBlock) == -1) { + return -1; + } + } else { + if (WebRtcAecm_ProcessBlock(aecm, far_block_ptr, near_noisy_block_ptr, + NULL, outBlock) == -1) { + return -1; + } + } + + WebRtc_WriteBuffer(aecm->outFrameBuf, outBlock, PART_LEN); + } + + // Stuff the out buffer if we have less than a frame to output. + // This should only happen for the first frame. + size = (int)WebRtc_available_read(aecm->outFrameBuf); + if (size < FRAME_LEN) { + WebRtc_MoveReadPtr(aecm->outFrameBuf, size - FRAME_LEN); + } + + // Obtain an output frame. + WebRtc_ReadBuffer(aecm->outFrameBuf, (void**)&out_ptr, out, FRAME_LEN); + if (out_ptr != out) { + // ReadBuffer() hasn't copied to `out` in this case. + memcpy(out, out_ptr, FRAME_LEN * sizeof(int16_t)); + } + + return 0; +} + +// WebRtcAecm_AsymFilt(...) +// +// Performs asymmetric filtering. +// +// Inputs: +// - filtOld : Previous filtered value. +// - inVal : New input value. +// - stepSizePos : Step size when we have a positive contribution. +// - stepSizeNeg : Step size when we have a negative contribution. +// +// Output: +// +// Return: - Filtered value. +// +int16_t WebRtcAecm_AsymFilt(const int16_t filtOld, + const int16_t inVal, + const int16_t stepSizePos, + const int16_t stepSizeNeg) { + int16_t retVal; + + if ((filtOld == WEBRTC_SPL_WORD16_MAX) | (filtOld == WEBRTC_SPL_WORD16_MIN)) { + return inVal; + } + retVal = filtOld; + if (filtOld > inVal) { + retVal -= (filtOld - inVal) >> stepSizeNeg; + } else { + retVal += (inVal - filtOld) >> stepSizePos; + } + + return retVal; +} + +// ExtractFractionPart(a, zeros) +// +// returns the fraction part of `a`, with `zeros` number of leading zeros, as an +// int16_t scaled to Q8. There is no sanity check of `a` in the sense that the +// number of zeros match. +static int16_t ExtractFractionPart(uint32_t a, int zeros) { + return (int16_t)(((a << zeros) & 0x7FFFFFFF) >> 23); +} + +// Calculates and returns the log of `energy` in Q8. The input `energy` is +// supposed to be in Q(`q_domain`). +static int16_t LogOfEnergyInQ8(uint32_t energy, int q_domain) { + static const int16_t kLogLowValue = PART_LEN_SHIFT << 7; + int16_t log_energy_q8 = kLogLowValue; + if (energy > 0) { + int zeros = WebRtcSpl_NormU32(energy); + int16_t frac = ExtractFractionPart(energy, zeros); + // log2 of `energy` in Q8. + log_energy_q8 += ((31 - zeros) << 8) + frac - (q_domain << 8); + } + return log_energy_q8; +} + +// WebRtcAecm_CalcEnergies(...) +// +// This function calculates the log of energies for nearend, farend and +// estimated echoes. There is also an update of energy decision levels, i.e. +// internal VAD. +// +// +// @param aecm [i/o] Handle of the AECM instance. +// @param far_spectrum [in] Pointer to farend spectrum. +// @param far_q [in] Q-domain of farend spectrum. +// @param nearEner [in] Near end energy for current block in +// Q(aecm->dfaQDomain). +// @param echoEst [out] Estimated echo in Q(xfa_q+RESOLUTION_CHANNEL16). +// +void WebRtcAecm_CalcEnergies(AecmCore* aecm, + const uint16_t* far_spectrum, + const int16_t far_q, + const uint32_t nearEner, + int32_t* echoEst) { + // Local variables + uint32_t tmpAdapt = 0; + uint32_t tmpStored = 0; + uint32_t tmpFar = 0; + + int i; + + int16_t tmp16; + int16_t increase_max_shifts = 4; + int16_t decrease_max_shifts = 11; + int16_t increase_min_shifts = 11; + int16_t decrease_min_shifts = 3; + + // Get log of near end energy and store in buffer + + // Shift buffer + memmove(aecm->nearLogEnergy + 1, aecm->nearLogEnergy, + sizeof(int16_t) * (MAX_BUF_LEN - 1)); + + // Logarithm of integrated magnitude spectrum (nearEner) + aecm->nearLogEnergy[0] = LogOfEnergyInQ8(nearEner, aecm->dfaNoisyQDomain); + + WebRtcAecm_CalcLinearEnergies(aecm, far_spectrum, echoEst, &tmpFar, &tmpAdapt, + &tmpStored); + + // Shift buffers + memmove(aecm->echoAdaptLogEnergy + 1, aecm->echoAdaptLogEnergy, + sizeof(int16_t) * (MAX_BUF_LEN - 1)); + memmove(aecm->echoStoredLogEnergy + 1, aecm->echoStoredLogEnergy, + sizeof(int16_t) * (MAX_BUF_LEN - 1)); + + // Logarithm of delayed far end energy + aecm->farLogEnergy = LogOfEnergyInQ8(tmpFar, far_q); + + // Logarithm of estimated echo energy through adapted channel + aecm->echoAdaptLogEnergy[0] = + LogOfEnergyInQ8(tmpAdapt, RESOLUTION_CHANNEL16 + far_q); + + // Logarithm of estimated echo energy through stored channel + aecm->echoStoredLogEnergy[0] = + LogOfEnergyInQ8(tmpStored, RESOLUTION_CHANNEL16 + far_q); + + // Update farend energy levels (min, max, vad, mse) + if (aecm->farLogEnergy > FAR_ENERGY_MIN) { + if (aecm->startupState == 0) { + increase_max_shifts = 2; + decrease_min_shifts = 2; + increase_min_shifts = 8; + } + + aecm->farEnergyMin = + WebRtcAecm_AsymFilt(aecm->farEnergyMin, aecm->farLogEnergy, + increase_min_shifts, decrease_min_shifts); + aecm->farEnergyMax = + WebRtcAecm_AsymFilt(aecm->farEnergyMax, aecm->farLogEnergy, + increase_max_shifts, decrease_max_shifts); + aecm->farEnergyMaxMin = (aecm->farEnergyMax - aecm->farEnergyMin); + + // Dynamic VAD region size + tmp16 = 2560 - aecm->farEnergyMin; + if (tmp16 > 0) { + tmp16 = (int16_t)((tmp16 * FAR_ENERGY_VAD_REGION) >> 9); + } else { + tmp16 = 0; + } + tmp16 += FAR_ENERGY_VAD_REGION; + + if ((aecm->startupState == 0) | (aecm->vadUpdateCount > 1024)) { + // In startup phase or VAD update halted + aecm->farEnergyVAD = aecm->farEnergyMin + tmp16; + } else { + if (aecm->farEnergyVAD > aecm->farLogEnergy) { + aecm->farEnergyVAD += + (aecm->farLogEnergy + tmp16 - aecm->farEnergyVAD) >> 6; + aecm->vadUpdateCount = 0; + } else { + aecm->vadUpdateCount++; + } + } + // Put MSE threshold higher than VAD + aecm->farEnergyMSE = aecm->farEnergyVAD + (1 << 8); + } + + // Update VAD variables + if (aecm->farLogEnergy > aecm->farEnergyVAD) { + if ((aecm->startupState == 0) | (aecm->farEnergyMaxMin > FAR_ENERGY_DIFF)) { + // We are in startup or have significant dynamics in input speech level + aecm->currentVADValue = 1; + } + } else { + aecm->currentVADValue = 0; + } + if ((aecm->currentVADValue) && (aecm->firstVAD)) { + aecm->firstVAD = 0; + if (aecm->echoAdaptLogEnergy[0] > aecm->nearLogEnergy[0]) { + // The estimated echo has higher energy than the near end signal. + // This means that the initialization was too aggressive. Scale + // down by a factor 8 + for (i = 0; i < PART_LEN1; i++) { + aecm->channelAdapt16[i] >>= 3; + } + // Compensate the adapted echo energy level accordingly. + aecm->echoAdaptLogEnergy[0] -= (3 << 8); + aecm->firstVAD = 1; + } + } +} + +// WebRtcAecm_CalcStepSize(...) +// +// This function calculates the step size used in channel estimation +// +// +// @param aecm [in] Handle of the AECM instance. +// @param mu [out] (Return value) Stepsize in log2(), i.e. number of +// shifts. +// +// +int16_t WebRtcAecm_CalcStepSize(AecmCore* const aecm) { + int32_t tmp32; + int16_t tmp16; + int16_t mu = MU_MAX; + + // Here we calculate the step size mu used in the + // following NLMS based Channel estimation algorithm + if (!aecm->currentVADValue) { + // Far end energy level too low, no channel update + mu = 0; + } else if (aecm->startupState > 0) { + if (aecm->farEnergyMin >= aecm->farEnergyMax) { + mu = MU_MIN; + } else { + tmp16 = (aecm->farLogEnergy - aecm->farEnergyMin); + tmp32 = tmp16 * MU_DIFF; + tmp32 = WebRtcSpl_DivW32W16(tmp32, aecm->farEnergyMaxMin); + mu = MU_MIN - 1 - (int16_t)(tmp32); + // The -1 is an alternative to rounding. This way we get a larger + // stepsize, so we in some sense compensate for truncation in NLMS + } + if (mu < MU_MAX) { + mu = MU_MAX; // Equivalent with maximum step size of 2^-MU_MAX + } + } + + return mu; +} + +// WebRtcAecm_UpdateChannel(...) +// +// This function performs channel estimation. NLMS and decision on channel +// storage. +// +// +// @param aecm [i/o] Handle of the AECM instance. +// @param far_spectrum [in] Absolute value of the farend signal in Q(far_q) +// @param far_q [in] Q-domain of the farend signal +// @param dfa [in] Absolute value of the nearend signal +// (Q[aecm->dfaQDomain]) +// @param mu [in] NLMS step size. +// @param echoEst [i/o] Estimated echo in Q(far_q+RESOLUTION_CHANNEL16). +// +void WebRtcAecm_UpdateChannel(AecmCore* aecm, + const uint16_t* far_spectrum, + const int16_t far_q, + const uint16_t* const dfa, + const int16_t mu, + int32_t* echoEst) { + uint32_t tmpU32no1, tmpU32no2; + int32_t tmp32no1, tmp32no2; + int32_t mseStored; + int32_t mseAdapt; + + int i; + + int16_t zerosFar, zerosNum, zerosCh, zerosDfa; + int16_t shiftChFar, shiftNum, shift2ResChan; + int16_t tmp16no1; + int16_t xfaQ, dfaQ; + + // This is the channel estimation algorithm. It is base on NLMS but has a + // variable step length, which was calculated above. + if (mu) { + for (i = 0; i < PART_LEN1; i++) { + // Determine norm of channel and farend to make sure we don't get overflow + // in multiplication + zerosCh = WebRtcSpl_NormU32(aecm->channelAdapt32[i]); + zerosFar = WebRtcSpl_NormU32((uint32_t)far_spectrum[i]); + if (zerosCh + zerosFar > 31) { + // Multiplication is safe + tmpU32no1 = + WEBRTC_SPL_UMUL_32_16(aecm->channelAdapt32[i], far_spectrum[i]); + shiftChFar = 0; + } else { + // We need to shift down before multiplication + shiftChFar = 32 - zerosCh - zerosFar; + // If zerosCh == zerosFar == 0, shiftChFar is 32. A + // right shift of 32 is undefined. To avoid that, we + // do this check. + tmpU32no1 = + dchecked_cast( + shiftChFar >= 32 ? 0 : aecm->channelAdapt32[i] >> shiftChFar) * + far_spectrum[i]; + } + // Determine Q-domain of numerator + zerosNum = WebRtcSpl_NormU32(tmpU32no1); + if (dfa[i]) { + zerosDfa = WebRtcSpl_NormU32((uint32_t)dfa[i]); + } else { + zerosDfa = 32; + } + tmp16no1 = zerosDfa - 2 + aecm->dfaNoisyQDomain - RESOLUTION_CHANNEL32 - + far_q + shiftChFar; + if (zerosNum > tmp16no1 + 1) { + xfaQ = tmp16no1; + dfaQ = zerosDfa - 2; + } else { + xfaQ = zerosNum - 2; + dfaQ = RESOLUTION_CHANNEL32 + far_q - aecm->dfaNoisyQDomain - + shiftChFar + xfaQ; + } + // Add in the same Q-domain + tmpU32no1 = WEBRTC_SPL_SHIFT_W32(tmpU32no1, xfaQ); + tmpU32no2 = WEBRTC_SPL_SHIFT_W32((uint32_t)dfa[i], dfaQ); + tmp32no1 = (int32_t)tmpU32no2 - (int32_t)tmpU32no1; + zerosNum = WebRtcSpl_NormW32(tmp32no1); + if ((tmp32no1) && (far_spectrum[i] > (CHANNEL_VAD << far_q))) { + // + // Update is needed + // + // This is what we would like to compute + // + // tmp32no1 = dfa[i] - (aecm->channelAdapt[i] * far_spectrum[i]) + // tmp32norm = (i + 1) + // aecm->channelAdapt[i] += (2^mu) * tmp32no1 + // / (tmp32norm * far_spectrum[i]) + // + + // Make sure we don't get overflow in multiplication. + if (zerosNum + zerosFar > 31) { + if (tmp32no1 > 0) { + tmp32no2 = + (int32_t)WEBRTC_SPL_UMUL_32_16(tmp32no1, far_spectrum[i]); + } else { + tmp32no2 = + -(int32_t)WEBRTC_SPL_UMUL_32_16(-tmp32no1, far_spectrum[i]); + } + shiftNum = 0; + } else { + shiftNum = 32 - (zerosNum + zerosFar); + if (tmp32no1 > 0) { + tmp32no2 = (tmp32no1 >> shiftNum) * far_spectrum[i]; + } else { + tmp32no2 = -((-tmp32no1 >> shiftNum) * far_spectrum[i]); + } + } + // Normalize with respect to frequency bin + tmp32no2 = WebRtcSpl_DivW32W16(tmp32no2, i + 1); + // Make sure we are in the right Q-domain + shift2ResChan = + shiftNum + shiftChFar - xfaQ - mu - ((30 - zerosFar) << 1); + if (WebRtcSpl_NormW32(tmp32no2) < shift2ResChan) { + tmp32no2 = WEBRTC_SPL_WORD32_MAX; + } else { + tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, shift2ResChan); + } + aecm->channelAdapt32[i] = + WebRtcSpl_AddSatW32(aecm->channelAdapt32[i], tmp32no2); + if (aecm->channelAdapt32[i] < 0) { + // We can never have negative channel gain + aecm->channelAdapt32[i] = 0; + } + aecm->channelAdapt16[i] = (int16_t)(aecm->channelAdapt32[i] >> 16); + } + } + } + // END: Adaptive channel update + + // Determine if we should store or restore the channel + if ((aecm->startupState == 0) & (aecm->currentVADValue)) { + // During startup we store the channel every block, + // and we recalculate echo estimate + WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst); + } else { + if (aecm->farLogEnergy < aecm->farEnergyMSE) { + aecm->mseChannelCount = 0; + } else { + aecm->mseChannelCount++; + } + // Enough data for validation. Store channel if we can. + if (aecm->mseChannelCount >= (MIN_MSE_COUNT + 10)) { + // We have enough data. + // Calculate MSE of "Adapt" and "Stored" versions. + // It is actually not MSE, but average absolute error. + mseStored = 0; + mseAdapt = 0; + for (i = 0; i < MIN_MSE_COUNT; i++) { + tmp32no1 = ((int32_t)aecm->echoStoredLogEnergy[i] - + (int32_t)aecm->nearLogEnergy[i]); + tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1); + mseStored += tmp32no2; + + tmp32no1 = ((int32_t)aecm->echoAdaptLogEnergy[i] - + (int32_t)aecm->nearLogEnergy[i]); + tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1); + mseAdapt += tmp32no2; + } + if (((mseStored << MSE_RESOLUTION) < (MIN_MSE_DIFF * mseAdapt)) & + ((aecm->mseStoredOld << MSE_RESOLUTION) < + (MIN_MSE_DIFF * aecm->mseAdaptOld))) { + // The stored channel has a significantly lower MSE than the adaptive + // one for two consecutive calculations. Reset the adaptive channel. + WebRtcAecm_ResetAdaptiveChannel(aecm); + } else if (((MIN_MSE_DIFF * mseStored) > (mseAdapt << MSE_RESOLUTION)) & + (mseAdapt < aecm->mseThreshold) & + (aecm->mseAdaptOld < aecm->mseThreshold)) { + // The adaptive channel has a significantly lower MSE than the stored + // one. The MSE for the adaptive channel has also been low for two + // consecutive calculations. Store the adaptive channel. + WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst); + + // Update threshold + if (aecm->mseThreshold == WEBRTC_SPL_WORD32_MAX) { + aecm->mseThreshold = (mseAdapt + aecm->mseAdaptOld); + } else { + int scaled_threshold = aecm->mseThreshold * 5 / 8; + aecm->mseThreshold += ((mseAdapt - scaled_threshold) * 205) >> 8; + } + } + + // Reset counter + aecm->mseChannelCount = 0; + + // Store the MSE values. + aecm->mseStoredOld = mseStored; + aecm->mseAdaptOld = mseAdapt; + } + } + // END: Determine if we should store or reset channel estimate. +} + +// CalcSuppressionGain(...) +// +// This function calculates the suppression gain that is used in the Wiener +// filter. +// +// +// @param aecm [i/n] Handle of the AECM instance. +// @param supGain [out] (Return value) Suppression gain with which to scale +// the noise +// level (Q14). +// +// +int16_t WebRtcAecm_CalcSuppressionGain(AecmCore* const aecm) { + int32_t tmp32no1; + + int16_t supGain = SUPGAIN_DEFAULT; + int16_t tmp16no1; + int16_t dE = 0; + + // Determine suppression gain used in the Wiener filter. The gain is based on + // a mix of far end energy and echo estimation error. Adjust for the far end + // signal level. A low signal level indicates no far end signal, hence we set + // the suppression gain to 0 + if (!aecm->currentVADValue) { + supGain = 0; + } else { + // Adjust for possible double talk. If we have large variations in + // estimation error we likely have double talk (or poor channel). + tmp16no1 = (aecm->nearLogEnergy[0] - aecm->echoStoredLogEnergy[0] - + ENERGY_DEV_OFFSET); + dE = WEBRTC_SPL_ABS_W16(tmp16no1); + + if (dE < ENERGY_DEV_TOL) { + // Likely no double talk. The better estimation, the more we can suppress + // signal. Update counters + if (dE < SUPGAIN_EPC_DT) { + tmp32no1 = aecm->supGainErrParamDiffAB * dE; + tmp32no1 += (SUPGAIN_EPC_DT >> 1); + tmp16no1 = (int16_t)WebRtcSpl_DivW32W16(tmp32no1, SUPGAIN_EPC_DT); + supGain = aecm->supGainErrParamA - tmp16no1; + } else { + tmp32no1 = aecm->supGainErrParamDiffBD * (ENERGY_DEV_TOL - dE); + tmp32no1 += ((ENERGY_DEV_TOL - SUPGAIN_EPC_DT) >> 1); + tmp16no1 = (int16_t)WebRtcSpl_DivW32W16( + tmp32no1, (ENERGY_DEV_TOL - SUPGAIN_EPC_DT)); + supGain = aecm->supGainErrParamD + tmp16no1; + } + } else { + // Likely in double talk. Use default value + supGain = aecm->supGainErrParamD; + } + } + + if (supGain > aecm->supGainOld) { + tmp16no1 = supGain; + } else { + tmp16no1 = aecm->supGainOld; + } + aecm->supGainOld = supGain; + if (tmp16no1 < aecm->supGain) { + aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4); + } else { + aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4); + } + + // END: Update suppression gain + + return aecm->supGain; +} + +void WebRtcAecm_BufferFarFrame(AecmCore* const aecm, + const int16_t* const farend, + const int farLen) { + int writeLen = farLen, writePos = 0; + + // Check if the write position must be wrapped + while (aecm->farBufWritePos + writeLen > FAR_BUF_LEN) { + // Write to remaining buffer space before wrapping + writeLen = FAR_BUF_LEN - aecm->farBufWritePos; + memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos, + sizeof(int16_t) * writeLen); + aecm->farBufWritePos = 0; + writePos = writeLen; + writeLen = farLen - writeLen; + } + + memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos, + sizeof(int16_t) * writeLen); + aecm->farBufWritePos += writeLen; +} + +void WebRtcAecm_FetchFarFrame(AecmCore* const aecm, + int16_t* const farend, + const int farLen, + const int knownDelay) { + int readLen = farLen; + int readPos = 0; + int delayChange = knownDelay - aecm->lastKnownDelay; + + aecm->farBufReadPos -= delayChange; + + // Check if delay forces a read position wrap + while (aecm->farBufReadPos < 0) { + aecm->farBufReadPos += FAR_BUF_LEN; + } + while (aecm->farBufReadPos > FAR_BUF_LEN - 1) { + aecm->farBufReadPos -= FAR_BUF_LEN; + } + + aecm->lastKnownDelay = knownDelay; + + // Check if read position must be wrapped + while (aecm->farBufReadPos + readLen > FAR_BUF_LEN) { + // Read from remaining buffer space before wrapping + readLen = FAR_BUF_LEN - aecm->farBufReadPos; + memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos, + sizeof(int16_t) * readLen); + aecm->farBufReadPos = 0; + readPos = readLen; + readLen = farLen - readLen; + } + memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos, + sizeof(int16_t) * readLen); + aecm->farBufReadPos += readLen; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core.h b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core.h new file mode 100644 index 00000000..3de49315 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core.h @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Performs echo control (suppression) with fft routines in fixed-point. + +#ifndef MODULES_AUDIO_PROCESSING_AECM_AECM_CORE_H_ +#define MODULES_AUDIO_PROCESSING_AECM_AECM_CORE_H_ + +extern "C" { +#include "common_audio/ring_buffer.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +} +#include "modules/audio_processing/aecm/aecm_defines.h" + +struct RealFFT; + +namespace webrtc { + +#ifdef _MSC_VER // visual c++ +#define ALIGN8_BEG __declspec(align(8)) +#define ALIGN8_END +#else // gcc or icc +#define ALIGN8_BEG +#define ALIGN8_END __attribute__((aligned(8))) +#endif + +typedef struct { + int16_t real; + int16_t imag; +} ComplexInt16; + +typedef struct { + int farBufWritePos; + int farBufReadPos; + int knownDelay; + int lastKnownDelay; + int firstVAD; // Parameter to control poorly initialized channels + + RingBuffer* farFrameBuf; + RingBuffer* nearNoisyFrameBuf; + RingBuffer* nearCleanFrameBuf; + RingBuffer* outFrameBuf; + + int16_t farBuf[FAR_BUF_LEN]; + + int16_t mult; + uint32_t seed; + + // Delay estimation variables + void* delay_estimator_farend; + void* delay_estimator; + uint16_t currentDelay; + // Far end history variables + // TODO(bjornv): Replace `far_history` with ring_buffer. + uint16_t far_history[PART_LEN1 * MAX_DELAY]; + int far_history_pos; + int far_q_domains[MAX_DELAY]; + + int16_t nlpFlag; + int16_t fixedDelay; + + uint32_t totCount; + + int16_t dfaCleanQDomain; + int16_t dfaCleanQDomainOld; + int16_t dfaNoisyQDomain; + int16_t dfaNoisyQDomainOld; + + int16_t nearLogEnergy[MAX_BUF_LEN]; + int16_t farLogEnergy; + int16_t echoAdaptLogEnergy[MAX_BUF_LEN]; + int16_t echoStoredLogEnergy[MAX_BUF_LEN]; + + // The extra 16 or 32 bytes in the following buffers are for alignment based + // Neon code. + // It's designed this way since the current GCC compiler can't align a + // buffer in 16 or 32 byte boundaries properly. + int16_t channelStored_buf[PART_LEN1 + 8]; + int16_t channelAdapt16_buf[PART_LEN1 + 8]; + int32_t channelAdapt32_buf[PART_LEN1 + 8]; + int16_t xBuf_buf[PART_LEN2 + 16]; // farend + int16_t dBufClean_buf[PART_LEN2 + 16]; // nearend + int16_t dBufNoisy_buf[PART_LEN2 + 16]; // nearend + int16_t outBuf_buf[PART_LEN + 8]; + + // Pointers to the above buffers + int16_t* channelStored; + int16_t* channelAdapt16; + int32_t* channelAdapt32; + int16_t* xBuf; + int16_t* dBufClean; + int16_t* dBufNoisy; + int16_t* outBuf; + + int32_t echoFilt[PART_LEN1]; + int16_t nearFilt[PART_LEN1]; + int32_t noiseEst[PART_LEN1]; + int noiseEstTooLowCtr[PART_LEN1]; + int noiseEstTooHighCtr[PART_LEN1]; + int16_t noiseEstCtr; + int16_t cngMode; + + int32_t mseAdaptOld; + int32_t mseStoredOld; + int32_t mseThreshold; + + int16_t farEnergyMin; + int16_t farEnergyMax; + int16_t farEnergyMaxMin; + int16_t farEnergyVAD; + int16_t farEnergyMSE; + int currentVADValue; + int16_t vadUpdateCount; + + int16_t startupState; + int16_t mseChannelCount; + int16_t supGain; + int16_t supGainOld; + + int16_t supGainErrParamA; + int16_t supGainErrParamD; + int16_t supGainErrParamDiffAB; + int16_t supGainErrParamDiffBD; + + struct RealFFT* real_fft; + +#ifdef AEC_DEBUG + FILE* farFile; + FILE* nearFile; + FILE* outFile; +#endif +} AecmCore; + +//////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_CreateCore() +// +// Allocates the memory needed by the AECM. The memory needs to be +// initialized separately using the WebRtcAecm_InitCore() function. +// Returns a pointer to the instance and a nullptr at failure. +AecmCore* WebRtcAecm_CreateCore(); + +//////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_InitCore(...) +// +// This function initializes the AECM instant created with +// WebRtcAecm_CreateCore() +// Input: +// - aecm : Pointer to the AECM instance +// - samplingFreq : Sampling Frequency +// +// Output: +// - aecm : Initialized instance +// +// Return value : 0 - Ok +// -1 - Error +// +int WebRtcAecm_InitCore(AecmCore* const aecm, int samplingFreq); + +//////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_FreeCore(...) +// +// This function releases the memory allocated by WebRtcAecm_CreateCore() +// Input: +// - aecm : Pointer to the AECM instance +// +void WebRtcAecm_FreeCore(AecmCore* aecm); + +int WebRtcAecm_Control(AecmCore* aecm, int delay, int nlpFlag); + +//////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_InitEchoPathCore(...) +// +// This function resets the echo channel adaptation with the specified channel. +// Input: +// - aecm : Pointer to the AECM instance +// - echo_path : Pointer to the data that should initialize the echo +// path +// +// Output: +// - aecm : Initialized instance +// +void WebRtcAecm_InitEchoPathCore(AecmCore* aecm, const int16_t* echo_path); + +//////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_ProcessFrame(...) +// +// This function processes frames and sends blocks to +// WebRtcAecm_ProcessBlock(...) +// +// Inputs: +// - aecm : Pointer to the AECM instance +// - farend : In buffer containing one frame of echo signal +// - nearendNoisy : In buffer containing one frame of nearend+echo signal +// without NS +// - nearendClean : In buffer containing one frame of nearend+echo signal +// with NS +// +// Output: +// - out : Out buffer, one frame of nearend signal : +// +// +int WebRtcAecm_ProcessFrame(AecmCore* aecm, + const int16_t* farend, + const int16_t* nearendNoisy, + const int16_t* nearendClean, + int16_t* out); + +//////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_ProcessBlock(...) +// +// This function is called for every block within one frame +// This function is called by WebRtcAecm_ProcessFrame(...) +// +// Inputs: +// - aecm : Pointer to the AECM instance +// - farend : In buffer containing one block of echo signal +// - nearendNoisy : In buffer containing one frame of nearend+echo signal +// without NS +// - nearendClean : In buffer containing one frame of nearend+echo signal +// with NS +// +// Output: +// - out : Out buffer, one block of nearend signal : +// +// +int WebRtcAecm_ProcessBlock(AecmCore* aecm, + const int16_t* farend, + const int16_t* nearendNoisy, + const int16_t* noisyClean, + int16_t* out); + +//////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_BufferFarFrame() +// +// Inserts a frame of data into farend buffer. +// +// Inputs: +// - aecm : Pointer to the AECM instance +// - farend : In buffer containing one frame of farend signal +// - farLen : Length of frame +// +void WebRtcAecm_BufferFarFrame(AecmCore* const aecm, + const int16_t* const farend, + int farLen); + +//////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_FetchFarFrame() +// +// Read the farend buffer to account for known delay +// +// Inputs: +// - aecm : Pointer to the AECM instance +// - farend : In buffer containing one frame of farend signal +// - farLen : Length of frame +// - knownDelay : known delay +// +void WebRtcAecm_FetchFarFrame(AecmCore* const aecm, + int16_t* const farend, + int farLen, + int knownDelay); + +// All the functions below are intended to be private + +//////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_UpdateFarHistory() +// +// Moves the pointer to the next entry and inserts `far_spectrum` and +// corresponding Q-domain in its buffer. +// +// Inputs: +// - self : Pointer to the delay estimation instance +// - far_spectrum : Pointer to the far end spectrum +// - far_q : Q-domain of far end spectrum +// +void WebRtcAecm_UpdateFarHistory(AecmCore* self, + uint16_t* far_spectrum, + int far_q); + +//////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_AlignedFarend() +// +// Returns a pointer to the far end spectrum aligned to current near end +// spectrum. The function WebRtc_DelayEstimatorProcessFix(...) should have been +// called before AlignedFarend(...). Otherwise, you get the pointer to the +// previous frame. The memory is only valid until the next call of +// WebRtc_DelayEstimatorProcessFix(...). +// +// Inputs: +// - self : Pointer to the AECM instance. +// - delay : Current delay estimate. +// +// Output: +// - far_q : The Q-domain of the aligned far end spectrum +// +// Return value: +// - far_spectrum : Pointer to the aligned far end spectrum +// NULL - Error +// +const uint16_t* WebRtcAecm_AlignedFarend(AecmCore* self, int* far_q, int delay); + +/////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_CalcSuppressionGain() +// +// This function calculates the suppression gain that is used in the +// Wiener filter. +// +// Inputs: +// - aecm : Pointer to the AECM instance. +// +// Return value: +// - supGain : Suppression gain with which to scale the noise +// level (Q14). +// +int16_t WebRtcAecm_CalcSuppressionGain(AecmCore* const aecm); + +/////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_CalcEnergies() +// +// This function calculates the log of energies for nearend, farend and +// estimated echoes. There is also an update of energy decision levels, +// i.e. internal VAD. +// +// Inputs: +// - aecm : Pointer to the AECM instance. +// - far_spectrum : Pointer to farend spectrum. +// - far_q : Q-domain of farend spectrum. +// - nearEner : Near end energy for current block in +// Q(aecm->dfaQDomain). +// +// Output: +// - echoEst : Estimated echo in Q(xfa_q+RESOLUTION_CHANNEL16). +// +void WebRtcAecm_CalcEnergies(AecmCore* aecm, + const uint16_t* far_spectrum, + int16_t far_q, + uint32_t nearEner, + int32_t* echoEst); + +/////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_CalcStepSize() +// +// This function calculates the step size used in channel estimation +// +// Inputs: +// - aecm : Pointer to the AECM instance. +// +// Return value: +// - mu : Stepsize in log2(), i.e. number of shifts. +// +int16_t WebRtcAecm_CalcStepSize(AecmCore* const aecm); + +/////////////////////////////////////////////////////////////////////////////// +// WebRtcAecm_UpdateChannel(...) +// +// This function performs channel estimation. +// NLMS and decision on channel storage. +// +// Inputs: +// - aecm : Pointer to the AECM instance. +// - far_spectrum : Absolute value of the farend signal in Q(far_q) +// - far_q : Q-domain of the farend signal +// - dfa : Absolute value of the nearend signal +// (Q[aecm->dfaQDomain]) +// - mu : NLMS step size. +// Input/Output: +// - echoEst : Estimated echo in Q(far_q+RESOLUTION_CHANNEL16). +// +void WebRtcAecm_UpdateChannel(AecmCore* aecm, + const uint16_t* far_spectrum, + int16_t far_q, + const uint16_t* const dfa, + int16_t mu, + int32_t* echoEst); + +extern const int16_t WebRtcAecm_kCosTable[]; +extern const int16_t WebRtcAecm_kSinTable[]; + +/////////////////////////////////////////////////////////////////////////////// +// Some function pointers, for internal functions shared by ARM NEON and +// generic C code. +// +typedef void (*CalcLinearEnergies)(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echoEst, + uint32_t* far_energy, + uint32_t* echo_energy_adapt, + uint32_t* echo_energy_stored); +extern CalcLinearEnergies WebRtcAecm_CalcLinearEnergies; + +typedef void (*StoreAdaptiveChannel)(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est); +extern StoreAdaptiveChannel WebRtcAecm_StoreAdaptiveChannel; + +typedef void (*ResetAdaptiveChannel)(AecmCore* aecm); +extern ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel; + +// For the above function pointers, functions for generic platforms are declared +// and defined as static in file aecm_core.c, while those for ARM Neon platforms +// are declared below and defined in file aecm_core_neon.c. +#if defined(WEBRTC_HAS_NEON) +void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est, + uint32_t* far_energy, + uint32_t* echo_energy_adapt, + uint32_t* echo_energy_stored); + +void WebRtcAecm_StoreAdaptiveChannelNeon(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est); + +void WebRtcAecm_ResetAdaptiveChannelNeon(AecmCore* aecm); +#endif + +#if defined(MIPS32_LE) +void WebRtcAecm_CalcLinearEnergies_mips(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est, + uint32_t* far_energy, + uint32_t* echo_energy_adapt, + uint32_t* echo_energy_stored); +#if defined(MIPS_DSP_R1_LE) +void WebRtcAecm_StoreAdaptiveChannel_mips(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est); + +void WebRtcAecm_ResetAdaptiveChannel_mips(AecmCore* aecm); +#endif +#endif + +} // namespace webrtc + +#endif diff --git a/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core_c.cc b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core_c.cc new file mode 100644 index 00000000..1d750da3 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core_c.cc @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include "modules/audio_processing/aecm/aecm_core.h" + +extern "C" { +#include "common_audio/ring_buffer.h" +#include "common_audio/signal_processing/include/real_fft.h" +} +#include "modules/audio_processing/aecm/echo_control_mobile.h" +#include "modules/audio_processing/utility/delay_estimator_wrapper.h" +extern "C" { +#include "system_wrappers/include/cpu_features_wrapper.h" +} + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/sanitizer.h" + +namespace webrtc { + +namespace { + +// Square root of Hanning window in Q14. +static const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END = { + 0, 399, 798, 1196, 1594, 1990, 2386, 2780, 3172, 3562, 3951, + 4337, 4720, 5101, 5478, 5853, 6224, 6591, 6954, 7313, 7668, 8019, + 8364, 8705, 9040, 9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514, + 11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553, 13773, 13985, 14189, + 14384, 14571, 14749, 14918, 15079, 15231, 15373, 15506, 15631, 15746, 15851, + 15947, 16034, 16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384}; + +#ifdef AECM_WITH_ABS_APPROX +// Q15 alpha = 0.99439986968132 const Factor for magnitude approximation +static const uint16_t kAlpha1 = 32584; +// Q15 beta = 0.12967166976970 const Factor for magnitude approximation +static const uint16_t kBeta1 = 4249; +// Q15 alpha = 0.94234827210087 const Factor for magnitude approximation +static const uint16_t kAlpha2 = 30879; +// Q15 beta = 0.33787806009150 const Factor for magnitude approximation +static const uint16_t kBeta2 = 11072; +// Q15 alpha = 0.82247698684306 const Factor for magnitude approximation +static const uint16_t kAlpha3 = 26951; +// Q15 beta = 0.57762063060713 const Factor for magnitude approximation +static const uint16_t kBeta3 = 18927; +#endif + +static const int16_t kNoiseEstQDomain = 15; +static const int16_t kNoiseEstIncCount = 5; + +static void ComfortNoise(AecmCore* aecm, + const uint16_t* dfa, + ComplexInt16* out, + const int16_t* lambda) { + int16_t i; + int16_t tmp16; + int32_t tmp32; + + int16_t randW16[PART_LEN]; + int16_t uReal[PART_LEN1]; + int16_t uImag[PART_LEN1]; + int32_t outLShift32; + int16_t noiseRShift16[PART_LEN1]; + + int16_t shiftFromNearToNoise = kNoiseEstQDomain - aecm->dfaCleanQDomain; + int16_t minTrackShift; + + RTC_DCHECK_GE(shiftFromNearToNoise, 0); + RTC_DCHECK_LT(shiftFromNearToNoise, 16); + + if (aecm->noiseEstCtr < 100) { + // Track the minimum more quickly initially. + aecm->noiseEstCtr++; + minTrackShift = 6; + } else { + minTrackShift = 9; + } + + // Estimate noise power. + for (i = 0; i < PART_LEN1; i++) { + // Shift to the noise domain. + tmp32 = (int32_t)dfa[i]; + outLShift32 = tmp32 << shiftFromNearToNoise; + + if (outLShift32 < aecm->noiseEst[i]) { + // Reset "too low" counter + aecm->noiseEstTooLowCtr[i] = 0; + // Track the minimum. + if (aecm->noiseEst[i] < (1 << minTrackShift)) { + // For small values, decrease noiseEst[i] every + // `kNoiseEstIncCount` block. The regular approach below can not + // go further down due to truncation. + aecm->noiseEstTooHighCtr[i]++; + if (aecm->noiseEstTooHighCtr[i] >= kNoiseEstIncCount) { + aecm->noiseEst[i]--; + aecm->noiseEstTooHighCtr[i] = 0; // Reset the counter + } + } else { + aecm->noiseEst[i] -= + ((aecm->noiseEst[i] - outLShift32) >> minTrackShift); + } + } else { + // Reset "too high" counter + aecm->noiseEstTooHighCtr[i] = 0; + // Ramp slowly upwards until we hit the minimum again. + if ((aecm->noiseEst[i] >> 19) > 0) { + // Avoid overflow. + // Multiplication with 2049 will cause wrap around. Scale + // down first and then multiply + aecm->noiseEst[i] >>= 11; + aecm->noiseEst[i] *= 2049; + } else if ((aecm->noiseEst[i] >> 11) > 0) { + // Large enough for relative increase + aecm->noiseEst[i] *= 2049; + aecm->noiseEst[i] >>= 11; + } else { + // Make incremental increases based on size every + // `kNoiseEstIncCount` block + aecm->noiseEstTooLowCtr[i]++; + if (aecm->noiseEstTooLowCtr[i] >= kNoiseEstIncCount) { + aecm->noiseEst[i] += (aecm->noiseEst[i] >> 9) + 1; + aecm->noiseEstTooLowCtr[i] = 0; // Reset counter + } + } + } + } + + for (i = 0; i < PART_LEN1; i++) { + tmp32 = aecm->noiseEst[i] >> shiftFromNearToNoise; + if (tmp32 > 32767) { + tmp32 = 32767; + aecm->noiseEst[i] = tmp32 << shiftFromNearToNoise; + } + noiseRShift16[i] = (int16_t)tmp32; + + tmp16 = ONE_Q14 - lambda[i]; + noiseRShift16[i] = (int16_t)((tmp16 * noiseRShift16[i]) >> 14); + } + + // Generate a uniform random array on [0 2^15-1]. + WebRtcSpl_RandUArray(randW16, PART_LEN, &aecm->seed); + + // Generate noise according to estimated energy. + uReal[0] = 0; // Reject LF noise. + uImag[0] = 0; + for (i = 1; i < PART_LEN1; i++) { + // Get a random index for the cos and sin tables over [0 359]. + tmp16 = (int16_t)((359 * randW16[i - 1]) >> 15); + + // Tables are in Q13. + uReal[i] = + (int16_t)((noiseRShift16[i] * WebRtcAecm_kCosTable[tmp16]) >> 13); + uImag[i] = + (int16_t)((-noiseRShift16[i] * WebRtcAecm_kSinTable[tmp16]) >> 13); + } + uImag[PART_LEN] = 0; + + for (i = 0; i < PART_LEN1; i++) { + out[i].real = WebRtcSpl_AddSatW16(out[i].real, uReal[i]); + out[i].imag = WebRtcSpl_AddSatW16(out[i].imag, uImag[i]); + } +} + +static void WindowAndFFT(AecmCore* aecm, + int16_t* fft, + const int16_t* time_signal, + ComplexInt16* freq_signal, + int time_signal_scaling) { + int i = 0; + + // FFT of signal + for (i = 0; i < PART_LEN; i++) { + // Window time domain signal and insert into real part of + // transformation array `fft` + int16_t scaled_time_signal = time_signal[i] * (1 << time_signal_scaling); + fft[i] = (int16_t)((scaled_time_signal * WebRtcAecm_kSqrtHanning[i]) >> 14); + scaled_time_signal = time_signal[i + PART_LEN] * (1 << time_signal_scaling); + fft[PART_LEN + i] = (int16_t)((scaled_time_signal * + WebRtcAecm_kSqrtHanning[PART_LEN - i]) >> + 14); + } + + // Do forward FFT, then take only the first PART_LEN complex samples, + // and change signs of the imaginary parts. + WebRtcSpl_RealForwardFFT(aecm->real_fft, fft, (int16_t*)freq_signal); + for (i = 0; i < PART_LEN; i++) { + freq_signal[i].imag = -freq_signal[i].imag; + } +} + +static void InverseFFTAndWindow(AecmCore* aecm, + int16_t* fft, + ComplexInt16* efw, + int16_t* output, + const int16_t* nearendClean) { + int i, j, outCFFT; + int32_t tmp32no1; + // Reuse `efw` for the inverse FFT output after transferring + // the contents to `fft`. + int16_t* ifft_out = (int16_t*)efw; + + // Synthesis + for (i = 1, j = 2; i < PART_LEN; i += 1, j += 2) { + fft[j] = efw[i].real; + fft[j + 1] = -efw[i].imag; + } + fft[0] = efw[0].real; + fft[1] = -efw[0].imag; + + fft[PART_LEN2] = efw[PART_LEN].real; + fft[PART_LEN2 + 1] = -efw[PART_LEN].imag; + + // Inverse FFT. Keep outCFFT to scale the samples in the next block. + outCFFT = WebRtcSpl_RealInverseFFT(aecm->real_fft, fft, ifft_out); + for (i = 0; i < PART_LEN; i++) { + ifft_out[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( + ifft_out[i], WebRtcAecm_kSqrtHanning[i], 14); + tmp32no1 = WEBRTC_SPL_SHIFT_W32((int32_t)ifft_out[i], + outCFFT - aecm->dfaCleanQDomain); + output[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, + tmp32no1 + aecm->outBuf[i], + WEBRTC_SPL_WORD16_MIN); + + tmp32no1 = + (ifft_out[PART_LEN + i] * WebRtcAecm_kSqrtHanning[PART_LEN - i]) >> 14; + tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, outCFFT - aecm->dfaCleanQDomain); + aecm->outBuf[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, tmp32no1, + WEBRTC_SPL_WORD16_MIN); + } + + // Copy the current block to the old position + // (aecm->outBuf is shifted elsewhere) + memcpy(aecm->xBuf, aecm->xBuf + PART_LEN, sizeof(int16_t) * PART_LEN); + memcpy(aecm->dBufNoisy, aecm->dBufNoisy + PART_LEN, + sizeof(int16_t) * PART_LEN); + if (nearendClean != NULL) { + memcpy(aecm->dBufClean, aecm->dBufClean + PART_LEN, + sizeof(int16_t) * PART_LEN); + } +} + +// Transforms a time domain signal into the frequency domain, outputting the +// complex valued signal, absolute value and sum of absolute values. +// +// time_signal [in] Pointer to time domain signal +// freq_signal_real [out] Pointer to real part of frequency domain array +// freq_signal_imag [out] Pointer to imaginary part of frequency domain +// array +// freq_signal_abs [out] Pointer to absolute value of frequency domain +// array +// freq_signal_sum_abs [out] Pointer to the sum of all absolute values in +// the frequency domain array +// return value The Q-domain of current frequency values +// +static int TimeToFrequencyDomain(AecmCore* aecm, + const int16_t* time_signal, + ComplexInt16* freq_signal, + uint16_t* freq_signal_abs, + uint32_t* freq_signal_sum_abs) { + int i = 0; + int time_signal_scaling = 0; + + int32_t tmp32no1 = 0; + int32_t tmp32no2 = 0; + + // In fft_buf, +16 for 32-byte alignment. + int16_t fft_buf[PART_LEN4 + 16]; + int16_t* fft = (int16_t*)(((uintptr_t)fft_buf + 31) & ~31); + + int16_t tmp16no1; +#ifndef WEBRTC_ARCH_ARM_V7 + int16_t tmp16no2; +#endif +#ifdef AECM_WITH_ABS_APPROX + int16_t max_value = 0; + int16_t min_value = 0; + uint16_t alpha = 0; + uint16_t beta = 0; +#endif + +#ifdef AECM_DYNAMIC_Q + tmp16no1 = WebRtcSpl_MaxAbsValueW16(time_signal, PART_LEN2); + time_signal_scaling = WebRtcSpl_NormW16(tmp16no1); +#endif + + WindowAndFFT(aecm, fft, time_signal, freq_signal, time_signal_scaling); + + // Extract imaginary and real part, calculate the magnitude for + // all frequency bins + freq_signal[0].imag = 0; + freq_signal[PART_LEN].imag = 0; + freq_signal_abs[0] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[0].real); + freq_signal_abs[PART_LEN] = + (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[PART_LEN].real); + (*freq_signal_sum_abs) = + (uint32_t)(freq_signal_abs[0]) + (uint32_t)(freq_signal_abs[PART_LEN]); + + for (i = 1; i < PART_LEN; i++) { + if (freq_signal[i].real == 0) { + freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].imag); + } else if (freq_signal[i].imag == 0) { + freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].real); + } else { + // Approximation for magnitude of complex fft output + // magn = sqrt(real^2 + imag^2) + // magn ~= alpha * max(`imag`,`real`) + beta * min(`imag`,`real`) + // + // The parameters alpha and beta are stored in Q15 + +#ifdef AECM_WITH_ABS_APPROX + tmp16no1 = WEBRTC_SPL_ABS_W16(freq_signal[i].real); + tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag); + + if (tmp16no1 > tmp16no2) { + max_value = tmp16no1; + min_value = tmp16no2; + } else { + max_value = tmp16no2; + min_value = tmp16no1; + } + + // Magnitude in Q(-6) + if ((max_value >> 2) > min_value) { + alpha = kAlpha1; + beta = kBeta1; + } else if ((max_value >> 1) > min_value) { + alpha = kAlpha2; + beta = kBeta2; + } else { + alpha = kAlpha3; + beta = kBeta3; + } + tmp16no1 = (int16_t)((max_value * alpha) >> 15); + tmp16no2 = (int16_t)((min_value * beta) >> 15); + freq_signal_abs[i] = (uint16_t)tmp16no1 + (uint16_t)tmp16no2; +#else +#ifdef WEBRTC_ARCH_ARM_V7 + __asm __volatile( + "smulbb %[tmp32no1], %[real], %[real]\n\t" + "smlabb %[tmp32no2], %[imag], %[imag], %[tmp32no1]\n\t" + : [tmp32no1] "+&r"(tmp32no1), [tmp32no2] "=r"(tmp32no2) + : [real] "r"(freq_signal[i].real), [imag] "r"(freq_signal[i].imag)); +#else + tmp16no1 = WEBRTC_SPL_ABS_W16(freq_signal[i].real); + tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag); + tmp32no1 = tmp16no1 * tmp16no1; + tmp32no2 = tmp16no2 * tmp16no2; + tmp32no2 = WebRtcSpl_AddSatW32(tmp32no1, tmp32no2); +#endif // WEBRTC_ARCH_ARM_V7 + tmp32no1 = WebRtcSpl_SqrtFloor(tmp32no2); + + freq_signal_abs[i] = (uint16_t)tmp32no1; +#endif // AECM_WITH_ABS_APPROX + } + (*freq_signal_sum_abs) += (uint32_t)freq_signal_abs[i]; + } + + return time_signal_scaling; +} + +} // namespace + +int RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/8200 + WebRtcAecm_ProcessBlock(AecmCore* aecm, + const int16_t* farend, + const int16_t* nearendNoisy, + const int16_t* nearendClean, + int16_t* output) { + int i; + + uint32_t xfaSum; + uint32_t dfaNoisySum; + uint32_t dfaCleanSum; + uint32_t echoEst32Gained; + uint32_t tmpU32; + + int32_t tmp32no1; + + uint16_t xfa[PART_LEN1]; + uint16_t dfaNoisy[PART_LEN1]; + uint16_t dfaClean[PART_LEN1]; + uint16_t* ptrDfaClean = dfaClean; + const uint16_t* far_spectrum_ptr = NULL; + + // 32 byte aligned buffers (with +8 or +16). + // TODO(kma): define fft with ComplexInt16. + int16_t fft_buf[PART_LEN4 + 2 + 16]; // +2 to make a loop safe. + int32_t echoEst32_buf[PART_LEN1 + 8]; + int32_t dfw_buf[PART_LEN2 + 8]; + int32_t efw_buf[PART_LEN2 + 8]; + + int16_t* fft = (int16_t*)(((uintptr_t)fft_buf + 31) & ~31); + int32_t* echoEst32 = (int32_t*)(((uintptr_t)echoEst32_buf + 31) & ~31); + ComplexInt16* dfw = (ComplexInt16*)(((uintptr_t)dfw_buf + 31) & ~31); + ComplexInt16* efw = (ComplexInt16*)(((uintptr_t)efw_buf + 31) & ~31); + + int16_t hnl[PART_LEN1]; + int16_t numPosCoef = 0; + int16_t nlpGain = ONE_Q14; + int delay; + int16_t tmp16no1; + int16_t tmp16no2; + int16_t mu; + int16_t supGain; + int16_t zeros32, zeros16; + int16_t zerosDBufNoisy, zerosDBufClean, zerosXBuf; + int far_q; + int16_t resolutionDiff, qDomainDiff, dfa_clean_q_domain_diff; + + const int kMinPrefBand = 4; + const int kMaxPrefBand = 24; + int32_t avgHnl32 = 0; + + // Determine startup state. There are three states: + // (0) the first CONV_LEN blocks + // (1) another CONV_LEN blocks + // (2) the rest + + if (aecm->startupState < 2) { + aecm->startupState = + (aecm->totCount >= CONV_LEN) + (aecm->totCount >= CONV_LEN2); + } + // END: Determine startup state + + // Buffer near and far end signals + memcpy(aecm->xBuf + PART_LEN, farend, sizeof(int16_t) * PART_LEN); + memcpy(aecm->dBufNoisy + PART_LEN, nearendNoisy, sizeof(int16_t) * PART_LEN); + if (nearendClean != NULL) { + memcpy(aecm->dBufClean + PART_LEN, nearendClean, + sizeof(int16_t) * PART_LEN); + } + + // Transform far end signal from time domain to frequency domain. + far_q = TimeToFrequencyDomain(aecm, aecm->xBuf, dfw, xfa, &xfaSum); + + // Transform noisy near end signal from time domain to frequency domain. + zerosDBufNoisy = + TimeToFrequencyDomain(aecm, aecm->dBufNoisy, dfw, dfaNoisy, &dfaNoisySum); + aecm->dfaNoisyQDomainOld = aecm->dfaNoisyQDomain; + aecm->dfaNoisyQDomain = (int16_t)zerosDBufNoisy; + + if (nearendClean == NULL) { + ptrDfaClean = dfaNoisy; + aecm->dfaCleanQDomainOld = aecm->dfaNoisyQDomainOld; + aecm->dfaCleanQDomain = aecm->dfaNoisyQDomain; + dfaCleanSum = dfaNoisySum; + } else { + // Transform clean near end signal from time domain to frequency domain. + zerosDBufClean = TimeToFrequencyDomain(aecm, aecm->dBufClean, dfw, dfaClean, + &dfaCleanSum); + aecm->dfaCleanQDomainOld = aecm->dfaCleanQDomain; + aecm->dfaCleanQDomain = (int16_t)zerosDBufClean; + } + + // Get the delay + // Save far-end history and estimate delay + WebRtcAecm_UpdateFarHistory(aecm, xfa, far_q); + if (WebRtc_AddFarSpectrumFix(aecm->delay_estimator_farend, xfa, PART_LEN1, + far_q) == -1) { + return -1; + } + delay = WebRtc_DelayEstimatorProcessFix(aecm->delay_estimator, dfaNoisy, + PART_LEN1, zerosDBufNoisy); + if (delay == -1) { + return -1; + } else if (delay == -2) { + // If the delay is unknown, we assume zero. + // NOTE: this will have to be adjusted if we ever add lookahead. + delay = 0; + } + + if (aecm->fixedDelay >= 0) { + // Use fixed delay + delay = aecm->fixedDelay; + } + + // Get aligned far end spectrum + far_spectrum_ptr = WebRtcAecm_AlignedFarend(aecm, &far_q, delay); + zerosXBuf = (int16_t)far_q; + if (far_spectrum_ptr == NULL) { + return -1; + } + + // Calculate log(energy) and update energy threshold levels + WebRtcAecm_CalcEnergies(aecm, far_spectrum_ptr, zerosXBuf, dfaNoisySum, + echoEst32); + + // Calculate stepsize + mu = WebRtcAecm_CalcStepSize(aecm); + + // Update counters + aecm->totCount++; + + // This is the channel estimation algorithm. + // It is base on NLMS but has a variable step length, + // which was calculated above. + WebRtcAecm_UpdateChannel(aecm, far_spectrum_ptr, zerosXBuf, dfaNoisy, mu, + echoEst32); + supGain = WebRtcAecm_CalcSuppressionGain(aecm); + + // Calculate Wiener filter hnl[] + for (i = 0; i < PART_LEN1; i++) { + // Far end signal through channel estimate in Q8 + // How much can we shift right to preserve resolution + tmp32no1 = echoEst32[i] - aecm->echoFilt[i]; + aecm->echoFilt[i] += dchecked_cast((int64_t{tmp32no1} * 50) >> 8); + + zeros32 = WebRtcSpl_NormW32(aecm->echoFilt[i]) + 1; + zeros16 = WebRtcSpl_NormW16(supGain) + 1; + if (zeros32 + zeros16 > 16) { + // Multiplication is safe + // Result in + // Q(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN+ + // aecm->xfaQDomainBuf[diff]) + echoEst32Gained = + WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], (uint16_t)supGain); + resolutionDiff = 14 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; + resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); + } else { + tmp16no1 = 17 - zeros32 - zeros16; + resolutionDiff = + 14 + tmp16no1 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; + resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); + if (zeros32 > tmp16no1) { + echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], + supGain >> tmp16no1); + } else { + // Result in Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN-16) + echoEst32Gained = (aecm->echoFilt[i] >> tmp16no1) * supGain; + } + } + + zeros16 = WebRtcSpl_NormW16(aecm->nearFilt[i]); + RTC_DCHECK_GE(zeros16, 0); // `zeros16` is a norm, hence non-negative. + dfa_clean_q_domain_diff = aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld; + if (zeros16 < dfa_clean_q_domain_diff && aecm->nearFilt[i]) { + tmp16no1 = aecm->nearFilt[i] * (1 << zeros16); + qDomainDiff = zeros16 - dfa_clean_q_domain_diff; + tmp16no2 = ptrDfaClean[i] >> -qDomainDiff; + } else { + tmp16no1 = dfa_clean_q_domain_diff < 0 + ? aecm->nearFilt[i] >> -dfa_clean_q_domain_diff + : aecm->nearFilt[i] * (1 << dfa_clean_q_domain_diff); + qDomainDiff = 0; + tmp16no2 = ptrDfaClean[i]; + } + tmp32no1 = (int32_t)(tmp16no2 - tmp16no1); + tmp16no2 = (int16_t)(tmp32no1 >> 4); + tmp16no2 += tmp16no1; + zeros16 = WebRtcSpl_NormW16(tmp16no2); + if ((tmp16no2) & (-qDomainDiff > zeros16)) { + aecm->nearFilt[i] = WEBRTC_SPL_WORD16_MAX; + } else { + aecm->nearFilt[i] = qDomainDiff < 0 ? tmp16no2 * (1 << -qDomainDiff) + : tmp16no2 >> qDomainDiff; + } + + // Wiener filter coefficients, resulting hnl in Q14 + if (echoEst32Gained == 0) { + hnl[i] = ONE_Q14; + } else if (aecm->nearFilt[i] == 0) { + hnl[i] = 0; + } else { + // Multiply the suppression gain + // Rounding + echoEst32Gained += (uint32_t)(aecm->nearFilt[i] >> 1); + tmpU32 = + WebRtcSpl_DivU32U16(echoEst32Gained, (uint16_t)aecm->nearFilt[i]); + + // Current resolution is + // Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN- max(0,17-zeros16- zeros32)) + // Make sure we are in Q14 + tmp32no1 = (int32_t)WEBRTC_SPL_SHIFT_W32(tmpU32, resolutionDiff); + if (tmp32no1 > ONE_Q14) { + hnl[i] = 0; + } else if (tmp32no1 < 0) { + hnl[i] = ONE_Q14; + } else { + // 1-echoEst/dfa + hnl[i] = ONE_Q14 - (int16_t)tmp32no1; + if (hnl[i] < 0) { + hnl[i] = 0; + } + } + } + if (hnl[i]) { + numPosCoef++; + } + } + // Only in wideband. Prevent the gain in upper band from being larger than + // in lower band. + if (aecm->mult == 2) { + // TODO(bjornv): Investigate if the scaling of hnl[i] below can cause + // speech distortion in double-talk. + for (i = 0; i < PART_LEN1; i++) { + hnl[i] = (int16_t)((hnl[i] * hnl[i]) >> 14); + } + + for (i = kMinPrefBand; i <= kMaxPrefBand; i++) { + avgHnl32 += (int32_t)hnl[i]; + } + RTC_DCHECK_GT(kMaxPrefBand - kMinPrefBand + 1, 0); + avgHnl32 /= (kMaxPrefBand - kMinPrefBand + 1); + + for (i = kMaxPrefBand; i < PART_LEN1; i++) { + if (hnl[i] > (int16_t)avgHnl32) { + hnl[i] = (int16_t)avgHnl32; + } + } + } + + // Calculate NLP gain, result is in Q14 + if (aecm->nlpFlag) { + for (i = 0; i < PART_LEN1; i++) { + // Truncate values close to zero and one. + if (hnl[i] > NLP_COMP_HIGH) { + hnl[i] = ONE_Q14; + } else if (hnl[i] < NLP_COMP_LOW) { + hnl[i] = 0; + } + + // Remove outliers + if (numPosCoef < 3) { + nlpGain = 0; + } else { + nlpGain = ONE_Q14; + } + + // NLP + if ((hnl[i] == ONE_Q14) && (nlpGain == ONE_Q14)) { + hnl[i] = ONE_Q14; + } else { + hnl[i] = (int16_t)((hnl[i] * nlpGain) >> 14); + } + + // multiply with Wiener coefficients + efw[i].real = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real, + hnl[i], 14)); + efw[i].imag = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag, + hnl[i], 14)); + } + } else { + // multiply with Wiener coefficients + for (i = 0; i < PART_LEN1; i++) { + efw[i].real = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real, + hnl[i], 14)); + efw[i].imag = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag, + hnl[i], 14)); + } + } + + if (aecm->cngMode == AecmTrue) { + ComfortNoise(aecm, ptrDfaClean, efw, hnl); + } + + InverseFFTAndWindow(aecm, fft, efw, output, nearendClean); + + return 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core_neon_arm64.cc b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core_neon_arm64.cc new file mode 100644 index 00000000..584110d3 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_core_neon_arm64.cc @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "common_audio/signal_processing/include/real_fft.h" +#include "modules/audio_processing/aecm/aecm_core.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// TODO(kma): Re-write the corresponding assembly file, the offset +// generating script and makefile, to replace these C functions. + +static inline void AddLanes(uint32_t* ptr, uint32x4_t v) { +#if defined(WEBRTC_ARCH_ARM64) + *(ptr) = vaddvq_u32(v); +#else + uint32x2_t tmp_v; + tmp_v = vadd_u32(vget_low_u32(v), vget_high_u32(v)); + tmp_v = vpadd_u32(tmp_v, tmp_v); + *(ptr) = vget_lane_u32(tmp_v, 0); +#endif +} + +} // namespace + +void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est, + uint32_t* far_energy, + uint32_t* echo_energy_adapt, + uint32_t* echo_energy_stored) { + int16_t* start_stored_p = aecm->channelStored; + int16_t* start_adapt_p = aecm->channelAdapt16; + int32_t* echo_est_p = echo_est; + const int16_t* end_stored_p = aecm->channelStored + PART_LEN; + const uint16_t* far_spectrum_p = far_spectrum; + int16x8_t store_v, adapt_v; + uint16x8_t spectrum_v; + uint32x4_t echo_est_v_low, echo_est_v_high; + uint32x4_t far_energy_v, echo_stored_v, echo_adapt_v; + + far_energy_v = vdupq_n_u32(0); + echo_adapt_v = vdupq_n_u32(0); + echo_stored_v = vdupq_n_u32(0); + + // Get energy for the delayed far end signal and estimated + // echo using both stored and adapted channels. + // The C code: + // for (i = 0; i < PART_LEN1; i++) { + // echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], + // far_spectrum[i]); + // (*far_energy) += (uint32_t)(far_spectrum[i]); + // *echo_energy_adapt += aecm->channelAdapt16[i] * far_spectrum[i]; + // (*echo_energy_stored) += (uint32_t)echo_est[i]; + // } + while (start_stored_p < end_stored_p) { + spectrum_v = vld1q_u16(far_spectrum_p); + adapt_v = vld1q_s16(start_adapt_p); + store_v = vld1q_s16(start_stored_p); + + far_energy_v = vaddw_u16(far_energy_v, vget_low_u16(spectrum_v)); + far_energy_v = vaddw_u16(far_energy_v, vget_high_u16(spectrum_v)); + + echo_est_v_low = vmull_u16(vreinterpret_u16_s16(vget_low_s16(store_v)), + vget_low_u16(spectrum_v)); + echo_est_v_high = vmull_u16(vreinterpret_u16_s16(vget_high_s16(store_v)), + vget_high_u16(spectrum_v)); + vst1q_s32(echo_est_p, vreinterpretq_s32_u32(echo_est_v_low)); + vst1q_s32(echo_est_p + 4, vreinterpretq_s32_u32(echo_est_v_high)); + + echo_stored_v = vaddq_u32(echo_est_v_low, echo_stored_v); + echo_stored_v = vaddq_u32(echo_est_v_high, echo_stored_v); + + echo_adapt_v = + vmlal_u16(echo_adapt_v, vreinterpret_u16_s16(vget_low_s16(adapt_v)), + vget_low_u16(spectrum_v)); + echo_adapt_v = + vmlal_u16(echo_adapt_v, vreinterpret_u16_s16(vget_high_s16(adapt_v)), + vget_high_u16(spectrum_v)); + + start_stored_p += 8; + start_adapt_p += 8; + far_spectrum_p += 8; + echo_est_p += 8; + } + + AddLanes(far_energy, far_energy_v); + AddLanes(echo_energy_stored, echo_stored_v); + AddLanes(echo_energy_adapt, echo_adapt_v); + + echo_est[PART_LEN] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[PART_LEN], + far_spectrum[PART_LEN]); + *echo_energy_stored += (uint32_t)echo_est[PART_LEN]; + *far_energy += (uint32_t)far_spectrum[PART_LEN]; + *echo_energy_adapt += aecm->channelAdapt16[PART_LEN] * far_spectrum[PART_LEN]; +} + +void WebRtcAecm_StoreAdaptiveChannelNeon(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est) { + RTC_DCHECK_EQ(0, (uintptr_t)echo_est % 32); + RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelStored % 16); + RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelAdapt16 % 16); + + // This is C code of following optimized code. + // During startup we store the channel every block. + // memcpy(aecm->channelStored, + // aecm->channelAdapt16, + // sizeof(int16_t) * PART_LEN1); + // Recalculate echo estimate + // for (i = 0; i < PART_LEN; i += 4) { + // echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], + // far_spectrum[i]); + // echo_est[i + 1] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 1], + // far_spectrum[i + 1]); + // echo_est[i + 2] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 2], + // far_spectrum[i + 2]); + // echo_est[i + 3] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 3], + // far_spectrum[i + 3]); + // } + // echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], + // far_spectrum[i]); + const uint16_t* far_spectrum_p = far_spectrum; + int16_t* start_adapt_p = aecm->channelAdapt16; + int16_t* start_stored_p = aecm->channelStored; + const int16_t* end_stored_p = aecm->channelStored + PART_LEN; + int32_t* echo_est_p = echo_est; + + uint16x8_t far_spectrum_v; + int16x8_t adapt_v; + uint32x4_t echo_est_v_low, echo_est_v_high; + + while (start_stored_p < end_stored_p) { + far_spectrum_v = vld1q_u16(far_spectrum_p); + adapt_v = vld1q_s16(start_adapt_p); + + vst1q_s16(start_stored_p, adapt_v); + + echo_est_v_low = vmull_u16(vget_low_u16(far_spectrum_v), + vget_low_u16(vreinterpretq_u16_s16(adapt_v))); + echo_est_v_high = vmull_u16(vget_high_u16(far_spectrum_v), + vget_high_u16(vreinterpretq_u16_s16(adapt_v))); + + vst1q_s32(echo_est_p, vreinterpretq_s32_u32(echo_est_v_low)); + vst1q_s32(echo_est_p + 4, vreinterpretq_s32_u32(echo_est_v_high)); + + far_spectrum_p += 8; + start_adapt_p += 8; + start_stored_p += 8; + echo_est_p += 8; + } + aecm->channelStored[PART_LEN] = aecm->channelAdapt16[PART_LEN]; + echo_est[PART_LEN] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[PART_LEN], + far_spectrum[PART_LEN]); +} + +void WebRtcAecm_ResetAdaptiveChannelNeon(AecmCore* aecm) { + RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelStored % 16); + RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelAdapt16 % 16); + RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelAdapt32 % 32); + + // The C code of following optimized code. + // for (i = 0; i < PART_LEN1; i++) { + // aecm->channelAdapt16[i] = aecm->channelStored[i]; + // aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32( + // (int32_t)aecm->channelStored[i], 16); + // } + + int16_t* start_stored_p = aecm->channelStored; + int16_t* start_adapt16_p = aecm->channelAdapt16; + int32_t* start_adapt32_p = aecm->channelAdapt32; + const int16_t* end_stored_p = start_stored_p + PART_LEN; + + int16x8_t stored_v; + int32x4_t adapt32_v_low, adapt32_v_high; + + while (start_stored_p < end_stored_p) { + stored_v = vld1q_s16(start_stored_p); + vst1q_s16(start_adapt16_p, stored_v); + + adapt32_v_low = vshll_n_s16(vget_low_s16(stored_v), 16); + adapt32_v_high = vshll_n_s16(vget_high_s16(stored_v), 16); + + vst1q_s32(start_adapt32_p, adapt32_v_low); + vst1q_s32(start_adapt32_p + 4, adapt32_v_high); + + start_stored_p += 8; + start_adapt16_p += 8; + start_adapt32_p += 8; + } + aecm->channelAdapt16[PART_LEN] = aecm->channelStored[PART_LEN]; + aecm->channelAdapt32[PART_LEN] = (int32_t)aecm->channelStored[PART_LEN] << 16; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_defines.h b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_defines.h new file mode 100644 index 00000000..5805549e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aecm/aecm_defines.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AECM_AECM_DEFINES_H_ +#define MODULES_AUDIO_PROCESSING_AECM_AECM_DEFINES_H_ + +#define AECM_DYNAMIC_Q /* Turn on/off dynamic Q-domain. */ + +/* Algorithm parameters */ +#define FRAME_LEN 80 /* Total frame length, 10 ms. */ + +#define PART_LEN 64 /* Length of partition. */ +#define PART_LEN_SHIFT 7 /* Length of (PART_LEN * 2) in base 2. */ + +#define PART_LEN1 (PART_LEN + 1) /* Unique fft coefficients. */ +#define PART_LEN2 (PART_LEN << 1) /* Length of partition * 2. */ +#define PART_LEN4 (PART_LEN << 2) /* Length of partition * 4. */ +#define FAR_BUF_LEN PART_LEN4 /* Length of buffers. */ +#define MAX_DELAY 100 + +/* Counter parameters */ +#define CONV_LEN 512 /* Convergence length used at startup. */ +#define CONV_LEN2 (CONV_LEN << 1) /* Used at startup. */ + +/* Energy parameters */ +#define MAX_BUF_LEN 64 /* History length of energy signals. */ +#define FAR_ENERGY_MIN 1025 /* Lowest Far energy level: At least 2 */ + /* in energy. */ +#define FAR_ENERGY_DIFF 929 /* Allowed difference between max */ + /* and min. */ +#define ENERGY_DEV_OFFSET 0 /* The energy error offset in Q8. */ +#define ENERGY_DEV_TOL 400 /* The energy estimation tolerance (Q8). */ +#define FAR_ENERGY_VAD_REGION 230 /* Far VAD tolerance region. */ + +/* Stepsize parameters */ +#define MU_MIN 10 /* Min stepsize 2^-MU_MIN (far end energy */ + /* dependent). */ +#define MU_MAX 1 /* Max stepsize 2^-MU_MAX (far end energy */ + /* dependent). */ +#define MU_DIFF 9 /* MU_MIN - MU_MAX */ + +/* Channel parameters */ +#define MIN_MSE_COUNT 20 /* Min number of consecutive blocks with enough */ + /* far end energy to compare channel estimates. */ +#define MIN_MSE_DIFF 29 /* The ratio between adapted and stored channel to */ + /* accept a new storage (0.8 in Q-MSE_RESOLUTION). */ +#define MSE_RESOLUTION 5 /* MSE parameter resolution. */ +#define RESOLUTION_CHANNEL16 12 /* W16 Channel in Q-RESOLUTION_CHANNEL16. */ +#define RESOLUTION_CHANNEL32 28 /* W32 Channel in Q-RESOLUTION_CHANNEL. */ +#define CHANNEL_VAD 16 /* Minimum energy in frequency band */ + /* to update channel. */ + +/* Suppression gain parameters: SUPGAIN parameters in Q-(RESOLUTION_SUPGAIN). */ +#define RESOLUTION_SUPGAIN 8 /* Channel in Q-(RESOLUTION_SUPGAIN). */ +#define SUPGAIN_DEFAULT (1 << RESOLUTION_SUPGAIN) /* Default. */ +#define SUPGAIN_ERROR_PARAM_A 3072 /* Estimation error parameter */ + /* (Maximum gain) (8 in Q8). */ +#define SUPGAIN_ERROR_PARAM_B 1536 /* Estimation error parameter */ + /* (Gain before going down). */ +#define SUPGAIN_ERROR_PARAM_D SUPGAIN_DEFAULT /* Estimation error parameter */ +/* (Should be the same as Default) (1 in Q8). */ +#define SUPGAIN_EPC_DT 200 /* SUPGAIN_ERROR_PARAM_C * ENERGY_DEV_TOL */ + +/* Defines for "check delay estimation" */ +#define CORR_WIDTH 31 /* Number of samples to correlate over. */ +#define CORR_MAX 16 /* Maximum correlation offset. */ +#define CORR_MAX_BUF 63 +#define CORR_DEV 4 +#define CORR_MAX_LEVEL 20 +#define CORR_MAX_LOW 4 +#define CORR_BUF_LEN (CORR_MAX << 1) + 1 +/* Note that CORR_WIDTH + 2*CORR_MAX <= MAX_BUF_LEN. */ + +#define ONE_Q14 (1 << 14) + +/* NLP defines */ +#define NLP_COMP_LOW 3277 /* 0.2 in Q14 */ +#define NLP_COMP_HIGH ONE_Q14 /* 1 in Q14 */ + +#endif diff --git a/pkg/apm/webrtc/modules/audio_processing/aecm/echo_control_mobile.cc b/pkg/apm/webrtc/modules/audio_processing/aecm/echo_control_mobile.cc new file mode 100644 index 00000000..14522c0f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aecm/echo_control_mobile.cc @@ -0,0 +1,599 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/aecm/echo_control_mobile.h" + +#ifdef AEC_DEBUG +#include +#endif +#include +#include + +extern "C" { +#include "common_audio/ring_buffer.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "modules/audio_processing/aecm/aecm_defines.h" +} +#include "modules/audio_processing/aecm/aecm_core.h" + +namespace webrtc { + +namespace { + +#define BUF_SIZE_FRAMES 50 // buffer size (frames) +// Maximum length of resampled signal. Must be an integer multiple of frames +// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN +// The factor of 2 handles wb, and the + 1 is as a safety margin +#define MAX_RESAMP_LEN (5 * FRAME_LEN) + +static const size_t kBufSizeSamp = + BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples) +static const int kSampMsNb = 8; // samples per ms in nb +// Target suppression levels for nlp modes +// log{0.001, 0.00001, 0.00000001} +static const int kInitCheck = 42; + +typedef struct { + int sampFreq; + int scSampFreq; + short bufSizeStart; + int knownDelay; + + // Stores the last frame added to the farend buffer + short farendOld[2][FRAME_LEN]; + short initFlag; // indicates if AEC has been initialized + + // Variables used for averaging far end buffer size + short counter; + short sum; + short firstVal; + short checkBufSizeCtr; + + // Variables used for delay shifts + short msInSndCardBuf; + short filtDelay; + int timeForDelayChange; + int ECstartup; + int checkBuffSize; + int delayChange; + short lastDelayDiff; + + int16_t echoMode; + +#ifdef AEC_DEBUG + FILE* bufFile; + FILE* delayFile; + FILE* preCompFile; + FILE* postCompFile; +#endif // AEC_DEBUG + // Structures + RingBuffer* farendBuf; + + AecmCore* aecmCore; +} AecMobile; + +} // namespace + +// Estimates delay to set the position of the farend buffer read pointer +// (controlled by knownDelay) +static int WebRtcAecm_EstBufDelay(AecMobile* aecm, short msInSndCardBuf); + +// Stuffs the farend buffer if the estimated delay is too large +static int WebRtcAecm_DelayComp(AecMobile* aecm); + +void* WebRtcAecm_Create() { + // Allocate zero-filled memory. + AecMobile* aecm = static_cast(calloc(1, sizeof(AecMobile))); + + aecm->aecmCore = WebRtcAecm_CreateCore(); + if (!aecm->aecmCore) { + WebRtcAecm_Free(aecm); + return NULL; + } + + aecm->farendBuf = WebRtc_CreateBuffer(kBufSizeSamp, sizeof(int16_t)); + if (!aecm->farendBuf) { + WebRtcAecm_Free(aecm); + return NULL; + } + +#ifdef AEC_DEBUG + aecm->aecmCore->farFile = fopen("aecFar.pcm", "wb"); + aecm->aecmCore->nearFile = fopen("aecNear.pcm", "wb"); + aecm->aecmCore->outFile = fopen("aecOut.pcm", "wb"); + // aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb"); + + aecm->bufFile = fopen("aecBuf.dat", "wb"); + aecm->delayFile = fopen("aecDelay.dat", "wb"); + aecm->preCompFile = fopen("preComp.pcm", "wb"); + aecm->postCompFile = fopen("postComp.pcm", "wb"); +#endif // AEC_DEBUG + return aecm; +} + +void WebRtcAecm_Free(void* aecmInst) { + AecMobile* aecm = static_cast(aecmInst); + + if (aecm == NULL) { + return; + } + +#ifdef AEC_DEBUG + fclose(aecm->aecmCore->farFile); + fclose(aecm->aecmCore->nearFile); + fclose(aecm->aecmCore->outFile); + // fclose(aecm->aecmCore->outLpFile); + + fclose(aecm->bufFile); + fclose(aecm->delayFile); + fclose(aecm->preCompFile); + fclose(aecm->postCompFile); +#endif // AEC_DEBUG + WebRtcAecm_FreeCore(aecm->aecmCore); + WebRtc_FreeBuffer(aecm->farendBuf); + free(aecm); +} + +int32_t WebRtcAecm_Init(void* aecmInst, int32_t sampFreq) { + AecMobile* aecm = static_cast(aecmInst); + AecmConfig aecConfig; + + if (aecm == NULL) { + return -1; + } + + if (sampFreq != 8000 && sampFreq != 16000) { + return AECM_BAD_PARAMETER_ERROR; + } + aecm->sampFreq = sampFreq; + + // Initialize AECM core + if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1) { + return AECM_UNSPECIFIED_ERROR; + } + + // Initialize farend buffer + WebRtc_InitBuffer(aecm->farendBuf); + + aecm->initFlag = kInitCheck; // indicates that initialization has been done + + aecm->delayChange = 1; + + aecm->sum = 0; + aecm->counter = 0; + aecm->checkBuffSize = 1; + aecm->firstVal = 0; + + aecm->ECstartup = 1; + aecm->bufSizeStart = 0; + aecm->checkBufSizeCtr = 0; + aecm->filtDelay = 0; + aecm->timeForDelayChange = 0; + aecm->knownDelay = 0; + aecm->lastDelayDiff = 0; + + memset(&aecm->farendOld, 0, sizeof(aecm->farendOld)); + + // Default settings. + aecConfig.cngMode = AecmTrue; + aecConfig.echoMode = 3; + + if (WebRtcAecm_set_config(aecm, aecConfig) == -1) { + return AECM_UNSPECIFIED_ERROR; + } + + return 0; +} + +// Returns any error that is caused when buffering the +// farend signal. +int32_t WebRtcAecm_GetBufferFarendError(void* aecmInst, + const int16_t* farend, + size_t nrOfSamples) { + AecMobile* aecm = static_cast(aecmInst); + + if (aecm == NULL) + return -1; + + if (farend == NULL) + return AECM_NULL_POINTER_ERROR; + + if (aecm->initFlag != kInitCheck) + return AECM_UNINITIALIZED_ERROR; + + if (nrOfSamples != 80 && nrOfSamples != 160) + return AECM_BAD_PARAMETER_ERROR; + + return 0; +} + +int32_t WebRtcAecm_BufferFarend(void* aecmInst, + const int16_t* farend, + size_t nrOfSamples) { + AecMobile* aecm = static_cast(aecmInst); + + const int32_t err = + WebRtcAecm_GetBufferFarendError(aecmInst, farend, nrOfSamples); + + if (err != 0) + return err; + + // TODO(unknown): Is this really a good idea? + if (!aecm->ECstartup) { + WebRtcAecm_DelayComp(aecm); + } + + WebRtc_WriteBuffer(aecm->farendBuf, farend, nrOfSamples); + + return 0; +} + +int32_t WebRtcAecm_Process(void* aecmInst, + const int16_t* nearendNoisy, + const int16_t* nearendClean, + int16_t* out, + size_t nrOfSamples, + int16_t msInSndCardBuf) { + AecMobile* aecm = static_cast(aecmInst); + int32_t retVal = 0; + size_t i; + short nmbrOfFilledBuffers; + size_t nBlocks10ms; + size_t nFrames; +#ifdef AEC_DEBUG + short msInAECBuf; +#endif + + if (aecm == NULL) { + return -1; + } + + if (nearendNoisy == NULL) { + return AECM_NULL_POINTER_ERROR; + } + + if (out == NULL) { + return AECM_NULL_POINTER_ERROR; + } + + if (aecm->initFlag != kInitCheck) { + return AECM_UNINITIALIZED_ERROR; + } + + if (nrOfSamples != 80 && nrOfSamples != 160) { + return AECM_BAD_PARAMETER_ERROR; + } + + if (msInSndCardBuf < 0) { + msInSndCardBuf = 0; + retVal = AECM_BAD_PARAMETER_WARNING; + } else if (msInSndCardBuf > 500) { + msInSndCardBuf = 500; + retVal = AECM_BAD_PARAMETER_WARNING; + } + msInSndCardBuf += 10; + aecm->msInSndCardBuf = msInSndCardBuf; + + nFrames = nrOfSamples / FRAME_LEN; + nBlocks10ms = nFrames / aecm->aecmCore->mult; + + if (aecm->ECstartup) { + if (nearendClean == NULL) { + if (out != nearendNoisy) { + memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples); + } + } else if (out != nearendClean) { + memcpy(out, nearendClean, sizeof(short) * nrOfSamples); + } + + nmbrOfFilledBuffers = + (short)WebRtc_available_read(aecm->farendBuf) / FRAME_LEN; + // The AECM is in the start up mode + // AECM is disabled until the soundcard buffer and farend buffers are OK + + // Mechanism to ensure that the soundcard buffer is reasonably stable. + if (aecm->checkBuffSize) { + aecm->checkBufSizeCtr++; + // Before we fill up the far end buffer we require the amount of data on + // the sound card to be stable (+/-8 ms) compared to the first value. This + // comparison is made during the following 4 consecutive frames. If it + // seems to be stable then we start to fill up the far end buffer. + + if (aecm->counter == 0) { + aecm->firstVal = aecm->msInSndCardBuf; + aecm->sum = 0; + } + + if (abs(aecm->firstVal - aecm->msInSndCardBuf) < + WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb)) { + aecm->sum += aecm->msInSndCardBuf; + aecm->counter++; + } else { + aecm->counter = 0; + } + + if (aecm->counter * nBlocks10ms >= 6) { + // The farend buffer size is determined in blocks of 80 samples + // Use 75% of the average value of the soundcard buffer + aecm->bufSizeStart = WEBRTC_SPL_MIN( + (3 * aecm->sum * aecm->aecmCore->mult) / (aecm->counter * 40), + BUF_SIZE_FRAMES); + // buffersize has now been determined + aecm->checkBuffSize = 0; + } + + if (aecm->checkBufSizeCtr * nBlocks10ms > 50) { + // for really bad sound cards, don't disable echocanceller for more than + // 0.5 sec + aecm->bufSizeStart = WEBRTC_SPL_MIN( + (3 * aecm->msInSndCardBuf * aecm->aecmCore->mult) / 40, + BUF_SIZE_FRAMES); + aecm->checkBuffSize = 0; + } + } + + // if checkBuffSize changed in the if-statement above + if (!aecm->checkBuffSize) { + // soundcard buffer is now reasonably stable + // When the far end buffer is filled with approximately the same amount of + // data as the amount on the sound card we end the start up phase and + // start to cancel echoes. + + if (nmbrOfFilledBuffers == aecm->bufSizeStart) { + aecm->ECstartup = 0; // Enable the AECM + } else if (nmbrOfFilledBuffers > aecm->bufSizeStart) { + WebRtc_MoveReadPtr(aecm->farendBuf, + (int)WebRtc_available_read(aecm->farendBuf) - + (int)aecm->bufSizeStart * FRAME_LEN); + aecm->ECstartup = 0; + } + } + + } else { + // AECM is enabled + + // Note only 1 block supported for nb and 2 blocks for wb + for (i = 0; i < nFrames; i++) { + int16_t farend[FRAME_LEN]; + const int16_t* farend_ptr = NULL; + + nmbrOfFilledBuffers = + (short)WebRtc_available_read(aecm->farendBuf) / FRAME_LEN; + + // Check that there is data in the far end buffer + if (nmbrOfFilledBuffers > 0) { + // Get the next 80 samples from the farend buffer + WebRtc_ReadBuffer(aecm->farendBuf, (void**)&farend_ptr, farend, + FRAME_LEN); + + // Always store the last frame for use when we run out of data + memcpy(&(aecm->farendOld[i][0]), farend_ptr, FRAME_LEN * sizeof(short)); + } else { + // We have no data so we use the last played frame + memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short)); + farend_ptr = farend; + } + + // Call buffer delay estimator when all data is extracted, + // i,e. i = 0 for NB and i = 1 for WB + if ((i == 0 && aecm->sampFreq == 8000) || + (i == 1 && aecm->sampFreq == 16000)) { + WebRtcAecm_EstBufDelay(aecm, aecm->msInSndCardBuf); + } + + // Call the AECM + /*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i], + &out[FRAME_LEN * i], aecm->knownDelay);*/ + if (WebRtcAecm_ProcessFrame( + aecm->aecmCore, farend_ptr, &nearendNoisy[FRAME_LEN * i], + (nearendClean ? &nearendClean[FRAME_LEN * i] : NULL), + &out[FRAME_LEN * i]) == -1) + return -1; + } + } + +#ifdef AEC_DEBUG + msInAECBuf = (short)WebRtc_available_read(aecm->farendBuf) / + (kSampMsNb * aecm->aecmCore->mult); + fwrite(&msInAECBuf, 2, 1, aecm->bufFile); + fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile); +#endif + + return retVal; +} + +int32_t WebRtcAecm_set_config(void* aecmInst, AecmConfig config) { + AecMobile* aecm = static_cast(aecmInst); + + if (aecm == NULL) { + return -1; + } + + if (aecm->initFlag != kInitCheck) { + return AECM_UNINITIALIZED_ERROR; + } + + if (config.cngMode != AecmFalse && config.cngMode != AecmTrue) { + return AECM_BAD_PARAMETER_ERROR; + } + aecm->aecmCore->cngMode = config.cngMode; + + if (config.echoMode < 0 || config.echoMode > 4) { + return AECM_BAD_PARAMETER_ERROR; + } + aecm->echoMode = config.echoMode; + + if (aecm->echoMode == 0) { + aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3; + aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3; + aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3; + aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3; + aecm->aecmCore->supGainErrParamDiffAB = + (SUPGAIN_ERROR_PARAM_A >> 3) - (SUPGAIN_ERROR_PARAM_B >> 3); + aecm->aecmCore->supGainErrParamDiffBD = + (SUPGAIN_ERROR_PARAM_B >> 3) - (SUPGAIN_ERROR_PARAM_D >> 3); + } else if (aecm->echoMode == 1) { + aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2; + aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2; + aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2; + aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2; + aecm->aecmCore->supGainErrParamDiffAB = + (SUPGAIN_ERROR_PARAM_A >> 2) - (SUPGAIN_ERROR_PARAM_B >> 2); + aecm->aecmCore->supGainErrParamDiffBD = + (SUPGAIN_ERROR_PARAM_B >> 2) - (SUPGAIN_ERROR_PARAM_D >> 2); + } else if (aecm->echoMode == 2) { + aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1; + aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1; + aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1; + aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1; + aecm->aecmCore->supGainErrParamDiffAB = + (SUPGAIN_ERROR_PARAM_A >> 1) - (SUPGAIN_ERROR_PARAM_B >> 1); + aecm->aecmCore->supGainErrParamDiffBD = + (SUPGAIN_ERROR_PARAM_B >> 1) - (SUPGAIN_ERROR_PARAM_D >> 1); + } else if (aecm->echoMode == 3) { + aecm->aecmCore->supGain = SUPGAIN_DEFAULT; + aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT; + aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A; + aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D; + aecm->aecmCore->supGainErrParamDiffAB = + SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B; + aecm->aecmCore->supGainErrParamDiffBD = + SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D; + } else if (aecm->echoMode == 4) { + aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1; + aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1; + aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1; + aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1; + aecm->aecmCore->supGainErrParamDiffAB = + (SUPGAIN_ERROR_PARAM_A << 1) - (SUPGAIN_ERROR_PARAM_B << 1); + aecm->aecmCore->supGainErrParamDiffBD = + (SUPGAIN_ERROR_PARAM_B << 1) - (SUPGAIN_ERROR_PARAM_D << 1); + } + + return 0; +} + +int32_t WebRtcAecm_InitEchoPath(void* aecmInst, + const void* echo_path, + size_t size_bytes) { + AecMobile* aecm = static_cast(aecmInst); + const int16_t* echo_path_ptr = static_cast(echo_path); + + if (aecmInst == NULL) { + return -1; + } + if (echo_path == NULL) { + return AECM_NULL_POINTER_ERROR; + } + if (size_bytes != WebRtcAecm_echo_path_size_bytes()) { + // Input channel size does not match the size of AECM + return AECM_BAD_PARAMETER_ERROR; + } + if (aecm->initFlag != kInitCheck) { + return AECM_UNINITIALIZED_ERROR; + } + + WebRtcAecm_InitEchoPathCore(aecm->aecmCore, echo_path_ptr); + + return 0; +} + +int32_t WebRtcAecm_GetEchoPath(void* aecmInst, + void* echo_path, + size_t size_bytes) { + AecMobile* aecm = static_cast(aecmInst); + int16_t* echo_path_ptr = static_cast(echo_path); + + if (aecmInst == NULL) { + return -1; + } + if (echo_path == NULL) { + return AECM_NULL_POINTER_ERROR; + } + if (size_bytes != WebRtcAecm_echo_path_size_bytes()) { + // Input channel size does not match the size of AECM + return AECM_BAD_PARAMETER_ERROR; + } + if (aecm->initFlag != kInitCheck) { + return AECM_UNINITIALIZED_ERROR; + } + + memcpy(echo_path_ptr, aecm->aecmCore->channelStored, size_bytes); + return 0; +} + +size_t WebRtcAecm_echo_path_size_bytes() { + return (PART_LEN1 * sizeof(int16_t)); +} + +static int WebRtcAecm_EstBufDelay(AecMobile* aecm, short msInSndCardBuf) { + short delayNew, nSampSndCard; + short nSampFar = (short)WebRtc_available_read(aecm->farendBuf); + short diff; + + nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; + + delayNew = nSampSndCard - nSampFar; + + if (delayNew < FRAME_LEN) { + WebRtc_MoveReadPtr(aecm->farendBuf, FRAME_LEN); + delayNew += FRAME_LEN; + } + + aecm->filtDelay = + WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10); + + diff = aecm->filtDelay - aecm->knownDelay; + if (diff > 224) { + if (aecm->lastDelayDiff < 96) { + aecm->timeForDelayChange = 0; + } else { + aecm->timeForDelayChange++; + } + } else if (diff < 96 && aecm->knownDelay > 0) { + if (aecm->lastDelayDiff > 224) { + aecm->timeForDelayChange = 0; + } else { + aecm->timeForDelayChange++; + } + } else { + aecm->timeForDelayChange = 0; + } + aecm->lastDelayDiff = diff; + + if (aecm->timeForDelayChange > 25) { + aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0); + } + return 0; +} + +static int WebRtcAecm_DelayComp(AecMobile* aecm) { + int nSampFar = (int)WebRtc_available_read(aecm->farendBuf); + int nSampSndCard, delayNew, nSampAdd; + const int maxStuffSamp = 10 * FRAME_LEN; + + nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; + delayNew = nSampSndCard - nSampFar; + + if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult) { + // The difference of the buffer sizes is larger than the maximum + // allowed known delay. Compensate by stuffing the buffer. + nSampAdd = + (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar), FRAME_LEN)); + nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp); + + WebRtc_MoveReadPtr(aecm->farendBuf, -nSampAdd); + aecm->delayChange = 1; // the delay needs to be updated + } + + return 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/aecm/echo_control_mobile.h b/pkg/apm/webrtc/modules/audio_processing/aecm/echo_control_mobile.h new file mode 100644 index 00000000..ee780524 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/aecm/echo_control_mobile.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AECM_ECHO_CONTROL_MOBILE_H_ +#define MODULES_AUDIO_PROCESSING_AECM_ECHO_CONTROL_MOBILE_H_ + +#include +#include + +namespace webrtc { + +enum { AecmFalse = 0, AecmTrue }; + +// Errors +#define AECM_UNSPECIFIED_ERROR 12000 +#define AECM_UNSUPPORTED_FUNCTION_ERROR 12001 +#define AECM_UNINITIALIZED_ERROR 12002 +#define AECM_NULL_POINTER_ERROR 12003 +#define AECM_BAD_PARAMETER_ERROR 12004 + +// Warnings +#define AECM_BAD_PARAMETER_WARNING 12100 + +typedef struct { + int16_t cngMode; // AECM_FALSE, AECM_TRUE (default) + int16_t echoMode; // 0, 1, 2, 3 (default), 4 +} AecmConfig; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Allocates the memory needed by the AECM. The memory needs to be + * initialized separately using the WebRtcAecm_Init() function. + * Returns a pointer to the instance and a nullptr at failure. + */ +void* WebRtcAecm_Create(); + +/* + * This function releases the memory allocated by WebRtcAecm_Create() + * + * Inputs Description + * ------------------------------------------------------------------- + * void* aecmInst Pointer to the AECM instance + */ +void WebRtcAecm_Free(void* aecmInst); + +/* + * Initializes an AECM instance. + * + * Inputs Description + * ------------------------------------------------------------------- + * void* aecmInst Pointer to the AECM instance + * int32_t sampFreq Sampling frequency of data + * + * Outputs Description + * ------------------------------------------------------------------- + * int32_t return 0: OK + * 1200-12004,12100: error/warning + */ +int32_t WebRtcAecm_Init(void* aecmInst, int32_t sampFreq); + +/* + * Inserts an 80 or 160 sample block of data into the farend buffer. + * + * Inputs Description + * ------------------------------------------------------------------- + * void* aecmInst Pointer to the AECM instance + * int16_t* farend In buffer containing one frame of + * farend signal + * int16_t nrOfSamples Number of samples in farend buffer + * + * Outputs Description + * ------------------------------------------------------------------- + * int32_t return 0: OK + * 1200-12004,12100: error/warning + */ +int32_t WebRtcAecm_BufferFarend(void* aecmInst, + const int16_t* farend, + size_t nrOfSamples); + +/* + * Reports any errors that would arise when buffering a farend buffer. + * + * Inputs Description + * ------------------------------------------------------------------- + * void* aecmInst Pointer to the AECM instance + * int16_t* farend In buffer containing one frame of + * farend signal + * int16_t nrOfSamples Number of samples in farend buffer + * + * Outputs Description + * ------------------------------------------------------------------- + * int32_t return 0: OK + * 1200-12004,12100: error/warning + */ +int32_t WebRtcAecm_GetBufferFarendError(void* aecmInst, + const int16_t* farend, + size_t nrOfSamples); + +/* + * Runs the AECM on an 80 or 160 sample blocks of data. + * + * Inputs Description + * ------------------------------------------------------------------- + * void* aecmInst Pointer to the AECM instance + * int16_t* nearendNoisy In buffer containing one frame of + * reference nearend+echo signal. If + * noise reduction is active, provide + * the noisy signal here. + * int16_t* nearendClean In buffer containing one frame of + * nearend+echo signal. If noise + * reduction is active, provide the + * clean signal here. Otherwise pass a + * NULL pointer. + * int16_t nrOfSamples Number of samples in nearend buffer + * int16_t msInSndCardBuf Delay estimate for sound card and + * system buffers + * + * Outputs Description + * ------------------------------------------------------------------- + * int16_t* out Out buffer, one frame of processed nearend + * int32_t return 0: OK + * 1200-12004,12100: error/warning + */ +int32_t WebRtcAecm_Process(void* aecmInst, + const int16_t* nearendNoisy, + const int16_t* nearendClean, + int16_t* out, + size_t nrOfSamples, + int16_t msInSndCardBuf); + +/* + * This function enables the user to set certain parameters on-the-fly + * + * Inputs Description + * ------------------------------------------------------------------- + * void* aecmInst Pointer to the AECM instance + * AecmConfig config Config instance that contains all + * properties to be set + * + * Outputs Description + * ------------------------------------------------------------------- + * int32_t return 0: OK + * 1200-12004,12100: error/warning + */ +int32_t WebRtcAecm_set_config(void* aecmInst, AecmConfig config); + +/* + * This function enables the user to set the echo path on-the-fly. + * + * Inputs Description + * ------------------------------------------------------------------- + * void* aecmInst Pointer to the AECM instance + * void* echo_path Pointer to the echo path to be set + * size_t size_bytes Size in bytes of the echo path + * + * Outputs Description + * ------------------------------------------------------------------- + * int32_t return 0: OK + * 1200-12004,12100: error/warning + */ +int32_t WebRtcAecm_InitEchoPath(void* aecmInst, + const void* echo_path, + size_t size_bytes); + +/* + * This function enables the user to get the currently used echo path + * on-the-fly + * + * Inputs Description + * ------------------------------------------------------------------- + * void* aecmInst Pointer to the AECM instance + * void* echo_path Pointer to echo path + * size_t size_bytes Size in bytes of the echo path + * + * Outputs Description + * ------------------------------------------------------------------- + * int32_t return 0: OK + * 1200-12004,12100: error/warning + */ +int32_t WebRtcAecm_GetEchoPath(void* aecmInst, + void* echo_path, + size_t size_bytes); + +/* + * This function enables the user to get the echo path size in bytes + * + * Outputs Description + * ------------------------------------------------------------------- + * size_t return Size in bytes + */ +size_t WebRtcAecm_echo_path_size_bytes(); + +#ifdef __cplusplus +} +#endif + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AECM_ECHO_CONTROL_MOBILE_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/agc.cc b/pkg/apm/webrtc/modules/audio_processing/agc/agc.cc new file mode 100644 index 00000000..f172b03a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/agc.cc @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc/agc.h" + +#include +#include +#include + +#include "modules/audio_processing/agc/loudness_histogram.h" +#include "modules/audio_processing/agc/utility.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr int kDefaultLevelDbfs = -18; +constexpr int kNumAnalysisFrames = 100; +constexpr double kActivityThreshold = 0.3; +constexpr int kNum10msFramesInOneSecond = 100; +constexpr int kMaxSampleRateHz = 384000; + +} // namespace + +Agc::Agc() + : target_level_loudness_(Dbfs2Loudness(kDefaultLevelDbfs)), + target_level_dbfs_(kDefaultLevelDbfs), + histogram_(LoudnessHistogram::Create(kNumAnalysisFrames)), + inactive_histogram_(LoudnessHistogram::Create()) {} + +Agc::~Agc() = default; + +void Agc::Process(ArrayView audio) { + const int sample_rate_hz = audio.size() * kNum10msFramesInOneSecond; + RTC_DCHECK_LE(sample_rate_hz, kMaxSampleRateHz); + vad_.ProcessChunk(audio.data(), audio.size(), sample_rate_hz); + const std::vector& rms = vad_.chunkwise_rms(); + const std::vector& probabilities = + vad_.chunkwise_voice_probabilities(); + RTC_DCHECK_EQ(rms.size(), probabilities.size()); + for (size_t i = 0; i < rms.size(); ++i) { + histogram_->Update(rms[i], probabilities[i]); + } +} + +bool Agc::GetRmsErrorDb(int* error) { + if (!error) { + RTC_DCHECK_NOTREACHED(); + return false; + } + + if (histogram_->num_updates() < kNumAnalysisFrames) { + // We haven't yet received enough frames. + return false; + } + + if (histogram_->AudioContent() < kNumAnalysisFrames * kActivityThreshold) { + // We are likely in an inactive segment. + return false; + } + + double loudness = Linear2Loudness(histogram_->CurrentRms()); + *error = std::floor(Loudness2Db(target_level_loudness_ - loudness) + 0.5); + histogram_->Reset(); + return true; +} + +void Agc::Reset() { + histogram_->Reset(); +} + +int Agc::set_target_level_dbfs(int level) { + // TODO(turajs): just some arbitrary sanity check. We can come up with better + // limits. The upper limit should be chosen such that the risk of clipping is + // low. The lower limit should not result in a too quiet signal. + if (level >= 0 || level <= -100) + return -1; + target_level_dbfs_ = level; + target_level_loudness_ = Dbfs2Loudness(level); + return 0; +} + +int Agc::target_level_dbfs() const { + return target_level_dbfs_; +} + +float Agc::voice_probability() const { + return vad_.last_voice_probability(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/agc.go b/pkg/apm/webrtc/modules/audio_processing/agc/agc.go new file mode 100644 index 00000000..40002767 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/agc.go @@ -0,0 +1,14 @@ +//go:build console + +package agc + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" + +import ( + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/agc/legacy" +) diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/agc.h b/pkg/apm/webrtc/modules/audio_processing/agc/agc.h new file mode 100644 index 00000000..65afbf07 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/agc.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC_AGC_H_ +#define MODULES_AUDIO_PROCESSING_AGC_AGC_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/vad/voice_activity_detector.h" + +namespace webrtc { + +class LoudnessHistogram; + +class Agc { + public: + Agc(); + virtual ~Agc(); + + // `audio` must be mono; in a multi-channel stream, provide the first (usually + // left) channel. + virtual void Process(ArrayView audio); + + // Retrieves the difference between the target RMS level and the current + // signal RMS level in dB. Returns true if an update is available and false + // otherwise, in which case `error` should be ignored and no action taken. + virtual bool GetRmsErrorDb(int* error); + virtual void Reset(); + + virtual int set_target_level_dbfs(int level); + virtual int target_level_dbfs() const; + virtual float voice_probability() const; + + private: + double target_level_loudness_; + int target_level_dbfs_; + std::unique_ptr histogram_; + std::unique_ptr inactive_histogram_; + VoiceActivityDetector vad_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_AGC_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/agc_manager_direct.cc b/pkg/apm/webrtc/modules/audio_processing/agc/agc_manager_direct.cc new file mode 100644 index 00000000..5fc12c04 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/agc_manager_direct.cc @@ -0,0 +1,715 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc/agc_manager_direct.h" + +#include +#include + +#include "api/array_view.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/agc/gain_control.h" +#include "modules/audio_processing/agc2/gain_map_internal.h" +#include "modules/audio_processing/agc2/input_volume_stats_reporter.h" +#include "modules/audio_processing/include/audio_frame_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +namespace { + +// Amount of error we tolerate in the microphone level (presumably due to OS +// quantization) before we assume the user has manually adjusted the microphone. +constexpr int kLevelQuantizationSlack = 25; + +constexpr int kDefaultCompressionGain = 7; +constexpr int kMaxCompressionGain = 12; +constexpr int kMinCompressionGain = 2; +// Controls the rate of compression changes towards the target. +constexpr float kCompressionGainStep = 0.05f; + +constexpr int kMaxMicLevel = 255; +static_assert(kGainMapSize > kMaxMicLevel, "gain map too small"); +constexpr int kMinMicLevel = 12; + +// Prevent very large microphone level changes. +constexpr int kMaxResidualGainChange = 15; + +// Maximum additional gain allowed to compensate for microphone level +// restrictions from clipping events. +constexpr int kSurplusCompressionGain = 6; + +// Target speech level (dBFs) and speech probability threshold used to compute +// the RMS error override in `GetSpeechLevelErrorDb()`. These are only used for +// computing the error override and they are not passed to `agc_`. +// TODO(webrtc:7494): Move these to a config and pass in the ctor. +constexpr float kOverrideTargetSpeechLevelDbfs = -18.0f; +constexpr float kOverrideSpeechProbabilitySilenceThreshold = 0.5f; +// The minimum number of frames between `UpdateGain()` calls. +// TODO(webrtc:7494): Move this to a config and pass in the ctor with +// kOverrideWaitFrames = 100. Default value zero needed for the unit tests. +constexpr int kOverrideWaitFrames = 0; + +using AnalogAgcConfig = + AudioProcessing::Config::GainController1::AnalogGainController; + +// If the "WebRTC-Audio-2ndAgcMinMicLevelExperiment" field trial is specified, +// parses it and returns a value between 0 and 255 depending on the field-trial +// string. Returns an unspecified value if the field trial is not specified, if +// disabled or if it cannot be parsed. Example: +// 'WebRTC-Audio-2ndAgcMinMicLevelExperiment/Enabled-80' => returns 80. +std::optional GetMinMicLevelOverride(const FieldTrialsView& field_trials) { + constexpr char kMinMicLevelFieldTrial[] = + "WebRTC-Audio-2ndAgcMinMicLevelExperiment"; + if (!field_trials.IsEnabled(kMinMicLevelFieldTrial)) { + return std::nullopt; + } + const auto field_trial_string = field_trials.Lookup(kMinMicLevelFieldTrial); + int min_mic_level = -1; + sscanf(field_trial_string.c_str(), "Enabled-%d", &min_mic_level); + if (min_mic_level >= 0 && min_mic_level <= 255) { + return min_mic_level; + } else { + RTC_LOG(LS_WARNING) << "[agc] Invalid parameter for " + << kMinMicLevelFieldTrial << ", ignored."; + return std::nullopt; + } +} + +int LevelFromGainError(int gain_error, int level, int min_mic_level) { + RTC_DCHECK_GE(level, 0); + RTC_DCHECK_LE(level, kMaxMicLevel); + if (gain_error == 0) { + return level; + } + + int new_level = level; + if (gain_error > 0) { + while (kGainMap[new_level] - kGainMap[level] < gain_error && + new_level < kMaxMicLevel) { + ++new_level; + } + } else { + while (kGainMap[new_level] - kGainMap[level] > gain_error && + new_level > min_mic_level) { + --new_level; + } + } + return new_level; +} + +// Returns the proportion of samples in the buffer which are at full-scale +// (and presumably clipped). +float ComputeClippedRatio(const float* const* audio, + size_t num_channels, + size_t samples_per_channel) { + RTC_DCHECK_GT(samples_per_channel, 0); + int num_clipped = 0; + for (size_t ch = 0; ch < num_channels; ++ch) { + int num_clipped_in_ch = 0; + for (size_t i = 0; i < samples_per_channel; ++i) { + RTC_DCHECK(audio[ch]); + if (audio[ch][i] >= 32767.0f || audio[ch][i] <= -32768.0f) { + ++num_clipped_in_ch; + } + } + num_clipped = std::max(num_clipped, num_clipped_in_ch); + } + return static_cast(num_clipped) / (samples_per_channel); +} + +void LogClippingMetrics(int clipping_rate) { + RTC_LOG(LS_INFO) << "Input clipping rate: " << clipping_rate << "%"; + RTC_HISTOGRAM_COUNTS_LINEAR(/*name=*/"WebRTC.Audio.Agc.InputClippingRate", + /*sample=*/clipping_rate, /*min=*/0, /*max=*/100, + /*bucket_count=*/50); +} + +// Computes the speech level error in dB. `speech_level_dbfs` is required to be +// in the range [-90.0f, 30.0f] and `speech_probability` in the range +// [0.0f, 1.0f]. +int GetSpeechLevelErrorDb(float speech_level_dbfs, float speech_probability) { + constexpr float kMinSpeechLevelDbfs = -90.0f; + constexpr float kMaxSpeechLevelDbfs = 30.0f; + RTC_DCHECK_GE(speech_level_dbfs, kMinSpeechLevelDbfs); + RTC_DCHECK_LE(speech_level_dbfs, kMaxSpeechLevelDbfs); + RTC_DCHECK_GE(speech_probability, 0.0f); + RTC_DCHECK_LE(speech_probability, 1.0f); + + if (speech_probability < kOverrideSpeechProbabilitySilenceThreshold) { + return 0; + } + + const float speech_level = SafeClamp( + speech_level_dbfs, kMinSpeechLevelDbfs, kMaxSpeechLevelDbfs); + + return std::round(kOverrideTargetSpeechLevelDbfs - speech_level); +} + +} // namespace + +MonoAgc::MonoAgc(ApmDataDumper* /* data_dumper */, + int clipped_level_min, + bool disable_digital_adaptive, + int min_mic_level) + : min_mic_level_(min_mic_level), + disable_digital_adaptive_(disable_digital_adaptive), + agc_(std::make_unique()), + max_level_(kMaxMicLevel), + max_compression_gain_(kMaxCompressionGain), + target_compression_(kDefaultCompressionGain), + compression_(target_compression_), + compression_accumulator_(compression_), + clipped_level_min_(clipped_level_min) {} + +MonoAgc::~MonoAgc() = default; + +void MonoAgc::Initialize() { + max_level_ = kMaxMicLevel; + max_compression_gain_ = kMaxCompressionGain; + target_compression_ = disable_digital_adaptive_ ? 0 : kDefaultCompressionGain; + compression_ = disable_digital_adaptive_ ? 0 : target_compression_; + compression_accumulator_ = compression_; + capture_output_used_ = true; + check_volume_on_next_process_ = true; + frames_since_update_gain_ = 0; + is_first_frame_ = true; +} + +void MonoAgc::Process(ArrayView audio, + std::optional rms_error_override) { + new_compression_to_set_ = std::nullopt; + + if (check_volume_on_next_process_) { + check_volume_on_next_process_ = false; + // We have to wait until the first process call to check the volume, + // because Chromium doesn't guarantee it to be valid any earlier. + CheckVolumeAndReset(); + } + + agc_->Process(audio); + + // Always check if `agc_` has a new error available. If yes, `agc_` gets + // reset. + // TODO(webrtc:7494) Replace the `agc_` call `GetRmsErrorDb()` with `Reset()` + // if an error override is used. + int rms_error = 0; + bool update_gain = agc_->GetRmsErrorDb(&rms_error); + if (rms_error_override.has_value()) { + if (is_first_frame_ || frames_since_update_gain_ < kOverrideWaitFrames) { + update_gain = false; + } else { + rms_error = *rms_error_override; + update_gain = true; + } + } + + if (update_gain) { + UpdateGain(rms_error); + } + + if (!disable_digital_adaptive_) { + UpdateCompressor(); + } + + is_first_frame_ = false; + if (frames_since_update_gain_ < kOverrideWaitFrames) { + ++frames_since_update_gain_; + } +} + +void MonoAgc::HandleClipping(int clipped_level_step) { + RTC_DCHECK_GT(clipped_level_step, 0); + // Always decrease the maximum level, even if the current level is below + // threshold. + SetMaxLevel(std::max(clipped_level_min_, max_level_ - clipped_level_step)); + if (log_to_histograms_) { + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed", + level_ - clipped_level_step >= clipped_level_min_); + } + if (level_ > clipped_level_min_) { + // Don't try to adjust the level if we're already below the limit. As + // a consequence, if the user has brought the level above the limit, we + // will still not react until the postproc updates the level. + SetLevel(std::max(clipped_level_min_, level_ - clipped_level_step)); + // Reset the AGCs for all channels since the level has changed. + agc_->Reset(); + frames_since_update_gain_ = 0; + is_first_frame_ = false; + } +} + +void MonoAgc::SetLevel(int new_level) { + int voe_level = recommended_input_volume_; + if (voe_level == 0) { + RTC_DLOG(LS_INFO) + << "[agc] VolumeCallbacks returned level=0, taking no action."; + return; + } + if (voe_level < 0 || voe_level > kMaxMicLevel) { + RTC_LOG(LS_ERROR) << "VolumeCallbacks returned an invalid level=" + << voe_level; + return; + } + + // Detect manual input volume adjustments by checking if the current level + // `voe_level` is outside of the `[level_ - kLevelQuantizationSlack, level_ + + // kLevelQuantizationSlack]` range where `level_` is the last input volume + // known by this gain controller. + if (voe_level > level_ + kLevelQuantizationSlack || + voe_level < level_ - kLevelQuantizationSlack) { + RTC_DLOG(LS_INFO) << "[agc] Mic volume was manually adjusted. Updating " + "stored level from " + << level_ << " to " << voe_level; + level_ = voe_level; + // Always allow the user to increase the volume. + if (level_ > max_level_) { + SetMaxLevel(level_); + } + // Take no action in this case, since we can't be sure when the volume + // was manually adjusted. The compressor will still provide some of the + // desired gain change. + agc_->Reset(); + frames_since_update_gain_ = 0; + is_first_frame_ = false; + return; + } + + new_level = std::min(new_level, max_level_); + if (new_level == level_) { + return; + } + + recommended_input_volume_ = new_level; + RTC_DLOG(LS_INFO) << "[agc] voe_level=" << voe_level << ", level_=" << level_ + << ", new_level=" << new_level; + level_ = new_level; +} + +void MonoAgc::SetMaxLevel(int level) { + RTC_DCHECK_GE(level, clipped_level_min_); + max_level_ = level; + // Scale the `kSurplusCompressionGain` linearly across the restricted + // level range. + max_compression_gain_ = + kMaxCompressionGain + std::floor((1.f * kMaxMicLevel - max_level_) / + (kMaxMicLevel - clipped_level_min_) * + kSurplusCompressionGain + + 0.5f); + RTC_DLOG(LS_INFO) << "[agc] max_level_=" << max_level_ + << ", max_compression_gain_=" << max_compression_gain_; +} + +void MonoAgc::HandleCaptureOutputUsedChange(bool capture_output_used) { + if (capture_output_used_ == capture_output_used) { + return; + } + capture_output_used_ = capture_output_used; + + if (capture_output_used) { + // When we start using the output, we should reset things to be safe. + check_volume_on_next_process_ = true; + } +} + +int MonoAgc::CheckVolumeAndReset() { + int level = recommended_input_volume_; + // Reasons for taking action at startup: + // 1) A person starting a call is expected to be heard. + // 2) Independent of interpretation of `level` == 0 we should raise it so the + // AGC can do its job properly. + if (level == 0 && !startup_) { + RTC_DLOG(LS_INFO) + << "[agc] VolumeCallbacks returned level=0, taking no action."; + return 0; + } + if (level < 0 || level > kMaxMicLevel) { + RTC_LOG(LS_ERROR) << "[agc] VolumeCallbacks returned an invalid level=" + << level; + return -1; + } + RTC_DLOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level; + + if (level < min_mic_level_) { + level = min_mic_level_; + RTC_DLOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level; + recommended_input_volume_ = level; + } + agc_->Reset(); + level_ = level; + startup_ = false; + frames_since_update_gain_ = 0; + is_first_frame_ = true; + return 0; +} + +// Distributes the required gain change between the digital compression stage +// and volume slider. We use the compressor first, providing a slack region +// around the current slider position to reduce movement. +// +// If the slider needs to be moved, we check first if the user has adjusted +// it, in which case we take no action and cache the updated level. +void MonoAgc::UpdateGain(int rms_error_db) { + int rms_error = rms_error_db; + + // Always reset the counter regardless of whether the gain is changed + // or not. This matches with the bahvior of `agc_` where the histogram is + // reset every time an RMS error is successfully read. + frames_since_update_gain_ = 0; + + // The compressor will always add at least kMinCompressionGain. In effect, + // this adjusts our target gain upward by the same amount and rms_error + // needs to reflect that. + rms_error += kMinCompressionGain; + + // Handle as much error as possible with the compressor first. + int raw_compression = + SafeClamp(rms_error, kMinCompressionGain, max_compression_gain_); + + // Deemphasize the compression gain error. Move halfway between the current + // target and the newly received target. This serves to soften perceptible + // intra-talkspurt adjustments, at the cost of some adaptation speed. + if ((raw_compression == max_compression_gain_ && + target_compression_ == max_compression_gain_ - 1) || + (raw_compression == kMinCompressionGain && + target_compression_ == kMinCompressionGain + 1)) { + // Special case to allow the target to reach the endpoints of the + // compression range. The deemphasis would otherwise halt it at 1 dB shy. + target_compression_ = raw_compression; + } else { + target_compression_ = + (raw_compression - target_compression_) / 2 + target_compression_; + } + + // Residual error will be handled by adjusting the volume slider. Use the + // raw rather than deemphasized compression here as we would otherwise + // shrink the amount of slack the compressor provides. + const int residual_gain = + SafeClamp(rms_error - raw_compression, -kMaxResidualGainChange, + kMaxResidualGainChange); + RTC_DLOG(LS_INFO) << "[agc] rms_error=" << rms_error + << ", target_compression=" << target_compression_ + << ", residual_gain=" << residual_gain; + if (residual_gain == 0) + return; + + int old_level = level_; + SetLevel(LevelFromGainError(residual_gain, level_, min_mic_level_)); + if (old_level != level_) { + // Reset the AGC since the level has changed. + agc_->Reset(); + } +} + +void MonoAgc::UpdateCompressor() { + if (compression_ == target_compression_) { + return; + } + + // Adapt the compression gain slowly towards the target, in order to avoid + // highly perceptible changes. + if (target_compression_ > compression_) { + compression_accumulator_ += kCompressionGainStep; + } else { + compression_accumulator_ -= kCompressionGainStep; + } + + // The compressor accepts integer gains in dB. Adjust the gain when + // we've come within half a stepsize of the nearest integer. (We don't + // check for equality due to potential floating point imprecision). + int new_compression = compression_; + int nearest_neighbor = std::floor(compression_accumulator_ + 0.5); + if (std::fabs(compression_accumulator_ - nearest_neighbor) < + kCompressionGainStep / 2) { + new_compression = nearest_neighbor; + } + + // Set the new compression gain. + if (new_compression != compression_) { + compression_ = new_compression; + compression_accumulator_ = new_compression; + new_compression_to_set_ = compression_; + } +} + +std::atomic AgcManagerDirect::instance_counter_(0); + +AgcManagerDirect::AgcManagerDirect( + const Environment& env, + const AudioProcessing::Config::GainController1::AnalogGainController& + analog_config, + Agc* agc) + : AgcManagerDirect(env, /*num_capture_channels=*/1, analog_config) { + RTC_DCHECK(channel_agcs_[0]); + RTC_DCHECK(agc); + channel_agcs_[0]->set_agc(agc); +} + +AgcManagerDirect::AgcManagerDirect(const Environment& env, + int num_capture_channels, + const AnalogAgcConfig& analog_config) + : analog_controller_enabled_(analog_config.enabled), + min_mic_level_override_(GetMinMicLevelOverride(env.field_trials())), + data_dumper_(new ApmDataDumper(instance_counter_.fetch_add(1) + 1)), + num_capture_channels_(num_capture_channels), + disable_digital_adaptive_(!analog_config.enable_digital_adaptive), + frames_since_clipped_(analog_config.clipped_wait_frames), + capture_output_used_(true), + clipped_level_step_(analog_config.clipped_level_step), + clipped_ratio_threshold_(analog_config.clipped_ratio_threshold), + clipped_wait_frames_(analog_config.clipped_wait_frames), + channel_agcs_(num_capture_channels), + new_compressions_to_set_(num_capture_channels), + clipping_predictor_( + CreateClippingPredictor(num_capture_channels, + analog_config.clipping_predictor)), + use_clipping_predictor_step_( + !!clipping_predictor_ && + analog_config.clipping_predictor.use_predicted_step), + clipping_rate_log_(0.0f), + clipping_rate_log_counter_(0) { + RTC_LOG(LS_INFO) << "[agc] analog controller enabled: " + << (analog_controller_enabled_ ? "yes" : "no"); + const int min_mic_level = min_mic_level_override_.value_or(kMinMicLevel); + RTC_LOG(LS_INFO) << "[agc] Min mic level: " << min_mic_level + << " (overridden: " + << (min_mic_level_override_.has_value() ? "yes" : "no") + << ")"; + for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { + ApmDataDumper* data_dumper_ch = ch == 0 ? data_dumper_.get() : nullptr; + + channel_agcs_[ch] = std::make_unique( + data_dumper_ch, analog_config.clipped_level_min, + disable_digital_adaptive_, min_mic_level); + } + RTC_DCHECK(!channel_agcs_.empty()); + RTC_DCHECK_GT(clipped_level_step_, 0); + RTC_DCHECK_LE(clipped_level_step_, 255); + RTC_DCHECK_GT(clipped_ratio_threshold_, 0.0f); + RTC_DCHECK_LT(clipped_ratio_threshold_, 1.0f); + RTC_DCHECK_GT(clipped_wait_frames_, 0); + channel_agcs_[0]->ActivateLogging(); +} + +AgcManagerDirect::~AgcManagerDirect() {} + +void AgcManagerDirect::Initialize() { + RTC_DLOG(LS_INFO) << "AgcManagerDirect::Initialize"; + data_dumper_->InitiateNewSetOfRecordings(); + for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { + channel_agcs_[ch]->Initialize(); + } + capture_output_used_ = true; + + AggregateChannelLevels(); + clipping_rate_log_ = 0.0f; + clipping_rate_log_counter_ = 0; +} + +void AgcManagerDirect::SetupDigitalGainControl( + GainControl& gain_control) const { + if (gain_control.set_mode(GainControl::kFixedDigital) != 0) { + RTC_LOG(LS_ERROR) << "set_mode(GainControl::kFixedDigital) failed."; + } + const int target_level_dbfs = disable_digital_adaptive_ ? 0 : 2; + if (gain_control.set_target_level_dbfs(target_level_dbfs) != 0) { + RTC_LOG(LS_ERROR) << "set_target_level_dbfs() failed."; + } + const int compression_gain_db = + disable_digital_adaptive_ ? 0 : kDefaultCompressionGain; + if (gain_control.set_compression_gain_db(compression_gain_db) != 0) { + RTC_LOG(LS_ERROR) << "set_compression_gain_db() failed."; + } + const bool enable_limiter = !disable_digital_adaptive_; + if (gain_control.enable_limiter(enable_limiter) != 0) { + RTC_LOG(LS_ERROR) << "enable_limiter() failed."; + } +} + +void AgcManagerDirect::AnalyzePreProcess(const AudioBuffer& audio_buffer) { + const float* const* audio = audio_buffer.channels_const(); + size_t samples_per_channel = audio_buffer.num_frames(); + RTC_DCHECK(audio); + + AggregateChannelLevels(); + if (!capture_output_used_) { + return; + } + + if (!!clipping_predictor_) { + AudioFrameView frame = AudioFrameView( + audio, num_capture_channels_, static_cast(samples_per_channel)); + clipping_predictor_->Analyze(frame); + } + + // Check for clipped samples, as the AGC has difficulty detecting pitch + // under clipping distortion. We do this in the preprocessing phase in order + // to catch clipped echo as well. + // + // If we find a sufficiently clipped frame, drop the current microphone level + // and enforce a new maximum level, dropped the same amount from the current + // maximum. This harsh treatment is an effort to avoid repeated clipped echo + // events. As compensation for this restriction, the maximum compression + // gain is increased, through SetMaxLevel(). + float clipped_ratio = + ComputeClippedRatio(audio, num_capture_channels_, samples_per_channel); + clipping_rate_log_ = std::max(clipped_ratio, clipping_rate_log_); + clipping_rate_log_counter_++; + constexpr int kNumFramesIn30Seconds = 3000; + if (clipping_rate_log_counter_ == kNumFramesIn30Seconds) { + LogClippingMetrics(std::round(100.0f * clipping_rate_log_)); + clipping_rate_log_ = 0.0f; + clipping_rate_log_counter_ = 0; + } + + if (frames_since_clipped_ < clipped_wait_frames_) { + ++frames_since_clipped_; + return; + } + + const bool clipping_detected = clipped_ratio > clipped_ratio_threshold_; + bool clipping_predicted = false; + int predicted_step = 0; + if (!!clipping_predictor_) { + for (int channel = 0; channel < num_capture_channels_; ++channel) { + const auto step = clipping_predictor_->EstimateClippedLevelStep( + channel, recommended_input_volume_, clipped_level_step_, + channel_agcs_[channel]->min_mic_level(), kMaxMicLevel); + if (step.has_value()) { + predicted_step = std::max(predicted_step, step.value()); + clipping_predicted = true; + } + } + } + if (clipping_detected) { + RTC_DLOG(LS_INFO) << "[agc] Clipping detected. clipped_ratio=" + << clipped_ratio; + } + int step = clipped_level_step_; + if (clipping_predicted) { + predicted_step = std::max(predicted_step, clipped_level_step_); + RTC_DLOG(LS_INFO) << "[agc] Clipping predicted. step=" << predicted_step; + if (use_clipping_predictor_step_) { + step = predicted_step; + } + } + if (clipping_detected || + (clipping_predicted && use_clipping_predictor_step_)) { + for (auto& state_ch : channel_agcs_) { + state_ch->HandleClipping(step); + } + frames_since_clipped_ = 0; + if (!!clipping_predictor_) { + clipping_predictor_->Reset(); + } + } + AggregateChannelLevels(); +} + +void AgcManagerDirect::Process(const AudioBuffer& audio_buffer) { + Process(audio_buffer, /*speech_probability=*/std::nullopt, + /*speech_level_dbfs=*/std::nullopt); +} + +void AgcManagerDirect::Process(const AudioBuffer& audio_buffer, + std::optional speech_probability, + std::optional speech_level_dbfs) { + AggregateChannelLevels(); + const int volume_after_clipping_handling = recommended_input_volume_; + + if (!capture_output_used_) { + return; + } + + const size_t num_frames_per_band = audio_buffer.num_frames_per_band(); + std::optional rms_error_override = std::nullopt; + if (speech_probability.has_value() && speech_level_dbfs.has_value()) { + rms_error_override = + GetSpeechLevelErrorDb(*speech_level_dbfs, *speech_probability); + } + for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { + std::array audio_data; + int16_t* audio_use = audio_data.data(); + FloatS16ToS16(audio_buffer.split_bands_const_f(ch)[0], num_frames_per_band, + audio_use); + channel_agcs_[ch]->Process({audio_use, num_frames_per_band}, + rms_error_override); + new_compressions_to_set_[ch] = channel_agcs_[ch]->new_compression(); + } + + AggregateChannelLevels(); + if (volume_after_clipping_handling != recommended_input_volume_) { + // The recommended input volume was adjusted in order to match the target + // level. + UpdateHistogramOnRecommendedInputVolumeChangeToMatchTarget( + recommended_input_volume_); + } +} + +std::optional AgcManagerDirect::GetDigitalComressionGain() { + return new_compressions_to_set_[channel_controlling_gain_]; +} + +void AgcManagerDirect::HandleCaptureOutputUsedChange(bool capture_output_used) { + for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { + channel_agcs_[ch]->HandleCaptureOutputUsedChange(capture_output_used); + } + capture_output_used_ = capture_output_used; +} + +float AgcManagerDirect::voice_probability() const { + float max_prob = 0.f; + for (const auto& state_ch : channel_agcs_) { + max_prob = std::max(max_prob, state_ch->voice_probability()); + } + + return max_prob; +} + +void AgcManagerDirect::set_stream_analog_level(int level) { + if (!analog_controller_enabled_) { + recommended_input_volume_ = level; + } + + for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { + channel_agcs_[ch]->set_stream_analog_level(level); + } + + AggregateChannelLevels(); +} + +void AgcManagerDirect::AggregateChannelLevels() { + int new_recommended_input_volume = + channel_agcs_[0]->recommended_analog_level(); + channel_controlling_gain_ = 0; + for (size_t ch = 1; ch < channel_agcs_.size(); ++ch) { + int level = channel_agcs_[ch]->recommended_analog_level(); + if (level < new_recommended_input_volume) { + new_recommended_input_volume = level; + channel_controlling_gain_ = static_cast(ch); + } + } + + if (min_mic_level_override_.has_value() && new_recommended_input_volume > 0) { + new_recommended_input_volume = + std::max(new_recommended_input_volume, *min_mic_level_override_); + } + + if (analog_controller_enabled_) { + recommended_input_volume_ = new_recommended_input_volume; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/agc_manager_direct.h b/pkg/apm/webrtc/modules/audio_processing/agc/agc_manager_direct.h new file mode 100644 index 00000000..c3100e5f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/agc_manager_direct.h @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC_AGC_MANAGER_DIRECT_H_ +#define MODULES_AUDIO_PROCESSING_AGC_AGC_MANAGER_DIRECT_H_ + +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/audio_processing.h" +#include "api/environment/environment.h" +#include "modules/audio_processing/agc/agc.h" +#include "modules/audio_processing/agc2/clipping_predictor.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/gtest_prod_util.h" + +namespace webrtc { + +class MonoAgc; +class GainControl; + +// Adaptive Gain Controller (AGC) that controls the input volume and a digital +// gain. The input volume controller recommends what volume to use, handles +// volume changes and clipping. In particular, it handles changes triggered by +// the user (e.g., volume set to zero by a HW mute button). The digital +// controller chooses and applies the digital compression gain. +// This class is not thread-safe. +// TODO(bugs.webrtc.org/7494): Use applied/recommended input volume naming +// convention. +class AgcManagerDirect final { + public: + // Ctor. `num_capture_channels` specifies the number of channels for the audio + // passed to `AnalyzePreProcess()` and `Process()`. Clamps + // `analog_config.startup_min_level` in the [12, 255] range. + AgcManagerDirect( + const Environment& env, + int num_capture_channels, + const AudioProcessing::Config::GainController1::AnalogGainController& + analog_config); + + ~AgcManagerDirect(); + AgcManagerDirect(const AgcManagerDirect&) = delete; + AgcManagerDirect& operator=(const AgcManagerDirect&) = delete; + + void Initialize(); + + // Configures `gain_control` to work as a fixed digital controller so that the + // adaptive part is only handled by this gain controller. Must be called if + // `gain_control` is also used to avoid the side-effects of running two AGCs. + void SetupDigitalGainControl(GainControl& gain_control) const; + + // Sets the applied input volume. + void set_stream_analog_level(int level); + + // TODO(bugs.webrtc.org/7494): Add argument for the applied input volume and + // remove `set_stream_analog_level()`. + // Analyzes `audio` before `Process()` is called so that the analysis can be + // performed before external digital processing operations take place (e.g., + // echo cancellation). The analysis consists of input clipping detection and + // prediction (if enabled). Must be called after `set_stream_analog_level()`. + void AnalyzePreProcess(const AudioBuffer& audio_buffer); + + // Processes `audio_buffer`. Chooses a digital compression gain and the new + // input volume to recommend. Must be called after `AnalyzePreProcess()`. If + // `speech_probability` (range [0.0f, 1.0f]) and `speech_level_dbfs` (range + // [-90.f, 30.0f]) are given, uses them to override the estimated RMS error. + // TODO(webrtc:7494): This signature is needed for testing purposes, unify + // the signatures when the clean-up is done. + void Process(const AudioBuffer& audio_buffer, + std::optional speech_probability, + std::optional speech_level_dbfs); + + // Processes `audio_buffer`. Chooses a digital compression gain and the new + // input volume to recommend. Must be called after `AnalyzePreProcess()`. + void Process(const AudioBuffer& audio_buffer); + + // TODO(bugs.webrtc.org/7494): Return recommended input volume and remove + // `recommended_analog_level()`. + // Returns the recommended input volume. If the input volume contoller is + // disabled, returns the input volume set via the latest + // `set_stream_analog_level()` call. Must be called after + // `AnalyzePreProcess()` and `Process()`. + int recommended_analog_level() const { return recommended_input_volume_; } + + // Call when the capture stream output has been flagged to be used/not-used. + // If unused, the manager disregards all incoming audio. + void HandleCaptureOutputUsedChange(bool capture_output_used); + + float voice_probability() const; + + int num_channels() const { return num_capture_channels_; } + + // If available, returns the latest digital compression gain that has been + // chosen. + std::optional GetDigitalComressionGain(); + + // Returns true if clipping prediction is enabled. + bool clipping_predictor_enabled() const { return !!clipping_predictor_; } + + // Returns true if clipping prediction is used to adjust the input volume. + bool use_clipping_predictor_step() const { + return use_clipping_predictor_step_; + } + + private: + friend class AgcManagerDirectTestHelper; + + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectTest, DisableDigitalDisablesDigital); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectTest, + AgcMinMicLevelExperimentDefault); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectTest, + AgcMinMicLevelExperimentDisabled); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectTest, + AgcMinMicLevelExperimentOutOfRangeAbove); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectTest, + AgcMinMicLevelExperimentOutOfRangeBelow); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectTest, + AgcMinMicLevelExperimentEnabled50); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectTest, + AgcMinMicLevelExperimentEnabledAboveStartupLevel); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectParametrizedTest, + ClippingParametersVerified); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectParametrizedTest, + DisableClippingPredictorDoesNotLowerVolume); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectParametrizedTest, + UsedClippingPredictionsProduceLowerAnalogLevels); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectParametrizedTest, + UnusedClippingPredictionsProduceEqualAnalogLevels); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectParametrizedTest, + EmptyRmsErrorOverrideHasNoEffect); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectParametrizedTest, + NonEmptyRmsErrorOverrideHasEffect); + + // Ctor that creates a single channel AGC and by injecting `agc`. + // `agc` will be owned by this class; hence, do not delete it. + AgcManagerDirect( + const Environment& env, + const AudioProcessing::Config::GainController1::AnalogGainController& + analog_config, + Agc* agc); + + void AggregateChannelLevels(); + + const bool analog_controller_enabled_; + + const std::optional min_mic_level_override_; + std::unique_ptr data_dumper_; + static std::atomic instance_counter_; + const int num_capture_channels_; + const bool disable_digital_adaptive_; + + int frames_since_clipped_; + + // TODO(bugs.webrtc.org/7494): Create a separate member for the applied input + // volume. + // TODO(bugs.webrtc.org/7494): Once + // `AudioProcessingImpl::recommended_stream_analog_level()` becomes a trivial + // getter, leave uninitialized. + // Recommended input volume. After `set_stream_analog_level()` is called it + // holds the observed input volume. Possibly updated by `AnalyzePreProcess()` + // and `Process()`; after these calls, holds the recommended input volume. + int recommended_input_volume_ = 0; + + bool capture_output_used_; + int channel_controlling_gain_ = 0; + + const int clipped_level_step_; + const float clipped_ratio_threshold_; + const int clipped_wait_frames_; + + std::vector> channel_agcs_; + std::vector> new_compressions_to_set_; + + const std::unique_ptr clipping_predictor_; + const bool use_clipping_predictor_step_; + float clipping_rate_log_; + int clipping_rate_log_counter_; +}; + +// TODO(bugs.webrtc.org/7494): Use applied/recommended input volume naming +// convention. +class MonoAgc { + public: + MonoAgc(ApmDataDumper* data_dumper, + int clipped_level_min, + bool disable_digital_adaptive, + int min_mic_level); + ~MonoAgc(); + MonoAgc(const MonoAgc&) = delete; + MonoAgc& operator=(const MonoAgc&) = delete; + + void Initialize(); + void HandleCaptureOutputUsedChange(bool capture_output_used); + + // Sets the current input volume. + void set_stream_analog_level(int level) { recommended_input_volume_ = level; } + + // Lowers the recommended input volume in response to clipping based on the + // suggested reduction `clipped_level_step`. Must be called after + // `set_stream_analog_level()`. + void HandleClipping(int clipped_level_step); + + // Analyzes `audio`, requests the RMS error from AGC, updates the recommended + // input volume based on the estimated speech level and, if enabled, updates + // the (digital) compression gain to be applied by `agc_`. Must be called + // after `HandleClipping()`. If `rms_error_override` has a value, RMS error + // from AGC is overridden by it. + void Process(ArrayView audio, + std::optional rms_error_override); + + // Returns the recommended input volume. Must be called after `Process()`. + int recommended_analog_level() const { return recommended_input_volume_; } + + float voice_probability() const { return agc_->voice_probability(); } + void ActivateLogging() { log_to_histograms_ = true; } + std::optional new_compression() const { return new_compression_to_set_; } + + // Only used for testing. + void set_agc(Agc* agc) { agc_.reset(agc); } + int min_mic_level() const { return min_mic_level_; } + + private: + // Sets a new input volume, after first checking that it hasn't been updated + // by the user, in which case no action is taken. + void SetLevel(int new_level); + + // Set the maximum input volume the AGC is allowed to apply. Also updates the + // maximum compression gain to compensate. The volume must be at least + // `kClippedLevelMin`. + void SetMaxLevel(int level); + + int CheckVolumeAndReset(); + void UpdateGain(int rms_error_db); + void UpdateCompressor(); + + const int min_mic_level_; + const bool disable_digital_adaptive_; + std::unique_ptr agc_; + int level_ = 0; + int max_level_; + int max_compression_gain_; + int target_compression_; + int compression_; + float compression_accumulator_; + bool capture_output_used_ = true; + bool check_volume_on_next_process_ = true; + bool startup_ = true; + + // TODO(bugs.webrtc.org/7494): Create a separate member for the applied + // input volume. + // Recommended input volume. After `set_stream_analog_level()` is + // called, it holds the observed applied input volume. Possibly updated by + // `HandleClipping()` and `Process()`; after these calls, holds the + // recommended input volume. + int recommended_input_volume_ = 0; + + std::optional new_compression_to_set_; + bool log_to_histograms_ = false; + const int clipped_level_min_; + + // Frames since the last `UpdateGain()` call. + int frames_since_update_gain_ = 0; + // Set to true for the first frame after startup and reset, otherwise false. + bool is_first_frame_ = true; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_AGC_MANAGER_DIRECT_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/gain_control.h b/pkg/apm/webrtc/modules/audio_processing/agc/gain_control.h new file mode 100644 index 00000000..389b2114 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/gain_control.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC_GAIN_CONTROL_H_ +#define MODULES_AUDIO_PROCESSING_AGC_GAIN_CONTROL_H_ + +namespace webrtc { + +// 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. +class GainControl { + public: + // When an analog mode is set, this must be called prior to `ProcessStream()` + // to pass the current analog level from the audio HAL. Must be within the + // range provided to `set_analog_level_limits()`. + virtual int set_stream_analog_level(int level) = 0; + + // When an analog mode is set, this should be called after `ProcessStream()` + // to obtain the recommended new analog level for the audio HAL. It is the + // users responsibility to apply this level. + virtual int stream_analog_level() const = 0; + + 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 + }; + + virtual int set_mode(Mode mode) = 0; + virtual Mode mode() const = 0; + + // 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]. + // + // TODO(ajm): use a negative value here instead, if/when VoE will similarly + // update its interface. + virtual int set_target_level_dbfs(int level) = 0; + virtual int target_level_dbfs() const = 0; + + // 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]. + virtual int set_compression_gain_db(int gain) = 0; + virtual int compression_gain_db() const = 0; + + // 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. + virtual int enable_limiter(bool enable) = 0; + virtual bool is_limiter_enabled() const = 0; + + // Sets the `minimum` and `maximum` analog levels of the audio capture device. + // Must be set if and only if an analog mode is used. Limited to [0, 65535]. + virtual int set_analog_level_limits(int minimum, int maximum) = 0; + virtual int analog_level_minimum() const = 0; + virtual int analog_level_maximum() const = 0; + + // Returns true if the AGC has detected a saturation event (period where the + // signal reaches digital full-scale) in the current frame and the analog + // level cannot be reduced. + // + // This could be used as an indicator to reduce or disable analog mic gain at + // the audio HAL. + virtual bool stream_is_saturated() const = 0; + + protected: + virtual ~GainControl() {} +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_GAIN_CONTROL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/legacy/analog_agc.cc b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/analog_agc.cc new file mode 100644 index 00000000..e40a3f16 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/analog_agc.cc @@ -0,0 +1,1238 @@ +/* + * 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. + */ + +/* + * + * Using a feedback system, determines an appropriate analog volume level + * given an input signal and current volume level. Targets a conservative + * signal level and is intended for use with a digital AGC to apply + * additional gain. + * + */ + +#include "modules/audio_processing/agc/legacy/analog_agc.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Errors +#define AGC_UNSPECIFIED_ERROR 18000 +#define AGC_UNINITIALIZED_ERROR 18002 +#define AGC_NULL_POINTER_ERROR 18003 +#define AGC_BAD_PARAMETER_ERROR 18004 + +/* The slope of in Q13*/ +static const int16_t kSlope1[8] = {21793, 12517, 7189, 4129, + 2372, 1362, 472, 78}; + +/* The offset in Q14 */ +static const int16_t kOffset1[8] = {25395, 23911, 22206, 20737, + 19612, 18805, 17951, 17367}; + +/* The slope of in Q13*/ +static const int16_t kSlope2[8] = {2063, 1731, 1452, 1218, 1021, 857, 597, 337}; + +/* The offset in Q14 */ +static const int16_t kOffset2[8] = {18432, 18379, 18290, 18177, + 18052, 17920, 17670, 17286}; + +static const int16_t kMuteGuardTimeMs = 8000; +static const int16_t kInitCheck = 42; +static const size_t kNumSubframes = 10; + +/* Default settings if config is not used */ +#define AGC_DEFAULT_TARGET_LEVEL 3 +#define AGC_DEFAULT_COMP_GAIN 9 +/* This is the target level for the analog part in ENV scale. To convert to RMS + * scale you + * have to add OFFSET_ENV_TO_RMS. + */ +#define ANALOG_TARGET_LEVEL 11 +#define ANALOG_TARGET_LEVEL_2 5 // ANALOG_TARGET_LEVEL / 2 +/* Offset between RMS scale (analog part) and ENV scale (digital part). This + * value actually + * varies with the FIXED_ANALOG_TARGET_LEVEL, hence we should in the future + * replace it with + * a table. + */ +#define OFFSET_ENV_TO_RMS 9 +/* The reference input level at which the digital part gives an output of + * targetLevelDbfs + * (desired level) if we have no compression gain. This level should be set high + * enough not + * to compress the peaks due to the dynamics. + */ +#define DIGITAL_REF_AT_0_COMP_GAIN 4 +/* Speed of reference level decrease. + */ +#define DIFF_REF_TO_ANALOG 5 + +/* Size of analog gain table */ +#define GAIN_TBL_LEN 32 +/* Matlab code: + * fprintf(1, '\t%i, %i, %i, %i,\n', round(10.^(linspace(0,10,32)/20) * 2^12)); + */ +/* Q12 */ +static const uint16_t kGainTableAnalog[GAIN_TBL_LEN] = { + 4096, 4251, 4412, 4579, 4752, 4932, 5118, 5312, 5513, 5722, 5938, + 6163, 6396, 6638, 6889, 7150, 7420, 7701, 7992, 8295, 8609, 8934, + 9273, 9623, 9987, 10365, 10758, 11165, 11587, 12025, 12480, 12953}; + +/* Gain/Suppression tables for virtual Mic (in Q10) */ +static const uint16_t kGainTableVirtualMic[128] = { + 1052, 1081, 1110, 1141, 1172, 1204, 1237, 1271, 1305, 1341, 1378, + 1416, 1454, 1494, 1535, 1577, 1620, 1664, 1710, 1757, 1805, 1854, + 1905, 1957, 2010, 2065, 2122, 2180, 2239, 2301, 2364, 2428, 2495, + 2563, 2633, 2705, 2779, 2855, 2933, 3013, 3096, 3180, 3267, 3357, + 3449, 3543, 3640, 3739, 3842, 3947, 4055, 4166, 4280, 4397, 4517, + 4640, 4767, 4898, 5032, 5169, 5311, 5456, 5605, 5758, 5916, 6078, + 6244, 6415, 6590, 6770, 6956, 7146, 7341, 7542, 7748, 7960, 8178, + 8402, 8631, 8867, 9110, 9359, 9615, 9878, 10148, 10426, 10711, 11004, + 11305, 11614, 11932, 12258, 12593, 12938, 13292, 13655, 14029, 14412, 14807, + 15212, 15628, 16055, 16494, 16945, 17409, 17885, 18374, 18877, 19393, 19923, + 20468, 21028, 21603, 22194, 22801, 23425, 24065, 24724, 25400, 26095, 26808, + 27541, 28295, 29069, 29864, 30681, 31520, 32382}; +static const uint16_t kSuppressionTableVirtualMic[128] = { + 1024, 1006, 988, 970, 952, 935, 918, 902, 886, 870, 854, 839, 824, 809, 794, + 780, 766, 752, 739, 726, 713, 700, 687, 675, 663, 651, 639, 628, 616, 605, + 594, 584, 573, 563, 553, 543, 533, 524, 514, 505, 496, 487, 478, 470, 461, + 453, 445, 437, 429, 421, 414, 406, 399, 392, 385, 378, 371, 364, 358, 351, + 345, 339, 333, 327, 321, 315, 309, 304, 298, 293, 288, 283, 278, 273, 268, + 263, 258, 254, 249, 244, 240, 236, 232, 227, 223, 219, 215, 211, 208, 204, + 200, 197, 193, 190, 186, 183, 180, 176, 173, 170, 167, 164, 161, 158, 155, + 153, 150, 147, 145, 142, 139, 137, 134, 132, 130, 127, 125, 123, 121, 118, + 116, 114, 112, 110, 108, 106, 104, 102}; + +/* Table for target energy levels. Values in Q(-7) + * Matlab code + * targetLevelTable = fprintf('%d,\t%d,\t%d,\t%d,\n', + * round((32767*10.^(-(0:63)'/20)).^2*16/2^7) */ + +static const int32_t kTargetLevelTable[64] = { + 134209536, 106606424, 84680493, 67264106, 53429779, 42440782, 33711911, + 26778323, 21270778, 16895980, 13420954, 10660642, 8468049, 6726411, + 5342978, 4244078, 3371191, 2677832, 2127078, 1689598, 1342095, + 1066064, 846805, 672641, 534298, 424408, 337119, 267783, + 212708, 168960, 134210, 106606, 84680, 67264, 53430, + 42441, 33712, 26778, 21271, 16896, 13421, 10661, + 8468, 6726, 5343, 4244, 3371, 2678, 2127, + 1690, 1342, 1066, 847, 673, 534, 424, + 337, 268, 213, 169, 134, 107, 85, + 67}; + +} // namespace + +int WebRtcAgc_AddMic(void* state, + int16_t* const* in_mic, + size_t num_bands, + size_t samples) { + int32_t nrg, max_nrg, sample, tmp32; + int32_t* ptr; + uint16_t targetGainIdx, gain; + size_t i; + int16_t n, L, tmp16, tmp_speech[16]; + LegacyAgc* stt; + stt = reinterpret_cast(state); + + if (stt->fs == 8000) { + L = 8; + if (samples != 80) { + return -1; + } + } else { + L = 16; + if (samples != 160) { + return -1; + } + } + + /* apply slowly varying digital gain */ + if (stt->micVol > stt->maxAnalog) { + /* `maxLevel` is strictly >= `micVol`, so this condition should be + * satisfied here, ensuring there is no divide-by-zero. */ + RTC_DCHECK_GT(stt->maxLevel, stt->maxAnalog); + + /* Q1 */ + tmp16 = (int16_t)(stt->micVol - stt->maxAnalog); + tmp32 = (GAIN_TBL_LEN - 1) * tmp16; + tmp16 = (int16_t)(stt->maxLevel - stt->maxAnalog); + targetGainIdx = tmp32 / tmp16; + RTC_DCHECK_LT(targetGainIdx, GAIN_TBL_LEN); + + /* Increment through the table towards the target gain. + * If micVol drops below maxAnalog, we allow the gain + * to be dropped immediately. */ + if (stt->gainTableIdx < targetGainIdx) { + stt->gainTableIdx++; + } else if (stt->gainTableIdx > targetGainIdx) { + stt->gainTableIdx--; + } + + /* Q12 */ + gain = kGainTableAnalog[stt->gainTableIdx]; + + for (i = 0; i < samples; i++) { + size_t j; + for (j = 0; j < num_bands; ++j) { + sample = (in_mic[j][i] * gain) >> 12; + if (sample > 32767) { + in_mic[j][i] = 32767; + } else if (sample < -32768) { + in_mic[j][i] = -32768; + } else { + in_mic[j][i] = (int16_t)sample; + } + } + } + } else { + stt->gainTableIdx = 0; + } + + /* compute envelope */ + if (stt->inQueue > 0) { + ptr = stt->env[1]; + } else { + ptr = stt->env[0]; + } + + for (i = 0; i < kNumSubframes; i++) { + /* iterate over samples */ + max_nrg = 0; + for (n = 0; n < L; n++) { + nrg = in_mic[0][i * L + n] * in_mic[0][i * L + n]; + if (nrg > max_nrg) { + max_nrg = nrg; + } + } + ptr[i] = max_nrg; + } + + /* compute energy */ + if (stt->inQueue > 0) { + ptr = stt->Rxx16w32_array[1]; + } else { + ptr = stt->Rxx16w32_array[0]; + } + + for (i = 0; i < kNumSubframes / 2; i++) { + if (stt->fs == 16000) { + WebRtcSpl_DownsampleBy2(&in_mic[0][i * 32], 32, tmp_speech, + stt->filterState); + } else { + memcpy(tmp_speech, &in_mic[0][i * 16], 16 * sizeof(int16_t)); + } + /* Compute energy in blocks of 16 samples */ + ptr[i] = WebRtcSpl_DotProductWithScale(tmp_speech, tmp_speech, 16, 4); + } + + /* update queue information */ + if (stt->inQueue == 0) { + stt->inQueue = 1; + } else { + stt->inQueue = 2; + } + + /* call VAD (use low band only) */ + WebRtcAgc_ProcessVad(&stt->vadMic, in_mic[0], samples); + + return 0; +} + +int WebRtcAgc_AddFarend(void* state, const int16_t* in_far, size_t samples) { + LegacyAgc* stt = reinterpret_cast(state); + + int err = WebRtcAgc_GetAddFarendError(state, samples); + + if (err != 0) + return err; + + return WebRtcAgc_AddFarendToDigital(&stt->digitalAgc, in_far, samples); +} + +int WebRtcAgc_GetAddFarendError(void* state, size_t samples) { + LegacyAgc* stt; + stt = reinterpret_cast(state); + + if (stt == NULL) + return -1; + + if (stt->fs == 8000) { + if (samples != 80) + return -1; + } else if (stt->fs == 16000 || stt->fs == 32000 || stt->fs == 48000) { + if (samples != 160) + return -1; + } else { + return -1; + } + + return 0; +} + +int WebRtcAgc_VirtualMic(void* agcInst, + int16_t* const* in_near, + size_t num_bands, + size_t samples, + int32_t micLevelIn, + int32_t* micLevelOut) { + int32_t tmpFlt, micLevelTmp, gainIdx; + uint16_t gain; + size_t ii, j; + LegacyAgc* stt; + + uint32_t nrg; + size_t sampleCntr; + uint32_t frameNrg = 0; + uint32_t frameNrgLimit = 5500; + int16_t numZeroCrossing = 0; + const int16_t kZeroCrossingLowLim = 15; + const int16_t kZeroCrossingHighLim = 20; + + stt = reinterpret_cast(agcInst); + + /* + * Before applying gain decide if this is a low-level signal. + * The idea is that digital AGC will not adapt to low-level + * signals. + */ + if (stt->fs != 8000) { + frameNrgLimit = frameNrgLimit << 1; + } + + frameNrg = (uint32_t)(in_near[0][0] * in_near[0][0]); + for (sampleCntr = 1; sampleCntr < samples; sampleCntr++) { + // increment frame energy if it is less than the limit + // the correct value of the energy is not important + if (frameNrg < frameNrgLimit) { + nrg = (uint32_t)(in_near[0][sampleCntr] * in_near[0][sampleCntr]); + frameNrg += nrg; + } + + // Count the zero crossings + numZeroCrossing += + ((in_near[0][sampleCntr] ^ in_near[0][sampleCntr - 1]) < 0); + } + + if ((frameNrg < 500) || (numZeroCrossing <= 5)) { + stt->lowLevelSignal = 1; + } else if (numZeroCrossing <= kZeroCrossingLowLim) { + stt->lowLevelSignal = 0; + } else if (frameNrg <= frameNrgLimit) { + stt->lowLevelSignal = 1; + } else if (numZeroCrossing >= kZeroCrossingHighLim) { + stt->lowLevelSignal = 1; + } else { + stt->lowLevelSignal = 0; + } + + micLevelTmp = micLevelIn << stt->scale; + /* Set desired level */ + gainIdx = stt->micVol; + if (stt->micVol > stt->maxAnalog) { + gainIdx = stt->maxAnalog; + } + if (micLevelTmp != stt->micRef) { + /* Something has happened with the physical level, restart. */ + stt->micRef = micLevelTmp; + stt->micVol = 127; + *micLevelOut = 127; + stt->micGainIdx = 127; + gainIdx = 127; + } + /* Pre-process the signal to emulate the microphone level. */ + /* Take one step at a time in the gain table. */ + if (gainIdx > 127) { + gain = kGainTableVirtualMic[gainIdx - 128]; + } else { + gain = kSuppressionTableVirtualMic[127 - gainIdx]; + } + for (ii = 0; ii < samples; ii++) { + tmpFlt = (in_near[0][ii] * gain) >> 10; + if (tmpFlt > 32767) { + tmpFlt = 32767; + gainIdx--; + if (gainIdx >= 127) { + gain = kGainTableVirtualMic[gainIdx - 127]; + } else { + gain = kSuppressionTableVirtualMic[127 - gainIdx]; + } + } + if (tmpFlt < -32768) { + tmpFlt = -32768; + gainIdx--; + if (gainIdx >= 127) { + gain = kGainTableVirtualMic[gainIdx - 127]; + } else { + gain = kSuppressionTableVirtualMic[127 - gainIdx]; + } + } + in_near[0][ii] = (int16_t)tmpFlt; + for (j = 1; j < num_bands; ++j) { + tmpFlt = (in_near[j][ii] * gain) >> 10; + if (tmpFlt > 32767) { + tmpFlt = 32767; + } + if (tmpFlt < -32768) { + tmpFlt = -32768; + } + in_near[j][ii] = (int16_t)tmpFlt; + } + } + /* Set the level we (finally) used */ + stt->micGainIdx = gainIdx; + // *micLevelOut = stt->micGainIdx; + *micLevelOut = stt->micGainIdx >> stt->scale; + /* Add to Mic as if it was the output from a true microphone */ + if (WebRtcAgc_AddMic(agcInst, in_near, num_bands, samples) != 0) { + return -1; + } + return 0; +} + +void WebRtcAgc_UpdateAgcThresholds(LegacyAgc* stt) { + int16_t tmp16; + + /* Set analog target level in envelope dBOv scale */ + tmp16 = (DIFF_REF_TO_ANALOG * stt->compressionGaindB) + ANALOG_TARGET_LEVEL_2; + tmp16 = WebRtcSpl_DivW32W16ResW16((int32_t)tmp16, ANALOG_TARGET_LEVEL); + stt->analogTarget = DIGITAL_REF_AT_0_COMP_GAIN + tmp16; + if (stt->analogTarget < DIGITAL_REF_AT_0_COMP_GAIN) { + stt->analogTarget = DIGITAL_REF_AT_0_COMP_GAIN; + } + if (stt->agcMode == kAgcModeFixedDigital) { + /* Adjust for different parameter interpretation in FixedDigital mode */ + stt->analogTarget = stt->compressionGaindB; + } + /* Since the offset between RMS and ENV is not constant, we should make this + * into a + * table, but for now, we'll stick with a constant, tuned for the chosen + * analog + * target level. + */ + stt->targetIdx = ANALOG_TARGET_LEVEL + OFFSET_ENV_TO_RMS; + /* Analog adaptation limits */ + /* analogTargetLevel = round((32767*10^(-targetIdx/20))^2*16/2^7) */ + stt->analogTargetLevel = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx]; /* ex. -20 dBov */ + stt->startUpperLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx - 1]; /* -19 dBov */ + stt->startLowerLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx + 1]; /* -21 dBov */ + stt->upperPrimaryLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx - 2]; /* -18 dBov */ + stt->lowerPrimaryLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx + 2]; /* -22 dBov */ + stt->upperSecondaryLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx - 5]; /* -15 dBov */ + stt->lowerSecondaryLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx + 5]; /* -25 dBov */ + stt->upperLimit = stt->startUpperLimit; + stt->lowerLimit = stt->startLowerLimit; +} + +void WebRtcAgc_SaturationCtrl(LegacyAgc* stt, + uint8_t* saturated, + int32_t* env) { + int16_t i, tmpW16; + + /* Check if the signal is saturated */ + for (i = 0; i < 10; i++) { + tmpW16 = (int16_t)(env[i] >> 20); + if (tmpW16 > 875) { + stt->envSum += tmpW16; + } + } + + if (stt->envSum > 25000) { + *saturated = 1; + stt->envSum = 0; + } + + /* stt->envSum *= 0.99; */ + stt->envSum = (int16_t)((stt->envSum * 32440) >> 15); +} + +void WebRtcAgc_ZeroCtrl(LegacyAgc* stt, int32_t* inMicLevel, int32_t* env) { + int16_t i; + int64_t tmp = 0; + int32_t midVal; + + /* Is the input signal zero? */ + for (i = 0; i < 10; i++) { + tmp += env[i]; + } + + /* Each block is allowed to have a few non-zero + * samples. + */ + if (tmp < 500) { + stt->msZero += 10; + } else { + stt->msZero = 0; + } + + if (stt->muteGuardMs > 0) { + stt->muteGuardMs -= 10; + } + + if (stt->msZero > 500) { + stt->msZero = 0; + + /* Increase microphone level only if it's less than 50% */ + midVal = (stt->maxAnalog + stt->minLevel + 1) / 2; + if (*inMicLevel < midVal) { + /* *inMicLevel *= 1.1; */ + *inMicLevel = (1126 * *inMicLevel) >> 10; + /* Reduces risk of a muted mic repeatedly triggering excessive levels due + * to zero signal detection. */ + *inMicLevel = WEBRTC_SPL_MIN(*inMicLevel, stt->zeroCtrlMax); + stt->micVol = *inMicLevel; + } + + stt->activeSpeech = 0; + stt->Rxx16_LPw32Max = 0; + + /* The AGC has a tendency (due to problems with the VAD parameters), to + * vastly increase the volume after a muting event. This timer prevents + * upwards adaptation for a short period. */ + stt->muteGuardMs = kMuteGuardTimeMs; + } +} + +void WebRtcAgc_SpeakerInactiveCtrl(LegacyAgc* stt) { + /* Check if the near end speaker is inactive. + * If that is the case the VAD threshold is + * increased since the VAD speech model gets + * more sensitive to any sound after a long + * silence. + */ + + int32_t tmp32; + int16_t vadThresh; + + if (stt->vadMic.stdLongTerm < 2500) { + stt->vadThreshold = 1500; + } else { + vadThresh = kNormalVadThreshold; + if (stt->vadMic.stdLongTerm < 4500) { + /* Scale between min and max threshold */ + vadThresh += (4500 - stt->vadMic.stdLongTerm) / 2; + } + + /* stt->vadThreshold = (31 * stt->vadThreshold + vadThresh) / 32; */ + tmp32 = vadThresh + 31 * stt->vadThreshold; + stt->vadThreshold = (int16_t)(tmp32 >> 5); + } +} + +void WebRtcAgc_ExpCurve(int16_t volume, int16_t* index) { + // volume in Q14 + // index in [0-7] + /* 8 different curves */ + if (volume > 5243) { + if (volume > 7864) { + if (volume > 12124) { + *index = 7; + } else { + *index = 6; + } + } else { + if (volume > 6554) { + *index = 5; + } else { + *index = 4; + } + } + } else { + if (volume > 2621) { + if (volume > 3932) { + *index = 3; + } else { + *index = 2; + } + } else { + if (volume > 1311) { + *index = 1; + } else { + *index = 0; + } + } + } +} + +int32_t WebRtcAgc_ProcessAnalog(void* state, + int32_t inMicLevel, + int32_t* outMicLevel, + int16_t vadLogRatio, + int16_t echo, + uint8_t* saturationWarning) { + uint32_t tmpU32; + int32_t Rxx16w32, tmp32; + int32_t inMicLevelTmp, lastMicVol; + int16_t i; + uint8_t saturated = 0; + LegacyAgc* stt; + + stt = reinterpret_cast(state); + inMicLevelTmp = inMicLevel << stt->scale; + + if (inMicLevelTmp > stt->maxAnalog) { + return -1; + } else if (inMicLevelTmp < stt->minLevel) { + return -1; + } + + if (stt->firstCall == 0) { + int32_t tmpVol; + stt->firstCall = 1; + tmp32 = ((stt->maxLevel - stt->minLevel) * 51) >> 9; + tmpVol = (stt->minLevel + tmp32); + + /* If the mic level is very low at start, increase it! */ + if ((inMicLevelTmp < tmpVol) && (stt->agcMode == kAgcModeAdaptiveAnalog)) { + inMicLevelTmp = tmpVol; + } + stt->micVol = inMicLevelTmp; + } + + /* Set the mic level to the previous output value if there is digital input + * gain */ + if ((inMicLevelTmp == stt->maxAnalog) && (stt->micVol > stt->maxAnalog)) { + inMicLevelTmp = stt->micVol; + } + + /* If the mic level was manually changed to a very low value raise it! */ + if ((inMicLevelTmp != stt->micVol) && (inMicLevelTmp < stt->minOutput)) { + tmp32 = ((stt->maxLevel - stt->minLevel) * 51) >> 9; + inMicLevelTmp = (stt->minLevel + tmp32); + stt->micVol = inMicLevelTmp; + } + + if (inMicLevelTmp != stt->micVol) { + if (inMicLevel == stt->lastInMicLevel) { + // We requested a volume adjustment, but it didn't occur. This is + // probably due to a coarse quantization of the volume slider. + // Restore the requested value to prevent getting stuck. + inMicLevelTmp = stt->micVol; + } else { + // As long as the value changed, update to match. + stt->micVol = inMicLevelTmp; + } + } + + if (inMicLevelTmp > stt->maxLevel) { + // Always allow the user to raise the volume above the maxLevel. + stt->maxLevel = inMicLevelTmp; + } + + // Store last value here, after we've taken care of manual updates etc. + stt->lastInMicLevel = inMicLevel; + lastMicVol = stt->micVol; + + /* Checks if the signal is saturated. Also a check if individual samples + * are larger than 12000 is done. If they are the counter for increasing + * the volume level is set to -100ms + */ + WebRtcAgc_SaturationCtrl(stt, &saturated, stt->env[0]); + + /* The AGC is always allowed to lower the level if the signal is saturated */ + if (saturated == 1) { + /* Lower the recording level + * Rxx160_LP is adjusted down because it is so slow it could + * cause the AGC to make wrong decisions. */ + /* stt->Rxx160_LPw32 *= 0.875; */ + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 8) * 7; + + stt->zeroCtrlMax = stt->micVol; + + /* stt->micVol *= 0.903; */ + tmp32 = inMicLevelTmp - stt->minLevel; + tmpU32 = WEBRTC_SPL_UMUL(29591, (uint32_t)(tmp32)); + stt->micVol = (tmpU32 >> 15) + stt->minLevel; + if (stt->micVol > lastMicVol - 2) { + stt->micVol = lastMicVol - 2; + } + inMicLevelTmp = stt->micVol; + + if (stt->micVol < stt->minOutput) { + *saturationWarning = 1; + } + + /* Reset counter for decrease of volume level to avoid + * decreasing too much. The saturation control can still + * lower the level if needed. */ + stt->msTooHigh = -100; + + /* Enable the control mechanism to ensure that our measure, + * Rxx160_LP, is in the correct range. This must be done since + * the measure is very slow. */ + stt->activeSpeech = 0; + stt->Rxx16_LPw32Max = 0; + + /* Reset to initial values */ + stt->msecSpeechInnerChange = kMsecSpeechInner; + stt->msecSpeechOuterChange = kMsecSpeechOuter; + stt->changeToSlowMode = 0; + + stt->muteGuardMs = 0; + + stt->upperLimit = stt->startUpperLimit; + stt->lowerLimit = stt->startLowerLimit; + } + + /* Check if the input speech is zero. If so the mic volume + * is increased. On some computers the input is zero up as high + * level as 17% */ + WebRtcAgc_ZeroCtrl(stt, &inMicLevelTmp, stt->env[0]); + + /* Check if the near end speaker is inactive. + * If that is the case the VAD threshold is + * increased since the VAD speech model gets + * more sensitive to any sound after a long + * silence. + */ + WebRtcAgc_SpeakerInactiveCtrl(stt); + + for (i = 0; i < 5; i++) { + /* Computed on blocks of 16 samples */ + + Rxx16w32 = stt->Rxx16w32_array[0][i]; + + /* Rxx160w32 in Q(-7) */ + tmp32 = (Rxx16w32 - stt->Rxx16_vectorw32[stt->Rxx16pos]) >> 3; + stt->Rxx160w32 = stt->Rxx160w32 + tmp32; + stt->Rxx16_vectorw32[stt->Rxx16pos] = Rxx16w32; + + /* Circular buffer */ + stt->Rxx16pos++; + if (stt->Rxx16pos == kRxxBufferLen) { + stt->Rxx16pos = 0; + } + + /* Rxx16_LPw32 in Q(-4) */ + tmp32 = (Rxx16w32 - stt->Rxx16_LPw32) >> kAlphaShortTerm; + stt->Rxx16_LPw32 = (stt->Rxx16_LPw32) + tmp32; + + if (vadLogRatio > stt->vadThreshold) { + /* Speech detected! */ + + /* Check if Rxx160_LP is in the correct range. If + * it is too high/low then we set it to the maximum of + * Rxx16_LPw32 during the first 200ms of speech. + */ + if (stt->activeSpeech < 250) { + stt->activeSpeech += 2; + + if (stt->Rxx16_LPw32 > stt->Rxx16_LPw32Max) { + stt->Rxx16_LPw32Max = stt->Rxx16_LPw32; + } + } else if (stt->activeSpeech == 250) { + stt->activeSpeech += 2; + tmp32 = stt->Rxx16_LPw32Max >> 3; + stt->Rxx160_LPw32 = tmp32 * kRxxBufferLen; + } + + tmp32 = (stt->Rxx160w32 - stt->Rxx160_LPw32) >> kAlphaLongTerm; + stt->Rxx160_LPw32 = stt->Rxx160_LPw32 + tmp32; + + if (stt->Rxx160_LPw32 > stt->upperSecondaryLimit) { + stt->msTooHigh += 2; + stt->msTooLow = 0; + stt->changeToSlowMode = 0; + + if (stt->msTooHigh > stt->msecSpeechOuterChange) { + stt->msTooHigh = 0; + + /* Lower the recording level */ + /* Multiply by 0.828125 which corresponds to decreasing ~0.8dB */ + tmp32 = stt->Rxx160_LPw32 >> 6; + stt->Rxx160_LPw32 = tmp32 * 53; + + /* Reduce the max gain to avoid excessive oscillation + * (but never drop below the maximum analog level). + */ + stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; + stt->maxLevel = WEBRTC_SPL_MAX(stt->maxLevel, stt->maxAnalog); + + stt->zeroCtrlMax = stt->micVol; + + /* 0.95 in Q15 */ + tmp32 = inMicLevelTmp - stt->minLevel; + tmpU32 = WEBRTC_SPL_UMUL(31130, (uint32_t)(tmp32)); + stt->micVol = (tmpU32 >> 15) + stt->minLevel; + if (stt->micVol > lastMicVol - 1) { + stt->micVol = lastMicVol - 1; + } + inMicLevelTmp = stt->micVol; + + /* Enable the control mechanism to ensure that our measure, + * Rxx160_LP, is in the correct range. + */ + stt->activeSpeech = 0; + stt->Rxx16_LPw32Max = 0; + } + } else if (stt->Rxx160_LPw32 > stt->upperLimit) { + stt->msTooHigh += 2; + stt->msTooLow = 0; + stt->changeToSlowMode = 0; + + if (stt->msTooHigh > stt->msecSpeechInnerChange) { + /* Lower the recording level */ + stt->msTooHigh = 0; + /* Multiply by 0.828125 which corresponds to decreasing ~0.8dB */ + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 53; + + /* Reduce the max gain to avoid excessive oscillation + * (but never drop below the maximum analog level). + */ + stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; + stt->maxLevel = WEBRTC_SPL_MAX(stt->maxLevel, stt->maxAnalog); + + stt->zeroCtrlMax = stt->micVol; + + /* 0.965 in Q15 */ + tmp32 = inMicLevelTmp - stt->minLevel; + tmpU32 = + WEBRTC_SPL_UMUL(31621, (uint32_t)(inMicLevelTmp - stt->minLevel)); + stt->micVol = (tmpU32 >> 15) + stt->minLevel; + if (stt->micVol > lastMicVol - 1) { + stt->micVol = lastMicVol - 1; + } + inMicLevelTmp = stt->micVol; + } + } else if (stt->Rxx160_LPw32 < stt->lowerSecondaryLimit) { + stt->msTooHigh = 0; + stt->changeToSlowMode = 0; + stt->msTooLow += 2; + + if (stt->msTooLow > stt->msecSpeechOuterChange) { + /* Raise the recording level */ + int16_t index, weightFIX; + int16_t volNormFIX = 16384; // =1 in Q14. + + stt->msTooLow = 0; + + /* Normalize the volume level */ + tmp32 = (inMicLevelTmp - stt->minLevel) << 14; + if (stt->maxInit != stt->minLevel) { + volNormFIX = tmp32 / (stt->maxInit - stt->minLevel); + } + + /* Find correct curve */ + WebRtcAgc_ExpCurve(volNormFIX, &index); + + /* Compute weighting factor for the volume increase, 32^(-2*X)/2+1.05 + */ + weightFIX = + kOffset1[index] - (int16_t)((kSlope1[index] * volNormFIX) >> 13); + + /* stt->Rxx160_LPw32 *= 1.047 [~0.2 dB]; */ + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 67; + + tmp32 = inMicLevelTmp - stt->minLevel; + tmpU32 = + ((uint32_t)weightFIX * (uint32_t)(inMicLevelTmp - stt->minLevel)); + stt->micVol = (tmpU32 >> 14) + stt->minLevel; + if (stt->micVol < lastMicVol + 2) { + stt->micVol = lastMicVol + 2; + } + + inMicLevelTmp = stt->micVol; + } + } else if (stt->Rxx160_LPw32 < stt->lowerLimit) { + stt->msTooHigh = 0; + stt->changeToSlowMode = 0; + stt->msTooLow += 2; + + if (stt->msTooLow > stt->msecSpeechInnerChange) { + /* Raise the recording level */ + int16_t index, weightFIX; + int16_t volNormFIX = 16384; // =1 in Q14. + + stt->msTooLow = 0; + + /* Normalize the volume level */ + tmp32 = (inMicLevelTmp - stt->minLevel) << 14; + if (stt->maxInit != stt->minLevel) { + volNormFIX = tmp32 / (stt->maxInit - stt->minLevel); + } + + /* Find correct curve */ + WebRtcAgc_ExpCurve(volNormFIX, &index); + + /* Compute weighting factor for the volume increase, (3.^(-2.*X))/8+1 + */ + weightFIX = + kOffset2[index] - (int16_t)((kSlope2[index] * volNormFIX) >> 13); + + /* stt->Rxx160_LPw32 *= 1.047 [~0.2 dB]; */ + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 67; + + tmp32 = inMicLevelTmp - stt->minLevel; + tmpU32 = + ((uint32_t)weightFIX * (uint32_t)(inMicLevelTmp - stt->minLevel)); + stt->micVol = (tmpU32 >> 14) + stt->minLevel; + if (stt->micVol < lastMicVol + 1) { + stt->micVol = lastMicVol + 1; + } + + inMicLevelTmp = stt->micVol; + } + } else { + /* The signal is inside the desired range which is: + * lowerLimit < Rxx160_LP/640 < upperLimit + */ + if (stt->changeToSlowMode > 4000) { + stt->msecSpeechInnerChange = 1000; + stt->msecSpeechOuterChange = 500; + stt->upperLimit = stt->upperPrimaryLimit; + stt->lowerLimit = stt->lowerPrimaryLimit; + } else { + stt->changeToSlowMode += 2; // in milliseconds + } + stt->msTooLow = 0; + stt->msTooHigh = 0; + + stt->micVol = inMicLevelTmp; + } + } + } + + /* Ensure gain is not increased in presence of echo or after a mute event + * (but allow the zeroCtrl() increase on the frame of a mute detection). + */ + if (echo == 1 || + (stt->muteGuardMs > 0 && stt->muteGuardMs < kMuteGuardTimeMs)) { + if (stt->micVol > lastMicVol) { + stt->micVol = lastMicVol; + } + } + + /* limit the gain */ + if (stt->micVol > stt->maxLevel) { + stt->micVol = stt->maxLevel; + } else if (stt->micVol < stt->minOutput) { + stt->micVol = stt->minOutput; + } + + *outMicLevel = WEBRTC_SPL_MIN(stt->micVol, stt->maxAnalog) >> stt->scale; + + return 0; +} + +int WebRtcAgc_Analyze(void* agcInst, + const int16_t* const* in_near, + size_t num_bands, + size_t samples, + int32_t inMicLevel, + int32_t* outMicLevel, + int16_t echo, + uint8_t* saturationWarning, + int32_t gains[11]) { + LegacyAgc* stt = reinterpret_cast(agcInst); + + if (stt == NULL) { + return -1; + } + + if (stt->fs == 8000) { + if (samples != 80) { + return -1; + } + } else if (stt->fs == 16000 || stt->fs == 32000 || stt->fs == 48000) { + if (samples != 160) { + return -1; + } + } else { + return -1; + } + + *saturationWarning = 0; + // TODO(minyue): PUT IN RANGE CHECKING FOR INPUT LEVELS + *outMicLevel = inMicLevel; + + int32_t error = + WebRtcAgc_ComputeDigitalGains(&stt->digitalAgc, in_near, num_bands, + stt->fs, stt->lowLevelSignal, gains); + if (error == -1) { + return -1; + } + + if (stt->agcMode < kAgcModeFixedDigital && + (stt->lowLevelSignal == 0 || stt->agcMode != kAgcModeAdaptiveDigital)) { + if (WebRtcAgc_ProcessAnalog(agcInst, inMicLevel, outMicLevel, + stt->vadMic.logRatio, echo, + saturationWarning) == -1) { + return -1; + } + } + + /* update queue */ + if (stt->inQueue > 1) { + memcpy(stt->env[0], stt->env[1], 10 * sizeof(int32_t)); + memcpy(stt->Rxx16w32_array[0], stt->Rxx16w32_array[1], 5 * sizeof(int32_t)); + } + + if (stt->inQueue > 0) { + stt->inQueue--; + } + + return 0; +} + +int WebRtcAgc_Process(const void* agcInst, + const int32_t gains[11], + const int16_t* const* in_near, + size_t num_bands, + int16_t* const* out) { + const LegacyAgc* stt = (const LegacyAgc*)agcInst; + return WebRtcAgc_ApplyDigitalGains(gains, num_bands, stt->fs, in_near, out); +} + +int WebRtcAgc_set_config(void* agcInst, WebRtcAgcConfig agcConfig) { + LegacyAgc* stt; + stt = reinterpret_cast(agcInst); + + if (stt == NULL) { + return -1; + } + + if (stt->initFlag != kInitCheck) { + stt->lastError = AGC_UNINITIALIZED_ERROR; + return -1; + } + + if (agcConfig.limiterEnable != kAgcFalse && + agcConfig.limiterEnable != kAgcTrue) { + stt->lastError = AGC_BAD_PARAMETER_ERROR; + return -1; + } + stt->limiterEnable = agcConfig.limiterEnable; + stt->compressionGaindB = agcConfig.compressionGaindB; + if ((agcConfig.targetLevelDbfs < 0) || (agcConfig.targetLevelDbfs > 31)) { + stt->lastError = AGC_BAD_PARAMETER_ERROR; + return -1; + } + stt->targetLevelDbfs = agcConfig.targetLevelDbfs; + + if (stt->agcMode == kAgcModeFixedDigital) { + /* Adjust for different parameter interpretation in FixedDigital mode */ + stt->compressionGaindB += agcConfig.targetLevelDbfs; + } + + /* Update threshold levels for analog adaptation */ + WebRtcAgc_UpdateAgcThresholds(stt); + + /* Recalculate gain table */ + if (WebRtcAgc_CalculateGainTable( + &(stt->digitalAgc.gainTable[0]), stt->compressionGaindB, + stt->targetLevelDbfs, stt->limiterEnable, stt->analogTarget) == -1) { + return -1; + } + /* Store the config in a WebRtcAgcConfig */ + stt->usedConfig.compressionGaindB = agcConfig.compressionGaindB; + stt->usedConfig.limiterEnable = agcConfig.limiterEnable; + stt->usedConfig.targetLevelDbfs = agcConfig.targetLevelDbfs; + + return 0; +} + +int WebRtcAgc_get_config(void* agcInst, WebRtcAgcConfig* config) { + LegacyAgc* stt; + stt = reinterpret_cast(agcInst); + + if (stt == NULL) { + return -1; + } + + if (config == NULL) { + stt->lastError = AGC_NULL_POINTER_ERROR; + return -1; + } + + if (stt->initFlag != kInitCheck) { + stt->lastError = AGC_UNINITIALIZED_ERROR; + return -1; + } + + config->limiterEnable = stt->usedConfig.limiterEnable; + config->targetLevelDbfs = stt->usedConfig.targetLevelDbfs; + config->compressionGaindB = stt->usedConfig.compressionGaindB; + + return 0; +} + +void* WebRtcAgc_Create() { + LegacyAgc* stt = static_cast(malloc(sizeof(LegacyAgc))); + + stt->initFlag = 0; + stt->lastError = 0; + + return stt; +} + +void WebRtcAgc_Free(void* state) { + LegacyAgc* stt; + + stt = reinterpret_cast(state); + free(stt); +} + +/* minLevel - Minimum volume level + * maxLevel - Maximum volume level + */ +int WebRtcAgc_Init(void* agcInst, + int32_t minLevel, + int32_t maxLevel, + int16_t agcMode, + uint32_t fs) { + int32_t max_add, tmp32; + int16_t i; + int tmpNorm; + LegacyAgc* stt; + + /* typecast state pointer */ + stt = reinterpret_cast(agcInst); + + if (WebRtcAgc_InitDigital(&stt->digitalAgc, agcMode) != 0) { + stt->lastError = AGC_UNINITIALIZED_ERROR; + return -1; + } + + /* Analog AGC variables */ + stt->envSum = 0; + + /* mode = 0 - Only saturation protection + * 1 - Analog Automatic Gain Control [-targetLevelDbfs (default -3 + * dBOv)] + * 2 - Digital Automatic Gain Control [-targetLevelDbfs (default -3 + * dBOv)] + * 3 - Fixed Digital Gain [compressionGaindB (default 8 dB)] + */ + if (agcMode < kAgcModeUnchanged || agcMode > kAgcModeFixedDigital) { + return -1; + } + stt->agcMode = agcMode; + stt->fs = fs; + + /* initialize input VAD */ + WebRtcAgc_InitVad(&stt->vadMic); + + /* If the volume range is smaller than 0-256 then + * the levels are shifted up to Q8-domain */ + tmpNorm = WebRtcSpl_NormU32((uint32_t)maxLevel); + stt->scale = tmpNorm - 23; + if (stt->scale < 0) { + stt->scale = 0; + } + // TODO(bjornv): Investigate if we really need to scale up a small range now + // when we have + // a guard against zero-increments. For now, we do not support scale up (scale + // = 0). + stt->scale = 0; + maxLevel <<= stt->scale; + minLevel <<= stt->scale; + + /* Make minLevel and maxLevel static in AdaptiveDigital */ + if (stt->agcMode == kAgcModeAdaptiveDigital) { + minLevel = 0; + maxLevel = 255; + stt->scale = 0; + } + /* The maximum supplemental volume range is based on a vague idea + * of how much lower the gain will be than the real analog gain. */ + max_add = (maxLevel - minLevel) / 4; + + /* Minimum/maximum volume level that can be set */ + stt->minLevel = minLevel; + stt->maxAnalog = maxLevel; + stt->maxLevel = maxLevel + max_add; + stt->maxInit = stt->maxLevel; + + stt->zeroCtrlMax = stt->maxAnalog; + stt->lastInMicLevel = 0; + + /* Initialize micVol parameter */ + stt->micVol = stt->maxAnalog; + if (stt->agcMode == kAgcModeAdaptiveDigital) { + stt->micVol = 127; /* Mid-point of mic level */ + } + stt->micRef = stt->micVol; + stt->micGainIdx = 127; + + /* Minimum output volume is 4% higher than the available lowest volume level + */ + tmp32 = ((stt->maxLevel - stt->minLevel) * 10) >> 8; + stt->minOutput = (stt->minLevel + tmp32); + + stt->msTooLow = 0; + stt->msTooHigh = 0; + stt->changeToSlowMode = 0; + stt->firstCall = 0; + stt->msZero = 0; + stt->muteGuardMs = 0; + stt->gainTableIdx = 0; + + stt->msecSpeechInnerChange = kMsecSpeechInner; + stt->msecSpeechOuterChange = kMsecSpeechOuter; + + stt->activeSpeech = 0; + stt->Rxx16_LPw32Max = 0; + + stt->vadThreshold = kNormalVadThreshold; + stt->inActive = 0; + + for (i = 0; i < kRxxBufferLen; i++) { + stt->Rxx16_vectorw32[i] = (int32_t)1000; /* -54dBm0 */ + } + stt->Rxx160w32 = 125 * kRxxBufferLen; /* (stt->Rxx16_vectorw32[0]>>3) = 125 */ + + stt->Rxx16pos = 0; + stt->Rxx16_LPw32 = (int32_t)16284; /* Q(-4) */ + + for (i = 0; i < 5; i++) { + stt->Rxx16w32_array[0][i] = 0; + } + for (i = 0; i < 10; i++) { + stt->env[0][i] = 0; + stt->env[1][i] = 0; + } + stt->inQueue = 0; + + WebRtcSpl_MemSetW32(stt->filterState, 0, 8); + + stt->initFlag = kInitCheck; + // Default config settings. + stt->defaultConfig.limiterEnable = kAgcTrue; + stt->defaultConfig.targetLevelDbfs = AGC_DEFAULT_TARGET_LEVEL; + stt->defaultConfig.compressionGaindB = AGC_DEFAULT_COMP_GAIN; + + if (WebRtcAgc_set_config(stt, stt->defaultConfig) == -1) { + stt->lastError = AGC_UNSPECIFIED_ERROR; + return -1; + } + stt->Rxx160_LPw32 = stt->analogTargetLevel; // Initialize rms value + + stt->lowLevelSignal = 0; + + /* Only positive values are allowed that are not too large */ + if ((minLevel >= maxLevel) || (maxLevel & 0xFC000000)) { + return -1; + } else { + return 0; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/legacy/analog_agc.h b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/analog_agc.h new file mode 100644 index 00000000..7a231c8a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/analog_agc.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC_LEGACY_ANALOG_AGC_H_ +#define MODULES_AUDIO_PROCESSING_AGC_LEGACY_ANALOG_AGC_H_ + +#include "modules/audio_processing/agc/legacy/digital_agc.h" +#include "modules/audio_processing/agc/legacy/gain_control.h" + +namespace webrtc { + +/* Analog Automatic Gain Control variables: + * Constant declarations (inner limits inside which no changes are done) + * In the beginning the range is narrower to widen as soon as the measure + * 'Rxx160_LP' is inside it. Currently the starting limits are -22.2+/-1dBm0 + * and the final limits -22.2+/-2.5dBm0. These levels makes the speech signal + * go towards -25.4dBm0 (-31.4dBov). Tuned with wbfile-31.4dBov.pcm + * The limits are created by running the AGC with a file having the desired + * signal level and thereafter plotting Rxx160_LP in the dBm0-domain defined + * by out=10*log10(in/260537279.7); Set the target level to the average level + * of our measure Rxx160_LP. Remember that the levels are in blocks of 16 in + * Q(-7). (Example matlab code: round(db2pow(-21.2)*16/2^7) ) + */ +constexpr int16_t kRxxBufferLen = 10; + +static const int16_t kMsecSpeechInner = 520; +static const int16_t kMsecSpeechOuter = 340; + +static const int16_t kNormalVadThreshold = 400; + +static const int16_t kAlphaShortTerm = 6; // 1 >> 6 = 0.0156 +static const int16_t kAlphaLongTerm = 10; // 1 >> 10 = 0.000977 + +typedef struct { + // Configurable parameters/variables + uint32_t fs; // Sampling frequency + int16_t compressionGaindB; // Fixed gain level in dB + int16_t targetLevelDbfs; // Target level in -dBfs of envelope (default -3) + int16_t agcMode; // Hard coded mode (adaptAna/adaptDig/fixedDig) + uint8_t limiterEnable; // Enabling limiter (on/off (default off)) + WebRtcAgcConfig defaultConfig; + WebRtcAgcConfig usedConfig; + + // General variables + int16_t initFlag; + int16_t lastError; + + // Target level parameters + // Based on the above: analogTargetLevel = round((32767*10^(-22/20))^2*16/2^7) + int32_t analogTargetLevel; // = kRxxBufferLen * 846805; -22 dBfs + int32_t startUpperLimit; // = kRxxBufferLen * 1066064; -21 dBfs + int32_t startLowerLimit; // = kRxxBufferLen * 672641; -23 dBfs + int32_t upperPrimaryLimit; // = kRxxBufferLen * 1342095; -20 dBfs + int32_t lowerPrimaryLimit; // = kRxxBufferLen * 534298; -24 dBfs + int32_t upperSecondaryLimit; // = kRxxBufferLen * 2677832; -17 dBfs + int32_t lowerSecondaryLimit; // = kRxxBufferLen * 267783; -27 dBfs + uint16_t targetIdx; // Table index for corresponding target level + int16_t analogTarget; // Digital reference level in ENV scale + + // Analog AGC specific variables + int32_t filterState[8]; // For downsampling wb to nb + int32_t upperLimit; // Upper limit for mic energy + int32_t lowerLimit; // Lower limit for mic energy + int32_t Rxx160w32; // Average energy for one frame + int32_t Rxx16_LPw32; // Low pass filtered subframe energies + int32_t Rxx160_LPw32; // Low pass filtered frame energies + int32_t Rxx16_LPw32Max; // Keeps track of largest energy subframe + int32_t Rxx16_vectorw32[kRxxBufferLen]; // Array with subframe energies + int32_t Rxx16w32_array[2][5]; // Energy values of microphone signal + int32_t env[2][10]; // Envelope values of subframes + + int16_t Rxx16pos; // Current position in the Rxx16_vectorw32 + int16_t envSum; // Filtered scaled envelope in subframes + int16_t vadThreshold; // Threshold for VAD decision + int16_t inActive; // Inactive time in milliseconds + int16_t msTooLow; // Milliseconds of speech at a too low level + int16_t msTooHigh; // Milliseconds of speech at a too high level + int16_t changeToSlowMode; // Change to slow mode after some time at target + int16_t firstCall; // First call to the process-function + int16_t msZero; // Milliseconds of zero input + int16_t msecSpeechOuterChange; // Min ms of speech between volume changes + int16_t msecSpeechInnerChange; // Min ms of speech between volume changes + int16_t activeSpeech; // Milliseconds of active speech + int16_t muteGuardMs; // Counter to prevent mute action + int16_t inQueue; // 10 ms batch indicator + + // Microphone level variables + int32_t micRef; // Remember ref. mic level for virtual mic + uint16_t gainTableIdx; // Current position in virtual gain table + int32_t micGainIdx; // Gain index of mic level to increase slowly + int32_t micVol; // Remember volume between frames + int32_t maxLevel; // Max possible vol level, incl dig gain + int32_t maxAnalog; // Maximum possible analog volume level + int32_t maxInit; // Initial value of "max" + int32_t minLevel; // Minimum possible volume level + int32_t minOutput; // Minimum output volume level + int32_t zeroCtrlMax; // Remember max gain => don't amp low input + int32_t lastInMicLevel; + + int16_t scale; // Scale factor for internal volume levels + // Structs for VAD and digital_agc + AgcVad vadMic; + DigitalAgc digitalAgc; + + int16_t lowLevelSignal; +} LegacyAgc; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_LEGACY_ANALOG_AGC_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/legacy/digital_agc.cc b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/digital_agc.cc new file mode 100644 index 00000000..5bf7aaff --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/digital_agc.cc @@ -0,0 +1,704 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc/legacy/digital_agc.h" + +#include + +#include "modules/audio_processing/agc/legacy/gain_control.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// To generate the gaintable, copy&paste the following lines to a Matlab window: +// MaxGain = 6; MinGain = 0; CompRatio = 3; Knee = 1; +// zeros = 0:31; lvl = 2.^(1-zeros); +// A = -10*log10(lvl) * (CompRatio - 1) / CompRatio; +// B = MaxGain - MinGain; +// gains = round(2^16*10.^(0.05 * (MinGain + B * ( +// log(exp(-Knee*A)+exp(-Knee*B)) - log(1+exp(-Knee*B)) ) / +// log(1/(1+exp(Knee*B)))))); +// fprintf(1, '\t%i, %i, %i, %i,\n', gains); +// % Matlab code for plotting the gain and input/output level characteristic +// (copy/paste the following 3 lines): +// in = 10*log10(lvl); out = 20*log10(gains/65536); +// subplot(121); plot(in, out); axis([-30, 0, -5, 20]); grid on; xlabel('Input +// (dB)'); ylabel('Gain (dB)'); +// subplot(122); plot(in, in+out); axis([-30, 0, -30, 5]); grid on; +// xlabel('Input (dB)'); ylabel('Output (dB)'); +// zoom on; + +// Generator table for y=log2(1+e^x) in Q8. +enum { kGenFuncTableSize = 128 }; +static const uint16_t kGenFuncTable[kGenFuncTableSize] = { + 256, 485, 786, 1126, 1484, 1849, 2217, 2586, 2955, 3324, 3693, + 4063, 4432, 4801, 5171, 5540, 5909, 6279, 6648, 7017, 7387, 7756, + 8125, 8495, 8864, 9233, 9603, 9972, 10341, 10711, 11080, 11449, 11819, + 12188, 12557, 12927, 13296, 13665, 14035, 14404, 14773, 15143, 15512, 15881, + 16251, 16620, 16989, 17359, 17728, 18097, 18466, 18836, 19205, 19574, 19944, + 20313, 20682, 21052, 21421, 21790, 22160, 22529, 22898, 23268, 23637, 24006, + 24376, 24745, 25114, 25484, 25853, 26222, 26592, 26961, 27330, 27700, 28069, + 28438, 28808, 29177, 29546, 29916, 30285, 30654, 31024, 31393, 31762, 32132, + 32501, 32870, 33240, 33609, 33978, 34348, 34717, 35086, 35456, 35825, 36194, + 36564, 36933, 37302, 37672, 38041, 38410, 38780, 39149, 39518, 39888, 40257, + 40626, 40996, 41365, 41734, 42104, 42473, 42842, 43212, 43581, 43950, 44320, + 44689, 45058, 45428, 45797, 46166, 46536, 46905}; + +static const int16_t kAvgDecayTime = 250; // frames; < 3000 + +// the 32 most significant bits of A(19) * B(26) >> 13 +#define AGC_MUL32(A, B) (((B) >> 13) * (A) + (((0x00001FFF & (B)) * (A)) >> 13)) +// C + the 32 most significant bits of A * B +#define AGC_SCALEDIFF32(A, B, C) \ + ((C) + ((B) >> 16) * (A) + (((0x0000FFFF & (B)) * (A)) >> 16)) + +} // namespace + +int32_t WebRtcAgc_CalculateGainTable(int32_t* gainTable, // Q16 + int16_t digCompGaindB, // Q0 + int16_t targetLevelDbfs, // Q0 + uint8_t limiterEnable, + int16_t analogTarget) { // Q0 + // This function generates the compressor gain table used in the fixed digital + // part. + uint32_t tmpU32no1, tmpU32no2, absInLevel, logApprox; + int32_t inLevel, limiterLvl; + int32_t tmp32, tmp32no1, tmp32no2, numFIX, den, y32; + const uint16_t kLog10 = 54426; // log2(10) in Q14 + const uint16_t kLog10_2 = 49321; // 10*log10(2) in Q14 + const uint16_t kLogE_1 = 23637; // log2(e) in Q14 + uint16_t constMaxGain; + uint16_t tmpU16, intPart, fracPart; + const int16_t kCompRatio = 3; + int16_t limiterOffset = 0; // Limiter offset + int16_t limiterIdx, limiterLvlX; + int16_t constLinApprox, maxGain, diffGain; + int16_t i, tmp16, tmp16no1; + int zeros, zerosScale; + + // Constants + // kLogE_1 = 23637; // log2(e) in Q14 + // kLog10 = 54426; // log2(10) in Q14 + // kLog10_2 = 49321; // 10*log10(2) in Q14 + + // Calculate maximum digital gain and zero gain level + tmp32no1 = (digCompGaindB - analogTarget) * (kCompRatio - 1); + tmp16no1 = analogTarget - targetLevelDbfs; + tmp16no1 += + WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio); + maxGain = WEBRTC_SPL_MAX(tmp16no1, (analogTarget - targetLevelDbfs)); + tmp32no1 = maxGain * kCompRatio; + if ((digCompGaindB <= analogTarget) && (limiterEnable)) { + limiterOffset = 0; + } + + // Calculate the difference between maximum gain and gain at 0dB0v + tmp32no1 = digCompGaindB * (kCompRatio - 1); + diffGain = + WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio); + if (diffGain < 0 || diffGain >= kGenFuncTableSize) { + RTC_DCHECK(0); + return -1; + } + + // Calculate the limiter level and index: + // limiterLvlX = analogTarget - limiterOffset + // limiterLvl = targetLevelDbfs + limiterOffset/compRatio + limiterLvlX = analogTarget - limiterOffset; + limiterIdx = 2 + WebRtcSpl_DivW32W16ResW16((int32_t)limiterLvlX * (1 << 13), + kLog10_2 / 2); + tmp16no1 = + WebRtcSpl_DivW32W16ResW16(limiterOffset + (kCompRatio >> 1), kCompRatio); + limiterLvl = targetLevelDbfs + tmp16no1; + + // Calculate (through table lookup): + // constMaxGain = log2(1+2^(log2(e)*diffGain)); (in Q8) + constMaxGain = kGenFuncTable[diffGain]; // in Q8 + + // Calculate a parameter used to approximate the fractional part of 2^x with a + // piecewise linear function in Q14: + // constLinApprox = round(3/2*(4*(3-2*sqrt(2))/(log(2)^2)-0.5)*2^14); + constLinApprox = 22817; // in Q14 + + // Calculate a denominator used in the exponential part to convert from dB to + // linear scale: + // den = 20*constMaxGain (in Q8) + den = WEBRTC_SPL_MUL_16_U16(20, constMaxGain); // in Q8 + + for (i = 0; i < 32; i++) { + // Calculate scaled input level (compressor): + // inLevel = + // fix((-constLog10_2*(compRatio-1)*(1-i)+fix(compRatio/2))/compRatio) + tmp16 = (int16_t)((kCompRatio - 1) * (i - 1)); // Q0 + tmp32 = WEBRTC_SPL_MUL_16_U16(tmp16, kLog10_2) + 1; // Q14 + inLevel = WebRtcSpl_DivW32W16(tmp32, kCompRatio); // Q14 + + // Calculate diffGain-inLevel, to map using the genFuncTable + inLevel = (int32_t)diffGain * (1 << 14) - inLevel; // Q14 + + // Make calculations on abs(inLevel) and compensate for the sign afterwards. + absInLevel = (uint32_t)WEBRTC_SPL_ABS_W32(inLevel); // Q14 + + // LUT with interpolation + intPart = (uint16_t)(absInLevel >> 14); + fracPart = + (uint16_t)(absInLevel & 0x00003FFF); // extract the fractional part + tmpU16 = kGenFuncTable[intPart + 1] - kGenFuncTable[intPart]; // Q8 + tmpU32no1 = tmpU16 * fracPart; // Q22 + tmpU32no1 += (uint32_t)kGenFuncTable[intPart] << 14; // Q22 + logApprox = tmpU32no1 >> 8; // Q14 + // Compensate for negative exponent using the relation: + // log2(1 + 2^-x) = log2(1 + 2^x) - x + if (inLevel < 0) { + zeros = WebRtcSpl_NormU32(absInLevel); + zerosScale = 0; + if (zeros < 15) { + // Not enough space for multiplication + tmpU32no2 = absInLevel >> (15 - zeros); // Q(zeros-1) + tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no2, kLogE_1); // Q(zeros+13) + if (zeros < 9) { + zerosScale = 9 - zeros; + tmpU32no1 >>= zerosScale; // Q(zeros+13) + } else { + tmpU32no2 >>= zeros - 9; // Q22 + } + } else { + tmpU32no2 = WEBRTC_SPL_UMUL_32_16(absInLevel, kLogE_1); // Q28 + tmpU32no2 >>= 6; // Q22 + } + logApprox = 0; + if (tmpU32no2 < tmpU32no1) { + logApprox = (tmpU32no1 - tmpU32no2) >> (8 - zerosScale); // Q14 + } + } + numFIX = (maxGain * constMaxGain) * (1 << 6); // Q14 + numFIX -= (int32_t)logApprox * diffGain; // Q14 + + // Calculate ratio + // Shift `numFIX` as much as possible. + // Ensure we avoid wrap-around in `den` as well. + if (numFIX > (den >> 8) || -numFIX > (den >> 8)) { // `den` is Q8. + zeros = WebRtcSpl_NormW32(numFIX); + } else { + zeros = WebRtcSpl_NormW32(den) + 8; + } + numFIX *= 1 << zeros; // Q(14+zeros) + + // Shift den so we end up in Qy1 + tmp32no1 = WEBRTC_SPL_SHIFT_W32(den, zeros - 9); // Q(zeros - 1) + y32 = numFIX / tmp32no1; // in Q15 + // This is to do rounding in Q14. + y32 = y32 >= 0 ? (y32 + 1) >> 1 : -((-y32 + 1) >> 1); + + if (limiterEnable && (i < limiterIdx)) { + tmp32 = WEBRTC_SPL_MUL_16_U16(i - 1, kLog10_2); // Q14 + tmp32 -= limiterLvl * (1 << 14); // Q14 + y32 = WebRtcSpl_DivW32W16(tmp32 + 10, 20); + } + if (y32 > 39000) { + tmp32 = (y32 >> 1) * kLog10 + 4096; // in Q27 + tmp32 >>= 13; // In Q14. + } else { + tmp32 = y32 * kLog10 + 8192; // in Q28 + tmp32 >>= 14; // In Q14. + } + tmp32 += 16 << 14; // in Q14 (Make sure final output is in Q16) + + // Calculate power + if (tmp32 > 0) { + intPart = (int16_t)(tmp32 >> 14); + fracPart = (uint16_t)(tmp32 & 0x00003FFF); // in Q14 + if ((fracPart >> 13) != 0) { + tmp16 = (2 << 14) - constLinApprox; + tmp32no2 = (1 << 14) - fracPart; + tmp32no2 *= tmp16; + tmp32no2 >>= 13; + tmp32no2 = (1 << 14) - tmp32no2; + } else { + tmp16 = constLinApprox - (1 << 14); + tmp32no2 = (fracPart * tmp16) >> 13; + } + fracPart = (uint16_t)tmp32no2; + gainTable[i] = + (1 << intPart) + WEBRTC_SPL_SHIFT_W32(fracPart, intPart - 14); + } else { + gainTable[i] = 0; + } + } + + return 0; +} + +int32_t WebRtcAgc_InitDigital(DigitalAgc* stt, int16_t agcMode) { + if (agcMode == kAgcModeFixedDigital) { + // start at minimum to find correct gain faster + stt->capacitorSlow = 0; + } else { + // start out with 0 dB gain + stt->capacitorSlow = 134217728; // (int32_t)(0.125f * 32768.0f * 32768.0f); + } + stt->capacitorFast = 0; + stt->gain = 65536; + stt->gatePrevious = 0; + stt->agcMode = agcMode; + + // initialize VADs + WebRtcAgc_InitVad(&stt->vadNearend); + WebRtcAgc_InitVad(&stt->vadFarend); + + return 0; +} + +int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc* stt, + const int16_t* in_far, + size_t nrSamples) { + RTC_DCHECK(stt); + // VAD for far end + WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples); + + return 0; +} + +// Gains is an 11 element long array (one value per ms, incl start & end). +int32_t WebRtcAgc_ComputeDigitalGains(DigitalAgc* stt, + const int16_t* const* in_near, + size_t /* num_bands */, + uint32_t FS, + int16_t lowlevelSignal, + int32_t gains[11]) { + int32_t tmp32; + int32_t env[10]; + int32_t max_nrg; + int32_t cur_level; + int32_t gain32; + int16_t logratio; + int16_t lower_thr, upper_thr; + int16_t zeros = 0, zeros_fast, frac = 0; + int16_t decay; + int16_t gate, gain_adj; + int16_t k; + size_t n, L; + + // determine number of samples per ms + if (FS == 8000) { + L = 8; + } else if (FS == 16000 || FS == 32000 || FS == 48000) { + L = 16; + } else { + return -1; + } + + // VAD for near end + logratio = WebRtcAgc_ProcessVad(&stt->vadNearend, in_near[0], L * 10); + + // Account for far end VAD + if (stt->vadFarend.counter > 10) { + tmp32 = 3 * logratio; + logratio = (int16_t)((tmp32 - stt->vadFarend.logRatio) >> 2); + } + + // Determine decay factor depending on VAD + // upper_thr = 1.0f; + // lower_thr = 0.25f; + upper_thr = 1024; // Q10 + lower_thr = 0; // Q10 + if (logratio > upper_thr) { + // decay = -2^17 / DecayTime; -> -65 + decay = -65; + } else if (logratio < lower_thr) { + decay = 0; + } else { + // decay = (int16_t)(((lower_thr - logratio) + // * (2^27/(DecayTime*(upper_thr-lower_thr)))) >> 10); + // SUBSTITUTED: 2^27/(DecayTime*(upper_thr-lower_thr)) -> 65 + tmp32 = (lower_thr - logratio) * 65; + decay = (int16_t)(tmp32 >> 10); + } + + // adjust decay factor for long silence (detected as low standard deviation) + // This is only done in the adaptive modes + if (stt->agcMode != kAgcModeFixedDigital) { + if (stt->vadNearend.stdLongTerm < 4000) { + decay = 0; + } else if (stt->vadNearend.stdLongTerm < 8096) { + // decay = (int16_t)(((stt->vadNearend.stdLongTerm - 4000) * decay) >> + // 12); + tmp32 = (stt->vadNearend.stdLongTerm - 4000) * decay; + decay = (int16_t)(tmp32 >> 12); + } + + if (lowlevelSignal != 0) { + decay = 0; + } + } + // Find max amplitude per sub frame + // iterate over sub frames + for (k = 0; k < 10; k++) { + // iterate over samples + max_nrg = 0; + for (n = 0; n < L; n++) { + int32_t nrg = in_near[0][k * L + n] * in_near[0][k * L + n]; + if (nrg > max_nrg) { + max_nrg = nrg; + } + } + env[k] = max_nrg; + } + + // Calculate gain per sub frame + gains[0] = stt->gain; + for (k = 0; k < 10; k++) { + // Fast envelope follower + // decay time = -131000 / -1000 = 131 (ms) + stt->capacitorFast = + AGC_SCALEDIFF32(-1000, stt->capacitorFast, stt->capacitorFast); + if (env[k] > stt->capacitorFast) { + stt->capacitorFast = env[k]; + } + // Slow envelope follower + if (env[k] > stt->capacitorSlow) { + // increase capacitorSlow + stt->capacitorSlow = AGC_SCALEDIFF32(500, (env[k] - stt->capacitorSlow), + stt->capacitorSlow); + } else { + // decrease capacitorSlow + stt->capacitorSlow = + AGC_SCALEDIFF32(decay, stt->capacitorSlow, stt->capacitorSlow); + } + + // use maximum of both capacitors as current level + if (stt->capacitorFast > stt->capacitorSlow) { + cur_level = stt->capacitorFast; + } else { + cur_level = stt->capacitorSlow; + } + // Translate signal level into gain, using a piecewise linear approximation + // find number of leading zeros + zeros = WebRtcSpl_NormU32((uint32_t)cur_level); + if (cur_level == 0) { + zeros = 31; + } + tmp32 = ((uint32_t)cur_level << zeros) & 0x7FFFFFFF; + frac = (int16_t)(tmp32 >> 19); // Q12. + // Interpolate between gainTable[zeros] and gainTable[zeros-1]. + tmp32 = + ((stt->gainTable[zeros - 1] - stt->gainTable[zeros]) * (int64_t)frac) >> + 12; + gains[k + 1] = stt->gainTable[zeros] + tmp32; + } + + // Gate processing (lower gain during absence of speech) + zeros = (zeros << 9) - (frac >> 3); + // find number of leading zeros + zeros_fast = WebRtcSpl_NormU32((uint32_t)stt->capacitorFast); + if (stt->capacitorFast == 0) { + zeros_fast = 31; + } + tmp32 = ((uint32_t)stt->capacitorFast << zeros_fast) & 0x7FFFFFFF; + zeros_fast <<= 9; + zeros_fast -= (int16_t)(tmp32 >> 22); + + gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm; + + if (gate < 0) { + stt->gatePrevious = 0; + } else { + tmp32 = stt->gatePrevious * 7; + gate = (int16_t)((gate + tmp32) >> 3); + stt->gatePrevious = gate; + } + // gate < 0 -> no gate + // gate > 2500 -> max gate + if (gate > 0) { + if (gate < 2500) { + gain_adj = (2500 - gate) >> 5; + } else { + gain_adj = 0; + } + for (k = 0; k < 10; k++) { + if ((gains[k + 1] - stt->gainTable[0]) > 8388608) { + // To prevent wraparound + tmp32 = (gains[k + 1] - stt->gainTable[0]) >> 8; + tmp32 *= 178 + gain_adj; + } else { + tmp32 = (gains[k + 1] - stt->gainTable[0]) * (178 + gain_adj); + tmp32 >>= 8; + } + gains[k + 1] = stt->gainTable[0] + tmp32; + } + } + + // Limit gain to avoid overload distortion + for (k = 0; k < 10; k++) { + // Find a shift of gains[k + 1] such that it can be squared without + // overflow, but at least by 10 bits. + zeros = 10; + if (gains[k + 1] > 47452159) { + zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]); + } + gain32 = (gains[k + 1] >> zeros) + 1; + gain32 *= gain32; + // check for overflow + while (AGC_MUL32((env[k] >> 12) + 1, gain32) > + WEBRTC_SPL_SHIFT_W32((int32_t)32767, 2 * (1 - zeros + 10))) { + // multiply by 253/256 ==> -0.1 dB + if (gains[k + 1] > 8388607) { + // Prevent wrap around + gains[k + 1] = (gains[k + 1] / 256) * 253; + } else { + gains[k + 1] = (gains[k + 1] * 253) / 256; + } + gain32 = (gains[k + 1] >> zeros) + 1; + gain32 *= gain32; + } + } + // gain reductions should be done 1 ms earlier than gain increases + for (k = 1; k < 10; k++) { + if (gains[k] > gains[k + 1]) { + gains[k] = gains[k + 1]; + } + } + // save start gain for next frame + stt->gain = gains[10]; + + return 0; +} + +int32_t WebRtcAgc_ApplyDigitalGains(const int32_t gains[11], + size_t num_bands, + uint32_t FS, + const int16_t* const* in_near, + int16_t* const* out) { + // Apply gain + // handle first sub frame separately + size_t L; + int16_t L2; // samples/subframe + + // determine number of samples per ms + if (FS == 8000) { + L = 8; + L2 = 3; + } else if (FS == 16000 || FS == 32000 || FS == 48000) { + L = 16; + L2 = 4; + } else { + return -1; + } + + for (size_t i = 0; i < num_bands; ++i) { + if (in_near[i] != out[i]) { + // Only needed if they don't already point to the same place. + memcpy(out[i], in_near[i], 10 * L * sizeof(in_near[i][0])); + } + } + + // iterate over samples + int32_t delta = (gains[1] - gains[0]) * (1 << (4 - L2)); + int32_t gain32 = gains[0] * (1 << 4); + for (size_t n = 0; n < L; n++) { + for (size_t i = 0; i < num_bands; ++i) { + int32_t out_tmp = (int64_t)out[i][n] * ((gain32 + 127) >> 7) >> 16; + if (out_tmp > 4095) { + out[i][n] = (int16_t)32767; + } else if (out_tmp < -4096) { + out[i][n] = (int16_t)-32768; + } else { + int32_t tmp32 = ((int64_t)out[i][n] * (gain32 >> 4)) >> 16; + out[i][n] = (int16_t)tmp32; + } + } + + gain32 += delta; + } + // iterate over subframes + for (int k = 1; k < 10; k++) { + delta = (gains[k + 1] - gains[k]) * (1 << (4 - L2)); + gain32 = gains[k] * (1 << 4); + // iterate over samples + for (size_t n = 0; n < L; n++) { + for (size_t i = 0; i < num_bands; ++i) { + int64_t tmp64 = ((int64_t)(out[i][k * L + n])) * (gain32 >> 4); + tmp64 = tmp64 >> 16; + if (tmp64 > 32767) { + out[i][k * L + n] = 32767; + } else if (tmp64 < -32768) { + out[i][k * L + n] = -32768; + } else { + out[i][k * L + n] = (int16_t)(tmp64); + } + } + gain32 += delta; + } + } + return 0; +} + +void WebRtcAgc_InitVad(AgcVad* state) { + int16_t k; + + state->HPstate = 0; // state of high pass filter + state->logRatio = 0; // log( P(active) / P(inactive) ) + // average input level (Q10) + state->meanLongTerm = 15 << 10; + + // variance of input level (Q8) + state->varianceLongTerm = 500 << 8; + + state->stdLongTerm = 0; // standard deviation of input level in dB + // short-term average input level (Q10) + state->meanShortTerm = 15 << 10; + + // short-term variance of input level (Q8) + state->varianceShortTerm = 500 << 8; + + state->stdShortTerm = + 0; // short-term standard deviation of input level in dB + state->counter = 3; // counts updates + for (k = 0; k < 8; k++) { + // downsampling filter + state->downState[k] = 0; + } +} + +int16_t WebRtcAgc_ProcessVad(AgcVad* state, // (i) VAD state + const int16_t* in, // (i) Speech signal + size_t nrSamples) { // (i) number of samples + uint32_t nrg; + int32_t out, tmp32, tmp32b; + uint16_t tmpU16; + int16_t k, subfr, tmp16; + int16_t buf1[8]; + int16_t buf2[4]; + int16_t HPstate; + int16_t zeros, dB; + int64_t tmp64; + + // process in 10 sub frames of 1 ms (to save on memory) + nrg = 0; + HPstate = state->HPstate; + for (subfr = 0; subfr < 10; subfr++) { + // downsample to 4 kHz + if (nrSamples == 160) { + for (k = 0; k < 8; k++) { + tmp32 = (int32_t)in[2 * k] + (int32_t)in[2 * k + 1]; + tmp32 >>= 1; + buf1[k] = (int16_t)tmp32; + } + in += 16; + + WebRtcSpl_DownsampleBy2(buf1, 8, buf2, state->downState); + } else { + WebRtcSpl_DownsampleBy2(in, 8, buf2, state->downState); + in += 8; + } + + // high pass filter and compute energy + for (k = 0; k < 4; k++) { + out = buf2[k] + HPstate; + tmp32 = 600 * out; + HPstate = (int16_t)((tmp32 >> 10) - buf2[k]); + + // Add 'out * out / 2**6' to 'nrg' in a non-overflowing + // way. Guaranteed to work as long as 'out * out / 2**6' fits in + // an int32_t. + nrg += out * (out / (1 << 6)); + nrg += out * (out % (1 << 6)) / (1 << 6); + } + } + state->HPstate = HPstate; + + // find number of leading zeros + if (!(0xFFFF0000 & nrg)) { + zeros = 16; + } else { + zeros = 0; + } + if (!(0xFF000000 & (nrg << zeros))) { + zeros += 8; + } + if (!(0xF0000000 & (nrg << zeros))) { + zeros += 4; + } + if (!(0xC0000000 & (nrg << zeros))) { + zeros += 2; + } + if (!(0x80000000 & (nrg << zeros))) { + zeros += 1; + } + + // energy level (range {-32..30}) (Q10) + dB = (15 - zeros) * (1 << 11); + + // Update statistics + + if (state->counter < kAvgDecayTime) { + // decay time = AvgDecTime * 10 ms + state->counter++; + } + + // update short-term estimate of mean energy level (Q10) + tmp32 = state->meanShortTerm * 15 + dB; + state->meanShortTerm = (int16_t)(tmp32 >> 4); + + // update short-term estimate of variance in energy level (Q8) + tmp32 = (dB * dB) >> 12; + tmp32 += state->varianceShortTerm * 15; + state->varianceShortTerm = tmp32 / 16; + + // update short-term estimate of standard deviation in energy level (Q10) + tmp32 = state->meanShortTerm * state->meanShortTerm; + tmp32 = (state->varianceShortTerm << 12) - tmp32; + state->stdShortTerm = (int16_t)WebRtcSpl_Sqrt(tmp32); + + // update long-term estimate of mean energy level (Q10) + tmp32 = state->meanLongTerm * state->counter + dB; + state->meanLongTerm = + WebRtcSpl_DivW32W16ResW16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1)); + + // update long-term estimate of variance in energy level (Q8) + tmp32 = (dB * dB) >> 12; + tmp32 += state->varianceLongTerm * state->counter; + state->varianceLongTerm = + WebRtcSpl_DivW32W16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1)); + + // update long-term estimate of standard deviation in energy level (Q10) + tmp32 = state->meanLongTerm * state->meanLongTerm; + tmp32 = (state->varianceLongTerm << 12) - tmp32; + state->stdLongTerm = (int16_t)WebRtcSpl_Sqrt(tmp32); + + // update voice activity measure (Q10) + tmp16 = 3 << 12; + // TODO(bjornv): (dB - state->meanLongTerm) can overflow, e.g., in + // ApmTest.Process unit test. Previously the macro WEBRTC_SPL_MUL_16_16() + // was used, which did an intermediate cast to (int16_t), hence losing + // significant bits. This cause logRatio to max out positive, rather than + // negative. This is a bug, but has very little significance. + tmp32 = tmp16 * (int16_t)(dB - state->meanLongTerm); + tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm); + tmpU16 = (13 << 12); + tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16); + tmp64 = tmp32; + tmp64 += tmp32b >> 10; + tmp64 >>= 6; + + // limit + if (tmp64 > 2048) { + tmp64 = 2048; + } else if (tmp64 < -2048) { + tmp64 = -2048; + } + state->logRatio = (int16_t)tmp64; + + return state->logRatio; // Q10 +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/legacy/digital_agc.h b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/digital_agc.h new file mode 100644 index 00000000..223c74b9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/digital_agc.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC_LEGACY_DIGITAL_AGC_H_ +#define MODULES_AUDIO_PROCESSING_AGC_LEGACY_DIGITAL_AGC_H_ + +#include "common_audio/signal_processing/include/signal_processing_library.h" + +namespace webrtc { + +typedef struct { + int32_t downState[8]; + int16_t HPstate; + int16_t counter; + int16_t logRatio; // log( P(active) / P(inactive) ) (Q10) + int16_t meanLongTerm; // Q10 + int32_t varianceLongTerm; // Q8 + int16_t stdLongTerm; // Q10 + int16_t meanShortTerm; // Q10 + int32_t varianceShortTerm; // Q8 + int16_t stdShortTerm; // Q10 +} AgcVad; // total = 54 bytes + +typedef struct { + int32_t capacitorSlow; + int32_t capacitorFast; + int32_t gain; + int32_t gainTable[32]; + int16_t gatePrevious; + int16_t agcMode; + AgcVad vadNearend; + AgcVad vadFarend; +} DigitalAgc; + +int32_t WebRtcAgc_InitDigital(DigitalAgc* digitalAgcInst, int16_t agcMode); + +int32_t WebRtcAgc_ComputeDigitalGains(DigitalAgc* digitalAgcInst, + const int16_t* const* inNear, + size_t num_bands, + uint32_t FS, + int16_t lowLevelSignal, + int32_t gains[11]); + +int32_t WebRtcAgc_ApplyDigitalGains(const int32_t gains[11], + size_t num_bands, + uint32_t FS, + const int16_t* const* in_near, + int16_t* const* out); + +int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc* digitalAgcInst, + const int16_t* inFar, + size_t nrSamples); + +void WebRtcAgc_InitVad(AgcVad* vadInst); + +int16_t WebRtcAgc_ProcessVad(AgcVad* vadInst, // (i) VAD state + const int16_t* in, // (i) Speech signal + size_t nrSamples); // (i) number of samples + +int32_t WebRtcAgc_CalculateGainTable(int32_t* gainTable, // Q16 + int16_t compressionGaindB, // Q0 (in dB) + int16_t targetLevelDbfs, // Q0 (in dB) + uint8_t limiterEnable, + int16_t analogTarget); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_LEGACY_DIGITAL_AGC_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/legacy/gain_control.h b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/gain_control.h new file mode 100644 index 00000000..6010a988 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/gain_control.h @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ +#define MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ + +#include +#include + +namespace webrtc { + +enum { + kAgcModeUnchanged, + kAgcModeAdaptiveAnalog, + kAgcModeAdaptiveDigital, + kAgcModeFixedDigital +}; + +enum { kAgcFalse = 0, kAgcTrue }; + +typedef struct { + int16_t targetLevelDbfs; // default 3 (-3 dBOv) + int16_t compressionGaindB; // default 9 dB + uint8_t limiterEnable; // default kAgcTrue (on) +} WebRtcAgcConfig; + +/* + * This function analyses the number of samples passed to + * farend and produces any error code that could arise. + * + * Input: + * - agcInst : AGC instance. + * - samples : Number of samples in input vector. + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error. + */ +int WebRtcAgc_GetAddFarendError(void* state, size_t samples); + +/* + * This function processes a 10 ms frame of far-end speech to determine + * if there is active speech. The length of the input speech vector must be + * given in samples (80 when FS=8000, and 160 when FS=16000, FS=32000 or + * FS=48000). + * + * Input: + * - agcInst : AGC instance. + * - inFar : Far-end input speech vector + * - samples : Number of samples in input vector + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_AddFarend(void* agcInst, const int16_t* inFar, size_t samples); + +/* + * This function processes a 10 ms frame of microphone speech to determine + * if there is active speech. The length of the input speech vector must be + * given in samples (80 when FS=8000, and 160 when FS=16000, FS=32000 or + * FS=48000). For very low input levels, the input signal is increased in level + * by multiplying and overwriting the samples in inMic[]. + * + * This function should be called before any further processing of the + * near-end microphone signal. + * + * Input: + * - agcInst : AGC instance. + * - inMic : Microphone input speech vector for each band + * - num_bands : Number of bands in input vector + * - samples : Number of samples in input vector + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_AddMic(void* agcInst, + int16_t* const* inMic, + size_t num_bands, + size_t samples); + +/* + * This function replaces the analog microphone with a virtual one. + * It is a digital gain applied to the input signal and is used in the + * agcAdaptiveDigital mode where no microphone level is adjustable. The length + * of the input speech vector must be given in samples (80 when FS=8000, and 160 + * when FS=16000, FS=32000 or FS=48000). + * + * Input: + * - agcInst : AGC instance. + * - inMic : Microphone input speech vector for each band + * - num_bands : Number of bands in input vector + * - samples : Number of samples in input vector + * - micLevelIn : Input level of microphone (static) + * + * Output: + * - inMic : Microphone output after processing (L band) + * - inMic_H : Microphone output after processing (H band) + * - micLevelOut : Adjusted microphone level after processing + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_VirtualMic(void* agcInst, + int16_t* const* inMic, + size_t num_bands, + size_t samples, + int32_t micLevelIn, + int32_t* micLevelOut); + +/* + * This function analyses a 10 ms frame and produces the analog and digital + * gains required to normalize the signal. The gain adjustments are done only + * during active periods of speech. The length of the speech vectors must be + * given in samples (80 when FS=8000, and 160 when FS=16000, FS=32000 or + * FS=48000). The echo parameter can be used to ensure the AGC will not adjust + * upward in the presence of echo. + * + * This function should be called after processing the near-end microphone + * signal, in any case after any echo cancellation. + * + * Input: + * - agcInst : AGC instance + * - inNear : Near-end input speech vector for each band + * - num_bands : Number of bands in input/output vector + * - samples : Number of samples in input/output vector + * - inMicLevel : Current microphone volume level + * - echo : Set to 0 if the signal passed to add_mic is + * almost certainly free of echo; otherwise set + * to 1. If you have no information regarding echo + * set to 0. + * + * Output: + * - outMicLevel : Adjusted microphone volume level + * - saturationWarning : A returned value of 1 indicates a saturation event + * has occurred and the volume cannot be further + * reduced. Otherwise will be set to 0. + * - gains : Vector of gains to apply for digital normalization + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_Analyze(void* agcInst, + const int16_t* const* inNear, + size_t num_bands, + size_t samples, + int32_t inMicLevel, + int32_t* outMicLevel, + int16_t echo, + uint8_t* saturationWarning, + int32_t gains[11]); + +/* + * This function processes a 10 ms frame by applying precomputed digital gains. + * + * Input: + * - agcInst : AGC instance + * - gains : Vector of gains to apply for digital normalization + * - in_near : Near-end input speech vector for each band + * - num_bands : Number of bands in input/output vector + * + * Output: + * - out : Gain-adjusted near-end speech vector + * : May be the same vector as the input. + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_Process(const void* agcInst, + const int32_t gains[11], + const int16_t* const* in_near, + size_t num_bands, + int16_t* const* out); + +/* + * This function sets the config parameters (targetLevelDbfs, + * compressionGaindB and limiterEnable). + * + * Input: + * - agcInst : AGC instance + * - config : config struct + * + * Output: + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_set_config(void* agcInst, WebRtcAgcConfig config); + +/* + * This function returns the config parameters (targetLevelDbfs, + * compressionGaindB and limiterEnable). + * + * Input: + * - agcInst : AGC instance + * + * Output: + * - config : config struct + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_get_config(void* agcInst, WebRtcAgcConfig* config); + +/* + * This function creates and returns an AGC instance, which will contain the + * state information for one (duplex) channel. + */ +void* WebRtcAgc_Create(void); + +/* + * This function frees the AGC instance created at the beginning. + * + * Input: + * - agcInst : AGC instance. + */ +void WebRtcAgc_Free(void* agcInst); + +/* + * This function initializes an AGC instance. + * + * Input: + * - agcInst : AGC instance. + * - minLevel : Minimum possible mic level + * - maxLevel : Maximum possible mic level + * - agcMode : 0 - Unchanged + * : 1 - Adaptive Analog Automatic Gain Control -3dBOv + * : 2 - Adaptive Digital Automatic Gain Control -3dBOv + * : 3 - Fixed Digital Gain 0dB + * - fs : Sampling frequency + * + * Return value : 0 - Ok + * -1 - Error + */ +int WebRtcAgc_Init(void* agcInst, + int32_t minLevel, + int32_t maxLevel, + int16_t agcMode, + uint32_t fs); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/legacy/legacy.go b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/legacy.go new file mode 100644 index 00000000..f6999b97 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/legacy/legacy.go @@ -0,0 +1,10 @@ +//go:build console + +package legacy + +// #cgo CXXFLAGS: -I${SRCDIR}/../../../.. -I${SRCDIR}/../../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/loudness_histogram.cc b/pkg/apm/webrtc/modules/audio_processing/agc/loudness_histogram.cc new file mode 100644 index 00000000..b0a1f53b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/loudness_histogram.cc @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc/loudness_histogram.h" + +#include + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +static const double kHistBinCenters[] = { + 7.59621091765857e-02, 9.02036021061016e-02, 1.07115112009343e-01, + 1.27197217770508e-01, 1.51044347572047e-01, 1.79362373905283e-01, + 2.12989507320644e-01, 2.52921107370304e-01, 3.00339145144454e-01, + 3.56647189489147e-01, 4.23511952494003e-01, 5.02912623991786e-01, + 5.97199455365749e-01, 7.09163326739184e-01, 8.42118356728544e-01, + 1.00000000000000e+00, 1.18748153630660e+00, 1.41011239906908e+00, + 1.67448243801153e+00, 1.98841697800836e+00, 2.36120844786349e+00, + 2.80389143520905e+00, 3.32956930911896e+00, 3.95380207843188e+00, + 4.69506696634852e+00, 5.57530533426190e+00, 6.62057214370769e+00, + 7.86180718043869e+00, 9.33575086877358e+00, 1.10860317842269e+01, + 1.31644580546776e+01, 1.56325508754123e+01, 1.85633655299256e+01, + 2.20436538184971e+01, 2.61764319021997e+01, 3.10840295702492e+01, + 3.69117111886792e+01, 4.38319755100383e+01, 5.20496616180135e+01, + 6.18080121423973e+01, 7.33958732149108e+01, 8.71562442838066e+01, + 1.03496430860848e+02, 1.22900100720889e+02, 1.45941600416277e+02, + 1.73302955873365e+02, 2.05794060286978e+02, 2.44376646872353e+02, + 2.90192756065437e+02, 3.44598539797631e+02, 4.09204403447902e+02, + 4.85922673669740e+02, 5.77024203055553e+02, 6.85205587130498e+02, + 8.13668983291589e+02, 9.66216894324125e+02, 1.14736472207740e+03, + 1.36247442287647e+03, 1.61791322085579e+03, 1.92124207711260e+03, + 2.28143949334655e+03, 2.70916727454970e+03, 3.21708611729384e+03, + 3.82023036499473e+03, 4.53645302286906e+03, 5.38695420497926e+03, + 6.39690865534207e+03, 7.59621091765857e+03, 9.02036021061016e+03, + 1.07115112009343e+04, 1.27197217770508e+04, 1.51044347572047e+04, + 1.79362373905283e+04, 2.12989507320644e+04, 2.52921107370304e+04, + 3.00339145144454e+04, 3.56647189489147e+04}; + +static const double kProbQDomain = 1024.0; +// Loudness of -15 dB (smallest expected loudness) in log domain, +// loudness_db = 13.5 * log10(rms); +static const double kLogDomainMinBinCenter = -2.57752062648587; +// Loudness step of 1 dB in log domain +static const double kLogDomainStepSizeInverse = 5.81954605750359; + +static const int kTransientWidthThreshold = 7; +static const double kLowProbabilityThreshold = 0.2; + +static const int kLowProbThresholdQ10 = + static_cast(kLowProbabilityThreshold * kProbQDomain); + +LoudnessHistogram::LoudnessHistogram() + : num_updates_(0), + audio_content_q10_(0), + bin_count_q10_(), + activity_probability_(), + hist_bin_index_(), + buffer_index_(0), + buffer_is_full_(false), + len_circular_buffer_(0), + len_high_activity_(0) { + static_assert( + kHistSize == sizeof(kHistBinCenters) / sizeof(kHistBinCenters[0]), + "histogram bin centers incorrect size"); +} + +LoudnessHistogram::LoudnessHistogram(int window_size) + : num_updates_(0), + audio_content_q10_(0), + bin_count_q10_(), + activity_probability_(new int[window_size]), + hist_bin_index_(new int[window_size]), + buffer_index_(0), + buffer_is_full_(false), + len_circular_buffer_(window_size), + len_high_activity_(0) {} + +LoudnessHistogram::~LoudnessHistogram() {} + +void LoudnessHistogram::Update(double rms, double activity_probaility) { + // If circular histogram is activated then remove the oldest entry. + if (len_circular_buffer_ > 0) + RemoveOldestEntryAndUpdate(); + + // Find the corresponding bin. + int hist_index = GetBinIndex(rms); + // To Q10 domain. + int prob_q10 = + static_cast(floor(activity_probaility * kProbQDomain)); + InsertNewestEntryAndUpdate(prob_q10, hist_index); +} + +// Doing nothing if buffer is not full, yet. +void LoudnessHistogram::RemoveOldestEntryAndUpdate() { + RTC_DCHECK_GT(len_circular_buffer_, 0); + // Do nothing if circular buffer is not full. + if (!buffer_is_full_) + return; + + int oldest_prob = activity_probability_[buffer_index_]; + int oldest_hist_index = hist_bin_index_[buffer_index_]; + UpdateHist(-oldest_prob, oldest_hist_index); +} + +void LoudnessHistogram::RemoveTransient() { + // Don't expect to be here if high-activity region is longer than + // `kTransientWidthThreshold` or there has not been any transient. + RTC_DCHECK_LE(len_high_activity_, kTransientWidthThreshold); + int index = + (buffer_index_ > 0) ? (buffer_index_ - 1) : len_circular_buffer_ - 1; + while (len_high_activity_ > 0) { + UpdateHist(-activity_probability_[index], hist_bin_index_[index]); + activity_probability_[index] = 0; + index = (index > 0) ? (index - 1) : (len_circular_buffer_ - 1); + len_high_activity_--; + } +} + +void LoudnessHistogram::InsertNewestEntryAndUpdate(int activity_prob_q10, + int hist_index) { + // Update the circular buffer if it is enabled. + if (len_circular_buffer_ > 0) { + // Removing transient. + if (activity_prob_q10 <= kLowProbThresholdQ10) { + // Lower than threshold probability, set it to zero. + activity_prob_q10 = 0; + // Check if this has been a transient. + if (len_high_activity_ <= kTransientWidthThreshold) + RemoveTransient(); // Remove this transient. + len_high_activity_ = 0; + } else if (len_high_activity_ <= kTransientWidthThreshold) { + len_high_activity_++; + } + // Updating the circular buffer. + activity_probability_[buffer_index_] = activity_prob_q10; + hist_bin_index_[buffer_index_] = hist_index; + // Increment the buffer index and check for wrap-around. + buffer_index_++; + if (buffer_index_ >= len_circular_buffer_) { + buffer_index_ = 0; + buffer_is_full_ = true; + } + } + + num_updates_++; + if (num_updates_ < 0) + num_updates_--; + + UpdateHist(activity_prob_q10, hist_index); +} + +void LoudnessHistogram::UpdateHist(int activity_prob_q10, int hist_index) { + bin_count_q10_[hist_index] += activity_prob_q10; + audio_content_q10_ += activity_prob_q10; +} + +double LoudnessHistogram::AudioContent() const { + return audio_content_q10_ / kProbQDomain; +} + +LoudnessHistogram* LoudnessHistogram::Create() { + return new LoudnessHistogram; +} + +LoudnessHistogram* LoudnessHistogram::Create(int window_size) { + if (window_size < 0) + return NULL; + return new LoudnessHistogram(window_size); +} + +void LoudnessHistogram::Reset() { + // Reset the histogram, audio-content and number of updates. + memset(bin_count_q10_, 0, sizeof(bin_count_q10_)); + audio_content_q10_ = 0; + num_updates_ = 0; + // Empty the circular buffer. + buffer_index_ = 0; + buffer_is_full_ = false; + len_high_activity_ = 0; +} + +int LoudnessHistogram::GetBinIndex(double rms) { + // First exclude overload cases. + if (rms <= kHistBinCenters[0]) { + return 0; + } else if (rms >= kHistBinCenters[kHistSize - 1]) { + return kHistSize - 1; + } else { + // The quantizer is uniform in log domain. Alternatively we could do binary + // search in linear domain. + double rms_log = log(rms); + + int index = static_cast( + floor((rms_log - kLogDomainMinBinCenter) * kLogDomainStepSizeInverse)); + // The final decision is in linear domain. + double b = 0.5 * (kHistBinCenters[index] + kHistBinCenters[index + 1]); + if (rms > b) { + return index + 1; + } + return index; + } +} + +double LoudnessHistogram::CurrentRms() const { + double p; + double mean_val = 0; + if (audio_content_q10_ > 0) { + double p_total_inverse = 1. / static_cast(audio_content_q10_); + for (int n = 0; n < kHistSize; n++) { + p = static_cast(bin_count_q10_[n]) * p_total_inverse; + mean_val += p * kHistBinCenters[n]; + } + } else { + mean_val = kHistBinCenters[0]; + } + return mean_val; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/loudness_histogram.h b/pkg/apm/webrtc/modules/audio_processing/agc/loudness_histogram.h new file mode 100644 index 00000000..51b38714 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/loudness_histogram.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC_LOUDNESS_HISTOGRAM_H_ +#define MODULES_AUDIO_PROCESSING_AGC_LOUDNESS_HISTOGRAM_H_ + +#include + +#include + +namespace webrtc { + +// This class implements the histogram of loudness with circular buffers so that +// the histogram tracks the last T seconds of the loudness. +class LoudnessHistogram { + public: + // Create a non-sliding LoudnessHistogram. + static LoudnessHistogram* Create(); + + // Create a sliding LoudnessHistogram, i.e. the histogram represents the last + // `window_size` samples. + static LoudnessHistogram* Create(int window_size); + ~LoudnessHistogram(); + + // Insert RMS and the corresponding activity probability. + void Update(double rms, double activity_probability); + + // Reset the histogram, forget the past. + void Reset(); + + // Current loudness, which is actually the mean of histogram in loudness + // domain. + double CurrentRms() const; + + // Sum of the histogram content. + double AudioContent() const; + + // Number of times the histogram has been updated. + int num_updates() const { return num_updates_; } + + private: + LoudnessHistogram(); + explicit LoudnessHistogram(int window); + + // Find the histogram bin associated with the given `rms`. + int GetBinIndex(double rms); + + void RemoveOldestEntryAndUpdate(); + void InsertNewestEntryAndUpdate(int activity_prob_q10, int hist_index); + void UpdateHist(int activity_prob_q10, int hist_index); + void RemoveTransient(); + + // Number of histogram bins. + static const int kHistSize = 77; + + // Number of times the histogram is updated + int num_updates_; + // Audio content, this should be equal to the sum of the components of + // `bin_count_q10_`. + int64_t audio_content_q10_; + + // LoudnessHistogram of input RMS in Q10 with `kHistSize_` bins. In each + // 'Update(),' we increment the associated histogram-bin with the given + // probability. The increment is implemented in Q10 to avoid rounding errors. + int64_t bin_count_q10_[kHistSize]; + + // Circular buffer for probabilities + std::unique_ptr activity_probability_; + // Circular buffer for histogram-indices of probabilities. + std::unique_ptr hist_bin_index_; + // Current index of circular buffer, where the newest data will be written to, + // therefore, pointing to the oldest data if buffer is full. + int buffer_index_; + // Indicating if buffer is full and we had a wrap around. + int buffer_is_full_; + // Size of circular buffer. + int len_circular_buffer_; + int len_high_activity_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_LOUDNESS_HISTOGRAM_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/utility.cc b/pkg/apm/webrtc/modules/audio_processing/agc/utility.cc new file mode 100644 index 00000000..2a87e5ce --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/utility.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc/utility.h" + +#include + +namespace webrtc { + +static const double kLog10 = 2.30258509299; +static const double kLinear2DbScale = 20.0 / kLog10; +static const double kLinear2LoudnessScale = 13.4 / kLog10; + +double Loudness2Db(double loudness) { + return loudness * kLinear2DbScale / kLinear2LoudnessScale; +} + +double Linear2Loudness(double rms) { + if (rms == 0) + return -15; + return kLinear2LoudnessScale * log(rms); +} + +double Db2Loudness(double db) { + return db * kLinear2LoudnessScale / kLinear2DbScale; +} + +double Dbfs2Loudness(double dbfs) { + return Db2Loudness(90 + dbfs); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc/utility.h b/pkg/apm/webrtc/modules/audio_processing/agc/utility.h new file mode 100644 index 00000000..56eec244 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc/utility.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC_UTILITY_H_ +#define MODULES_AUDIO_PROCESSING_AGC_UTILITY_H_ + +namespace webrtc { + +// TODO(turajs): Add description of function. +double Loudness2Db(double loudness); + +double Linear2Loudness(double rms); + +double Db2Loudness(double db); + +double Dbfs2Loudness(double dbfs); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_UTILITY_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller.cc new file mode 100644 index 00000000..8bbbedce --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller.cc @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/adaptive_digital_gain_controller.h" + +#include + +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { +namespace { + +using AdaptiveDigitalConfig = + AudioProcessing::Config::GainController2::AdaptiveDigital; + +constexpr int kHeadroomHistogramMin = 0; +constexpr int kHeadroomHistogramMax = 50; +constexpr int kGainDbHistogramMax = 30; + +// Computes the gain for `input_level_dbfs` to reach `-config.headroom_db`. +// Clamps the gain in [0, `config.max_gain_db`]. `config.headroom_db` is a +// safety margin to allow transient peaks to exceed the target peak level +// without clipping. +float ComputeGainDb(float input_level_dbfs, + const AdaptiveDigitalConfig& config) { + // If the level is very low, apply the maximum gain. + if (input_level_dbfs < -(config.headroom_db + config.max_gain_db)) { + return config.max_gain_db; + } + // We expect to end up here most of the time: the level is below + // -headroom, but we can boost it to -headroom. + if (input_level_dbfs < -config.headroom_db) { + return -config.headroom_db - input_level_dbfs; + } + // The level is too high and we can't boost. + RTC_DCHECK_GE(input_level_dbfs, -config.headroom_db); + return 0.0f; +} + +// Returns `target_gain_db` if applying such a gain to `input_noise_level_dbfs` +// does not exceed `max_output_noise_level_dbfs`. Otherwise lowers and returns +// `target_gain_db` so that the output noise level equals +// `max_output_noise_level_dbfs`. +float LimitGainByNoise(float target_gain_db, + float input_noise_level_dbfs, + float max_output_noise_level_dbfs, + ApmDataDumper& apm_data_dumper) { + const float max_allowed_gain_db = + max_output_noise_level_dbfs - input_noise_level_dbfs; + apm_data_dumper.DumpRaw("agc2_adaptive_gain_applier_max_allowed_gain_db", + max_allowed_gain_db); + return std::min(target_gain_db, std::max(max_allowed_gain_db, 0.0f)); +} + +float LimitGainByLowConfidence(float target_gain_db, + float last_gain_db, + float limiter_audio_level_dbfs, + bool estimate_is_confident) { + if (estimate_is_confident || + limiter_audio_level_dbfs <= kLimiterThresholdForAgcGainDbfs) { + return target_gain_db; + } + const float limiter_level_dbfs_before_gain = + limiter_audio_level_dbfs - last_gain_db; + + // Compute a new gain so that `limiter_level_dbfs_before_gain` + + // `new_target_gain_db` is not great than `kLimiterThresholdForAgcGainDbfs`. + const float new_target_gain_db = std::max( + kLimiterThresholdForAgcGainDbfs - limiter_level_dbfs_before_gain, 0.0f); + return std::min(new_target_gain_db, target_gain_db); +} + +// Computes how the gain should change during this frame. +// Return the gain difference in db to 'last_gain_db'. +float ComputeGainChangeThisFrameDb(float target_gain_db, + float last_gain_db, + bool gain_increase_allowed, + float max_gain_decrease_db, + float max_gain_increase_db) { + RTC_DCHECK_GT(max_gain_decrease_db, 0); + RTC_DCHECK_GT(max_gain_increase_db, 0); + float target_gain_difference_db = target_gain_db - last_gain_db; + if (!gain_increase_allowed) { + target_gain_difference_db = std::min(target_gain_difference_db, 0.0f); + } + return SafeClamp(target_gain_difference_db, -max_gain_decrease_db, + max_gain_increase_db); +} + +} // namespace + +AdaptiveDigitalGainController::AdaptiveDigitalGainController( + ApmDataDumper* apm_data_dumper, + const AudioProcessing::Config::GainController2::AdaptiveDigital& config, + int adjacent_speech_frames_threshold) + : apm_data_dumper_(apm_data_dumper), + gain_applier_( + /*hard_clip_samples=*/false, + /*initial_gain_factor=*/DbToRatio(config.initial_gain_db)), + config_(config), + adjacent_speech_frames_threshold_(adjacent_speech_frames_threshold), + max_gain_change_db_per_10ms_(config_.max_gain_change_db_per_second * + kFrameDurationMs / 1000.0f), + calls_since_last_gain_log_(0), + frames_to_gain_increase_allowed_(adjacent_speech_frames_threshold), + last_gain_db_(config_.initial_gain_db) { + RTC_DCHECK_GT(max_gain_change_db_per_10ms_, 0.0f); + RTC_DCHECK_GE(frames_to_gain_increase_allowed_, 1); + RTC_DCHECK_GE(config_.max_output_noise_level_dbfs, -90.0f); + RTC_DCHECK_LE(config_.max_output_noise_level_dbfs, 0.0f); +} + +void AdaptiveDigitalGainController::Process(const FrameInfo& info, + DeinterleavedView frame) { + RTC_DCHECK_GE(info.speech_level_dbfs, -150.0f); + RTC_DCHECK_GE(frame.num_channels(), 1); + RTC_DCHECK( + frame.samples_per_channel() == 80 || frame.samples_per_channel() == 160 || + frame.samples_per_channel() == 320 || frame.samples_per_channel() == 480) + << "`frame` does not look like a 10 ms frame for an APM supported sample " + "rate"; + + // Compute the input level used to select the desired gain. + RTC_DCHECK_GT(info.headroom_db, 0.0f); + const float input_level_dbfs = info.speech_level_dbfs + info.headroom_db; + + const float target_gain_db = LimitGainByLowConfidence( + LimitGainByNoise(ComputeGainDb(input_level_dbfs, config_), + info.noise_rms_dbfs, config_.max_output_noise_level_dbfs, + *apm_data_dumper_), + last_gain_db_, info.limiter_envelope_dbfs, info.speech_level_reliable); + + // Forbid increasing the gain until enough adjacent speech frames are + // observed. + bool first_confident_speech_frame = false; + if (info.speech_probability < kVadConfidenceThreshold) { + frames_to_gain_increase_allowed_ = adjacent_speech_frames_threshold_; + } else if (frames_to_gain_increase_allowed_ > 0) { + frames_to_gain_increase_allowed_--; + first_confident_speech_frame = frames_to_gain_increase_allowed_ == 0; + } + apm_data_dumper_->DumpRaw( + "agc2_adaptive_gain_applier_frames_to_gain_increase_allowed", + frames_to_gain_increase_allowed_); + + const bool gain_increase_allowed = frames_to_gain_increase_allowed_ == 0; + + float max_gain_increase_db = max_gain_change_db_per_10ms_; + if (first_confident_speech_frame) { + // No gain increase happened while waiting for a long enough speech + // sequence. Therefore, temporarily allow a faster gain increase. + RTC_DCHECK(gain_increase_allowed); + max_gain_increase_db *= adjacent_speech_frames_threshold_; + } + + const float gain_change_this_frame_db = ComputeGainChangeThisFrameDb( + target_gain_db, last_gain_db_, gain_increase_allowed, + /*max_gain_decrease_db=*/max_gain_change_db_per_10ms_, + max_gain_increase_db); + + apm_data_dumper_->DumpRaw("agc2_adaptive_gain_applier_want_to_change_by_db", + target_gain_db - last_gain_db_); + apm_data_dumper_->DumpRaw("agc2_adaptive_gain_applier_will_change_by_db", + gain_change_this_frame_db); + + // Optimization: avoid calling math functions if gain does not + // change. + if (gain_change_this_frame_db != 0.f) { + gain_applier_.SetGainFactor( + DbToRatio(last_gain_db_ + gain_change_this_frame_db)); + } + + gain_applier_.ApplyGain(frame); + + // Remember that the gain has changed for the next iteration. + last_gain_db_ = last_gain_db_ + gain_change_this_frame_db; + apm_data_dumper_->DumpRaw("agc2_adaptive_gain_applier_applied_gain_db", + last_gain_db_); + + // Log every 10 seconds. + calls_since_last_gain_log_++; + if (calls_since_last_gain_log_ == 1000) { + calls_since_last_gain_log_ = 0; + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc2.EstimatedSpeechLevel", + -info.speech_level_dbfs, 0, 100, 101); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc2.EstimatedNoiseLevel", + -info.noise_rms_dbfs, 0, 100, 101); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.Agc2.Headroom", info.headroom_db, kHeadroomHistogramMin, + kHeadroomHistogramMax, + kHeadroomHistogramMax - kHeadroomHistogramMin + 1); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc2.DigitalGainApplied", + last_gain_db_, 0, kGainDbHistogramMax, + kGainDbHistogramMax + 1); + RTC_LOG(LS_INFO) << "AGC2 adaptive digital" + << " | speech_dbfs: " << info.speech_level_dbfs + << " | noise_dbfs: " << info.noise_rms_dbfs + << " | headroom_db: " << info.headroom_db + << " | gain_db: " << last_gain_db_; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller.h b/pkg/apm/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller.h new file mode 100644 index 00000000..d464dc6b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_CONTROLLER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_CONTROLLER_H_ + +#include + +#include "api/audio/audio_processing.h" +#include "api/audio/audio_view.h" +#include "modules/audio_processing/agc2/gain_applier.h" + +namespace webrtc { + +class ApmDataDumper; + +// Selects the target digital gain, decides when and how quickly to adapt to the +// target and applies the current gain to 10 ms frames. +class AdaptiveDigitalGainController { + public: + // Information about a frame to process. + struct FrameInfo { + float speech_probability; // Probability of speech in the [0, 1] range. + float speech_level_dbfs; // Estimated speech level (dBFS). + bool speech_level_reliable; // True with reliable speech level estimation. + float noise_rms_dbfs; // Estimated noise RMS level (dBFS). + float headroom_db; // Headroom (dB). + // TODO(bugs.webrtc.org/7494): Remove `limiter_envelope_dbfs`. + float limiter_envelope_dbfs; // Envelope level from the limiter (dBFS). + }; + + AdaptiveDigitalGainController( + ApmDataDumper* apm_data_dumper, + const AudioProcessing::Config::GainController2::AdaptiveDigital& config, + int adjacent_speech_frames_threshold); + AdaptiveDigitalGainController(const AdaptiveDigitalGainController&) = delete; + AdaptiveDigitalGainController& operator=( + const AdaptiveDigitalGainController&) = delete; + + // Analyzes `info`, updates the digital gain and applies it to a 10 ms + // `frame`. Supports any sample rate supported by APM. + void Process(const FrameInfo& info, DeinterleavedView frame); + + private: + ApmDataDumper* const apm_data_dumper_; + GainApplier gain_applier_; + + const AudioProcessing::Config::GainController2::AdaptiveDigital config_; + const int adjacent_speech_frames_threshold_; + const float max_gain_change_db_per_10ms_; + + int calls_since_last_gain_log_; + int frames_to_gain_increase_allowed_; + float last_gain_db_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_CONTROLLER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/agc2.go b/pkg/apm/webrtc/modules/audio_processing/agc2/agc2.go new file mode 100644 index 00000000..4a8c17ce --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/agc2.go @@ -0,0 +1,14 @@ +//go:build console + +package agc2 + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" + +import ( + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad" +) diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/agc2_common.h b/pkg/apm/webrtc/modules/audio_processing/agc2/agc2_common.h new file mode 100644 index 00000000..4597bcd0 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/agc2_common.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_AGC2_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_AGC2_COMMON_H_ + +namespace webrtc { + +constexpr float kMinFloatS16Value = -32768.0f; +constexpr float kMaxFloatS16Value = 32767.0f; +constexpr float kMaxAbsFloatS16Value = 32768.0f; + +// Minimum audio level in dBFS scale for S16 samples. +constexpr float kMinLevelDbfs = -90.31f; + +constexpr int kFrameDurationMs = 10; +constexpr int kSubFramesInFrame = 20; +constexpr int kMaximalNumberOfSamplesPerChannel = 480; + +// Adaptive digital gain applier settings. + +// At what limiter levels should we start decreasing the adaptive digital gain. +constexpr float kLimiterThresholdForAgcGainDbfs = -1.0f; + +// Number of milliseconds to wait to periodically reset the VAD. +constexpr int kVadResetPeriodMs = 1500; + +// Speech probability threshold to detect speech activity. +constexpr float kVadConfidenceThreshold = 0.95f; + +// Minimum number of adjacent speech frames having a sufficiently high speech +// probability to reliably detect speech activity. +constexpr int kAdjacentSpeechFramesThreshold = 12; + +// Number of milliseconds of speech frames to observe to make the estimator +// confident. +constexpr float kLevelEstimatorTimeToConfidenceMs = 400; +constexpr float kLevelEstimatorLeakFactor = + 1.0f - 1.0f / kLevelEstimatorTimeToConfidenceMs; + +// Saturation Protector settings. +constexpr float kSaturationProtectorInitialHeadroomDb = 20.0f; +constexpr int kSaturationProtectorBufferSize = 4; + +// Number of interpolation points for each region of the limiter. +// These values have been tuned to limit the interpolated gain curve error given +// the limiter parameters and allowing a maximum error of +/- 32768^-1. +constexpr int kInterpolatedGainCurveKneePoints = 22; +constexpr int kInterpolatedGainCurveBeyondKneePoints = 10; +constexpr int kInterpolatedGainCurveTotalPoints = + kInterpolatedGainCurveKneePoints + kInterpolatedGainCurveBeyondKneePoints; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_AGC2_COMMON_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/agc2_testing_common.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/agc2_testing_common.cc new file mode 100644 index 00000000..8f3e9db4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/agc2_testing_common.cc @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/agc2_testing_common.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace test { + +std::vector LinSpace(double l, double r, int num_points) { + RTC_CHECK_GE(num_points, 2); + std::vector points(num_points); + const double step = (r - l) / (num_points - 1.0); + points[0] = l; + for (int i = 1; i < num_points - 1; i++) { + points[i] = static_cast(l) + i * step; + } + points[num_points - 1] = r; + return points; +} + +WhiteNoiseGenerator::WhiteNoiseGenerator(int min_amplitude, int max_amplitude) + : rand_gen_(42), + min_amplitude_(min_amplitude), + max_amplitude_(max_amplitude) { + RTC_DCHECK_LT(min_amplitude_, max_amplitude_); + RTC_DCHECK_LE(kMinS16, min_amplitude_); + RTC_DCHECK_LE(min_amplitude_, kMaxS16); + RTC_DCHECK_LE(kMinS16, max_amplitude_); + RTC_DCHECK_LE(max_amplitude_, kMaxS16); +} + +float WhiteNoiseGenerator::operator()() { + return static_cast(rand_gen_.Rand(min_amplitude_, max_amplitude_)); +} + +SineGenerator::SineGenerator(float amplitude, + float frequency_hz, + int sample_rate_hz) + : amplitude_(amplitude), + frequency_hz_(frequency_hz), + sample_rate_hz_(sample_rate_hz), + x_radians_(0.0f) { + RTC_DCHECK_GT(amplitude_, 0); + RTC_DCHECK_LE(amplitude_, kMaxS16); +} + +float SineGenerator::operator()() { + constexpr float kPi = 3.1415926536f; + x_radians_ += frequency_hz_ / sample_rate_hz_ * 2 * kPi; + if (x_radians_ >= 2 * kPi) { + x_radians_ -= 2 * kPi; + } + // Use sinf instead of std::sinf for libstdc++ compatibility. + return amplitude_ * sinf(x_radians_); +} + +PulseGenerator::PulseGenerator(float pulse_amplitude, + float no_pulse_amplitude, + float frequency_hz, + int sample_rate_hz) + : pulse_amplitude_(pulse_amplitude), + no_pulse_amplitude_(no_pulse_amplitude), + samples_period_( + static_cast(static_cast(sample_rate_hz) / frequency_hz)), + sample_counter_(0) { + RTC_DCHECK_GE(pulse_amplitude_, kMinS16); + RTC_DCHECK_LE(pulse_amplitude_, kMaxS16); + RTC_DCHECK_GT(no_pulse_amplitude_, kMinS16); + RTC_DCHECK_LE(no_pulse_amplitude_, kMaxS16); + RTC_DCHECK_GT(sample_rate_hz, frequency_hz); +} + +float PulseGenerator::operator()() { + sample_counter_++; + if (sample_counter_ >= samples_period_) { + sample_counter_ -= samples_period_; + } + return static_cast(sample_counter_ == 0 ? pulse_amplitude_ + : no_pulse_amplitude_); +} + +} // namespace test +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/agc2_testing_common.h b/pkg/apm/webrtc/modules/audio_processing/agc2/agc2_testing_common.h new file mode 100644 index 00000000..afed97e8 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/agc2_testing_common.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_AGC2_TESTING_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_AGC2_TESTING_COMMON_H_ + +#include +#include + +#include "rtc_base/random.h" + +namespace webrtc { +namespace test { + +constexpr float kMinS16 = + static_cast(std::numeric_limits::min()); +constexpr float kMaxS16 = + static_cast(std::numeric_limits::max()); + +// Level Estimator test parameters. +constexpr float kDecayMs = 20.0f; + +// Limiter parameters. +constexpr float kLimiterMaxInputLevelDbFs = 1.f; +constexpr float kLimiterKneeSmoothnessDb = 1.f; +constexpr float kLimiterCompressionRatio = 5.f; + +// Returns evenly spaced `num_points` numbers over a specified interval [l, r]. +std::vector LinSpace(double l, double r, int num_points); + +// Generates white noise. +class WhiteNoiseGenerator { + public: + WhiteNoiseGenerator(int min_amplitude, int max_amplitude); + float operator()(); + + private: + Random rand_gen_; + const int min_amplitude_; + const int max_amplitude_; +}; + +// Generates a sine function. +class SineGenerator { + public: + SineGenerator(float amplitude, float frequency_hz, int sample_rate_hz); + float operator()(); + + private: + const float amplitude_; + const float frequency_hz_; + const int sample_rate_hz_; + float x_radians_; +}; + +// Generates periodic pulses. +class PulseGenerator { + public: + PulseGenerator(float pulse_amplitude, + float no_pulse_amplitude, + float frequency_hz, + int sample_rate_hz); + float operator()(); + + private: + const float pulse_amplitude_; + const float no_pulse_amplitude_; + const int samples_period_; + int sample_counter_; +}; + +} // namespace test +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_AGC2_TESTING_COMMON_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/biquad_filter.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/biquad_filter.cc new file mode 100644 index 00000000..e2e11290 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/biquad_filter.cc @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/biquad_filter.h" + +#include "rtc_base/arraysize.h" + +namespace webrtc { + +BiQuadFilter::BiQuadFilter(const Config& config) + : config_(config), state_({}) {} + +BiQuadFilter::~BiQuadFilter() = default; + +void BiQuadFilter::SetConfig(const Config& config) { + config_ = config; + state_ = {}; +} + +void BiQuadFilter::Reset() { + state_ = {}; +} + +void BiQuadFilter::Process(ArrayView x, ArrayView y) { + RTC_DCHECK_EQ(x.size(), y.size()); + const float config_a0 = config_.a[0]; + const float config_a1 = config_.a[1]; + const float config_b0 = config_.b[0]; + const float config_b1 = config_.b[1]; + const float config_b2 = config_.b[2]; + float state_a0 = state_.a[0]; + float state_a1 = state_.a[1]; + float state_b0 = state_.b[0]; + float state_b1 = state_.b[1]; + for (size_t k = 0, x_size = x.size(); k < x_size; ++k) { + // Use a temporary variable for `x[k]` to allow in-place processing. + const float tmp = x[k]; + float y_k = config_b0 * tmp + config_b1 * state_b0 + config_b2 * state_b1 - + config_a0 * state_a0 - config_a1 * state_a1; + state_b1 = state_b0; + state_b0 = tmp; + state_a1 = state_a0; + state_a0 = y_k; + y[k] = y_k; + } + state_.a[0] = state_a0; + state_.a[1] = state_a1; + state_.b[0] = state_b0; + state_.b[1] = state_b1; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/biquad_filter.h b/pkg/apm/webrtc/modules/audio_processing/agc2/biquad_filter.h new file mode 100644 index 00000000..766a750e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/biquad_filter.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_ + +#include "api/array_view.h" + +namespace webrtc { + +// Transposed direct form I implementation of a bi-quad filter. +// b[0] + b[1] • z^(-1) + b[2] • z^(-2) +// H(z) = ------------------------------------ +// 1 + a[1] • z^(-1) + a[2] • z^(-2) +class BiQuadFilter { + public: + // Normalized filter coefficients. + // Computed as `[b, a] = scipy.signal.butter(N=2, Wn, btype)`. + struct Config { + float b[3]; // b[0], b[1], b[2]. + float a[2]; // a[1], a[2]. + }; + + explicit BiQuadFilter(const Config& config); + BiQuadFilter(const BiQuadFilter&) = delete; + BiQuadFilter& operator=(const BiQuadFilter&) = delete; + ~BiQuadFilter(); + + // Sets the filter configuration and resets the internal state. + void SetConfig(const Config& config); + + // Zeroes the filter state. + void Reset(); + + // Filters `x` and writes the output in `y`, which must have the same length + // of `x`. In-place processing is supported. + void Process(ArrayView x, ArrayView y); + + private: + Config config_; + struct State { + float b[2]; + float a[2]; + } state_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor.cc new file mode 100644 index 00000000..77f928d2 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor.cc @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/clipping_predictor.h" + +#include +#include + +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/agc2/clipping_predictor_level_buffer.h" +#include "modules/audio_processing/agc2/gain_map_internal.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +constexpr int kClippingPredictorMaxGainChange = 15; + +// Returns an input volume in the [`min_input_volume`, `max_input_volume`] range +// that reduces `gain_error_db`, which is a gain error estimated when +// `input_volume` was applied, according to a fixed gain map. +int ComputeVolumeUpdate(int gain_error_db, + int input_volume, + int min_input_volume, + int max_input_volume) { + RTC_DCHECK_GE(input_volume, 0); + RTC_DCHECK_LE(input_volume, max_input_volume); + if (gain_error_db == 0) { + return input_volume; + } + int new_volume = input_volume; + if (gain_error_db > 0) { + while (kGainMap[new_volume] - kGainMap[input_volume] < gain_error_db && + new_volume < max_input_volume) { + ++new_volume; + } + } else { + while (kGainMap[new_volume] - kGainMap[input_volume] > gain_error_db && + new_volume > min_input_volume) { + --new_volume; + } + } + return new_volume; +} + +float ComputeCrestFactor(const ClippingPredictorLevelBuffer::Level& level) { + const float crest_factor = + FloatS16ToDbfs(level.max) - FloatS16ToDbfs(std::sqrt(level.average)); + return crest_factor; +} + +// Crest factor-based clipping prediction and clipped level step estimation. +class ClippingEventPredictor : public ClippingPredictor { + public: + // ClippingEventPredictor with `num_channels` channels (limited to values + // higher than zero); window size `window_length` and reference window size + // `reference_window_length` (both referring to the number of frames in the + // respective sliding windows and limited to values higher than zero); + // reference window delay `reference_window_delay` (delay in frames, limited + // to values zero and higher with an additional requirement of + // `window_length` < `reference_window_length` + reference_window_delay`); + // and an estimation peak threshold `clipping_threshold` and a crest factor + // drop threshold `crest_factor_margin` (both in dB). + ClippingEventPredictor(int num_channels, + int window_length, + int reference_window_length, + int reference_window_delay, + float clipping_threshold, + float crest_factor_margin) + : window_length_(window_length), + reference_window_length_(reference_window_length), + reference_window_delay_(reference_window_delay), + clipping_threshold_(clipping_threshold), + crest_factor_margin_(crest_factor_margin) { + RTC_DCHECK_GT(num_channels, 0); + RTC_DCHECK_GT(window_length, 0); + RTC_DCHECK_GT(reference_window_length, 0); + RTC_DCHECK_GE(reference_window_delay, 0); + RTC_DCHECK_GT(reference_window_length + reference_window_delay, + window_length); + const int buffer_length = GetMinFramesProcessed(); + RTC_DCHECK_GT(buffer_length, 0); + for (int i = 0; i < num_channels; ++i) { + ch_buffers_.push_back( + std::make_unique(buffer_length)); + } + } + + ClippingEventPredictor(const ClippingEventPredictor&) = delete; + ClippingEventPredictor& operator=(const ClippingEventPredictor&) = delete; + ~ClippingEventPredictor() {} + + void Reset() { + const int num_channels = ch_buffers_.size(); + for (int i = 0; i < num_channels; ++i) { + ch_buffers_[i]->Reset(); + } + } + + // Analyzes a frame of audio and stores the framewise metrics in + // `ch_buffers_`. + void Analyze(const AudioFrameView& frame) { + const int num_channels = frame.num_channels(); + RTC_DCHECK_EQ(num_channels, ch_buffers_.size()); + const int samples_per_channel = frame.samples_per_channel(); + RTC_DCHECK_GT(samples_per_channel, 0); + for (int channel = 0; channel < num_channels; ++channel) { + float sum_squares = 0.0f; + float peak = 0.0f; + for (const auto& sample : frame.channel(channel)) { + sum_squares += sample * sample; + peak = std::max(std::fabs(sample), peak); + } + ch_buffers_[channel]->Push( + {sum_squares / static_cast(samples_per_channel), peak}); + } + } + + // Estimates the analog gain adjustment for channel `channel` using a + // sliding window over the frame-wise metrics in `ch_buffers_`. Returns an + // estimate for the clipped level step equal to `default_clipped_level_step_` + // if at least `GetMinFramesProcessed()` frames have been processed since the + // last reset and a clipping event is predicted. `level`, `min_mic_level`, and + // `max_mic_level` are limited to [0, 255] and `default_step` to [1, 255]. + std::optional EstimateClippedLevelStep(int channel, + int level, + int default_step, + int min_mic_level, + int max_mic_level) const { + RTC_CHECK_GE(channel, 0); + RTC_CHECK_LT(channel, ch_buffers_.size()); + RTC_DCHECK_GE(level, 0); + RTC_DCHECK_LE(level, 255); + RTC_DCHECK_GT(default_step, 0); + RTC_DCHECK_LE(default_step, 255); + RTC_DCHECK_GE(min_mic_level, 0); + RTC_DCHECK_LE(min_mic_level, 255); + RTC_DCHECK_GE(max_mic_level, 0); + RTC_DCHECK_LE(max_mic_level, 255); + if (level <= min_mic_level) { + return std::nullopt; + } + if (PredictClippingEvent(channel)) { + const int new_level = + SafeClamp(level - default_step, min_mic_level, max_mic_level); + const int step = level - new_level; + if (step > 0) { + return step; + } + } + return std::nullopt; + } + + private: + int GetMinFramesProcessed() const { + return reference_window_delay_ + reference_window_length_; + } + + // Predicts clipping events based on the processed audio frames. Returns + // true if a clipping event is likely. + bool PredictClippingEvent(int channel) const { + const auto metrics = + ch_buffers_[channel]->ComputePartialMetrics(0, window_length_); + if (!metrics.has_value() || + !(FloatS16ToDbfs(metrics.value().max) > clipping_threshold_)) { + return false; + } + const auto reference_metrics = ch_buffers_[channel]->ComputePartialMetrics( + reference_window_delay_, reference_window_length_); + if (!reference_metrics.has_value()) { + return false; + } + const float crest_factor = ComputeCrestFactor(metrics.value()); + const float reference_crest_factor = + ComputeCrestFactor(reference_metrics.value()); + if (crest_factor < reference_crest_factor - crest_factor_margin_) { + return true; + } + return false; + } + + std::vector> ch_buffers_; + const int window_length_; + const int reference_window_length_; + const int reference_window_delay_; + const float clipping_threshold_; + const float crest_factor_margin_; +}; + +// Performs crest factor-based clipping peak prediction. +class ClippingPeakPredictor : public ClippingPredictor { + public: + // Ctor. ClippingPeakPredictor with `num_channels` channels (limited to values + // higher than zero); window size `window_length` and reference window size + // `reference_window_length` (both referring to the number of frames in the + // respective sliding windows and limited to values higher than zero); + // reference window delay `reference_window_delay` (delay in frames, limited + // to values zero and higher with an additional requirement of + // `window_length` < `reference_window_length` + reference_window_delay`); + // and a clipping prediction threshold `clipping_threshold` (in dB). Adaptive + // clipped level step estimation is used if `adaptive_step_estimation` is + // true. + explicit ClippingPeakPredictor(int num_channels, + int window_length, + int reference_window_length, + int reference_window_delay, + int clipping_threshold, + bool adaptive_step_estimation) + : window_length_(window_length), + reference_window_length_(reference_window_length), + reference_window_delay_(reference_window_delay), + clipping_threshold_(clipping_threshold), + adaptive_step_estimation_(adaptive_step_estimation) { + RTC_DCHECK_GT(num_channels, 0); + RTC_DCHECK_GT(window_length, 0); + RTC_DCHECK_GT(reference_window_length, 0); + RTC_DCHECK_GE(reference_window_delay, 0); + RTC_DCHECK_GT(reference_window_length + reference_window_delay, + window_length); + const int buffer_length = GetMinFramesProcessed(); + RTC_DCHECK_GT(buffer_length, 0); + for (int i = 0; i < num_channels; ++i) { + ch_buffers_.push_back( + std::make_unique(buffer_length)); + } + } + + ClippingPeakPredictor(const ClippingPeakPredictor&) = delete; + ClippingPeakPredictor& operator=(const ClippingPeakPredictor&) = delete; + ~ClippingPeakPredictor() {} + + void Reset() { + const int num_channels = ch_buffers_.size(); + for (int i = 0; i < num_channels; ++i) { + ch_buffers_[i]->Reset(); + } + } + + // Analyzes a frame of audio and stores the framewise metrics in + // `ch_buffers_`. + void Analyze(const AudioFrameView& frame) { + const int num_channels = frame.num_channels(); + RTC_DCHECK_EQ(num_channels, ch_buffers_.size()); + const int samples_per_channel = frame.samples_per_channel(); + RTC_DCHECK_GT(samples_per_channel, 0); + for (int channel = 0; channel < num_channels; ++channel) { + float sum_squares = 0.0f; + float peak = 0.0f; + for (const auto& sample : frame.channel(channel)) { + sum_squares += sample * sample; + peak = std::max(std::fabs(sample), peak); + } + ch_buffers_[channel]->Push( + {sum_squares / static_cast(samples_per_channel), peak}); + } + } + + // Estimates the analog gain adjustment for channel `channel` using a + // sliding window over the frame-wise metrics in `ch_buffers_`. Returns an + // estimate for the clipped level step (equal to + // `default_clipped_level_step_` if `adaptive_estimation_` is false) if at + // least `GetMinFramesProcessed()` frames have been processed since the last + // reset and a clipping event is predicted. `level`, `min_mic_level`, and + // `max_mic_level` are limited to [0, 255] and `default_step` to [1, 255]. + std::optional EstimateClippedLevelStep(int channel, + int level, + int default_step, + int min_mic_level, + int max_mic_level) const { + RTC_DCHECK_GE(channel, 0); + RTC_DCHECK_LT(channel, ch_buffers_.size()); + RTC_DCHECK_GE(level, 0); + RTC_DCHECK_LE(level, 255); + RTC_DCHECK_GT(default_step, 0); + RTC_DCHECK_LE(default_step, 255); + RTC_DCHECK_GE(min_mic_level, 0); + RTC_DCHECK_LE(min_mic_level, 255); + RTC_DCHECK_GE(max_mic_level, 0); + RTC_DCHECK_LE(max_mic_level, 255); + if (level <= min_mic_level) { + return std::nullopt; + } + std::optional estimate_db = EstimatePeakValue(channel); + if (estimate_db.has_value() && estimate_db.value() > clipping_threshold_) { + int step = 0; + if (!adaptive_step_estimation_) { + step = default_step; + } else { + const int estimated_gain_change = + SafeClamp(-static_cast(std::ceil(estimate_db.value())), + -kClippingPredictorMaxGainChange, 0); + step = + std::max(level - ComputeVolumeUpdate(estimated_gain_change, level, + min_mic_level, max_mic_level), + default_step); + } + const int new_level = + SafeClamp(level - step, min_mic_level, max_mic_level); + if (level > new_level) { + return level - new_level; + } + } + return std::nullopt; + } + + private: + int GetMinFramesProcessed() { + return reference_window_delay_ + reference_window_length_; + } + + // Predicts clipping sample peaks based on the processed audio frames. + // Returns the estimated peak value if clipping is predicted. Otherwise + // returns std::nullopt. + std::optional EstimatePeakValue(int channel) const { + const auto reference_metrics = ch_buffers_[channel]->ComputePartialMetrics( + reference_window_delay_, reference_window_length_); + if (!reference_metrics.has_value()) { + return std::nullopt; + } + const auto metrics = + ch_buffers_[channel]->ComputePartialMetrics(0, window_length_); + if (!metrics.has_value() || + !(FloatS16ToDbfs(metrics.value().max) > clipping_threshold_)) { + return std::nullopt; + } + const float reference_crest_factor = + ComputeCrestFactor(reference_metrics.value()); + const float& mean_squares = metrics.value().average; + const float projected_peak = + reference_crest_factor + FloatS16ToDbfs(std::sqrt(mean_squares)); + return projected_peak; + } + + std::vector> ch_buffers_; + const int window_length_; + const int reference_window_length_; + const int reference_window_delay_; + const int clipping_threshold_; + const bool adaptive_step_estimation_; +}; + +} // namespace + +std::unique_ptr CreateClippingPredictor( + int num_channels, + const AudioProcessing::Config::GainController1::AnalogGainController:: + ClippingPredictor& config) { + if (!config.enabled) { + RTC_LOG(LS_INFO) << "[AGC2] Clipping prediction disabled."; + return nullptr; + } + RTC_LOG(LS_INFO) << "[AGC2] Clipping prediction enabled."; + using ClippingPredictorMode = AudioProcessing::Config::GainController1:: + AnalogGainController::ClippingPredictor::Mode; + switch (config.mode) { + case ClippingPredictorMode::kClippingEventPrediction: + return std::make_unique( + num_channels, config.window_length, config.reference_window_length, + config.reference_window_delay, config.clipping_threshold, + config.crest_factor_margin); + case ClippingPredictorMode::kAdaptiveStepClippingPeakPrediction: + return std::make_unique( + num_channels, config.window_length, config.reference_window_length, + config.reference_window_delay, config.clipping_threshold, + /*adaptive_step_estimation=*/true); + case ClippingPredictorMode::kFixedStepClippingPeakPrediction: + return std::make_unique( + num_channels, config.window_length, config.reference_window_length, + config.reference_window_delay, config.clipping_threshold, + /*adaptive_step_estimation=*/false); + } + RTC_DCHECK_NOTREACHED(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor.h b/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor.h new file mode 100644 index 00000000..3fd10866 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_CLIPPING_PREDICTOR_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_CLIPPING_PREDICTOR_H_ + +#include +#include +#include + +#include "api/audio/audio_processing.h" +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { + +// Frame-wise clipping prediction and clipped level step estimation. Analyzes +// 10 ms multi-channel frames and estimates an analog mic level decrease step +// to possibly avoid clipping when predicted. `Analyze()` and +// `EstimateClippedLevelStep()` can be called in any order. +class ClippingPredictor { + public: + virtual ~ClippingPredictor() = default; + + virtual void Reset() = 0; + + // Analyzes a 10 ms multi-channel audio frame. + virtual void Analyze(const AudioFrameView& frame) = 0; + + // Predicts if clipping is going to occur for the specified `channel` in the + // near-future and, if so, it returns a recommended analog mic level decrease + // step. Returns std::nullopt if clipping is not predicted. + // `level` is the current analog mic level, `default_step` is the amount the + // mic level is lowered by the analog controller with every clipping event and + // `min_mic_level` and `max_mic_level` is the range of allowed analog mic + // levels. + virtual std::optional EstimateClippedLevelStep( + int channel, + int level, + int default_step, + int min_mic_level, + int max_mic_level) const = 0; +}; + +// Creates a ClippingPredictor based on the provided `config`. When enabled, +// the following must hold for `config`: +// `window_length < reference_window_length + reference_window_delay`. +// Returns `nullptr` if `config.enabled` is false. +std::unique_ptr CreateClippingPredictor( + int num_channels, + const AudioProcessing::Config::GainController1::AnalogGainController:: + ClippingPredictor& config); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_CLIPPING_PREDICTOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor_level_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor_level_buffer.cc new file mode 100644 index 00000000..acc114c4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor_level_buffer.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/clipping_predictor_level_buffer.h" + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +bool ClippingPredictorLevelBuffer::Level::operator==(const Level& level) const { + constexpr float kEpsilon = 1e-6f; + return std::fabs(average - level.average) < kEpsilon && + std::fabs(max - level.max) < kEpsilon; +} + +ClippingPredictorLevelBuffer::ClippingPredictorLevelBuffer(int capacity) + : tail_(-1), size_(0), data_(std::max(1, capacity)) { + if (capacity > kMaxCapacity) { + RTC_LOG(LS_WARNING) << "[agc]: ClippingPredictorLevelBuffer exceeds the " + << "maximum allowed capacity. Capacity: " << capacity; + } + RTC_DCHECK(!data_.empty()); +} + +void ClippingPredictorLevelBuffer::Reset() { + tail_ = -1; + size_ = 0; +} + +void ClippingPredictorLevelBuffer::Push(Level level) { + ++tail_; + if (tail_ == Capacity()) { + tail_ = 0; + } + if (size_ < Capacity()) { + size_++; + } + data_[tail_] = level; +} + +// TODO(bugs.webrtc.org/12774): Optimize partial computation for long buffers. +std::optional +ClippingPredictorLevelBuffer::ComputePartialMetrics(int delay, + int num_items) const { + RTC_DCHECK_GE(delay, 0); + RTC_DCHECK_LT(delay, Capacity()); + RTC_DCHECK_GT(num_items, 0); + RTC_DCHECK_LE(num_items, Capacity()); + RTC_DCHECK_LE(delay + num_items, Capacity()); + if (delay + num_items > Size()) { + return std::nullopt; + } + float sum = 0.0f; + float max = 0.0f; + for (int i = 0; i < num_items && i < Size(); ++i) { + int idx = tail_ - delay - i; + if (idx < 0) { + idx += Capacity(); + } + sum += data_[idx].average; + max = std::fmax(data_[idx].max, max); + } + return std::optional({sum / static_cast(num_items), max}); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor_level_buffer.h b/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor_level_buffer.h new file mode 100644 index 00000000..21e9b461 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/clipping_predictor_level_buffer.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_CLIPPING_PREDICTOR_LEVEL_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_CLIPPING_PREDICTOR_LEVEL_BUFFER_H_ + +#include +#include +#include + +namespace webrtc { + +// A circular buffer to store frame-wise `Level` items for clipping prediction. +// The current implementation is not optimized for large buffer lengths. +class ClippingPredictorLevelBuffer { + public: + struct Level { + float average; + float max; + bool operator==(const Level& level) const; + }; + + // Recommended maximum capacity. It is possible to create a buffer with a + // larger capacity, but the implementation is not optimized for large values. + static constexpr int kMaxCapacity = 100; + + // Ctor. Sets the buffer capacity to max(1, `capacity`) and logs a warning + // message if the capacity is greater than `kMaxCapacity`. + explicit ClippingPredictorLevelBuffer(int capacity); + ~ClippingPredictorLevelBuffer() {} + ClippingPredictorLevelBuffer(const ClippingPredictorLevelBuffer&) = delete; + ClippingPredictorLevelBuffer& operator=(const ClippingPredictorLevelBuffer&) = + delete; + + void Reset(); + + // Returns the current number of items stored in the buffer. + int Size() const { return size_; } + + // Returns the capacity of the buffer. + int Capacity() const { return data_.size(); } + + // Adds a `level` item into the circular buffer `data_`. Stores at most + // `Capacity()` items. If more items are pushed, the new item replaces the + // least recently pushed item. + void Push(Level level); + + // If at least `num_items` + `delay` items have been pushed, returns the + // average and maximum value for the `num_items` most recently pushed items + // from `delay` to `delay` - `num_items` (a delay equal to zero corresponds + // to the most recently pushed item). The value of `delay` is limited to + // [0, N] and `num_items` to [1, M] where N + M is the capacity of the buffer. + std::optional ComputePartialMetrics(int delay, int num_items) const; + + private: + int tail_; + int size_; + std::vector data_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_CLIPPING_PREDICTOR_LEVEL_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc new file mode 100644 index 00000000..221b499e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/compute_interpolated_gain_curve.h" + +#include +#include +#include +#include +#include +#include + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/agc2/agc2_testing_common.h" +#include "modules/audio_processing/agc2/limiter_db_gain_curve.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +std::pair ComputeLinearApproximationParams( + const LimiterDbGainCurve* limiter, + const double x) { + const double m = limiter->GetGainFirstDerivativeLinear(x); + const double q = limiter->GetGainLinear(x) - m * x; + return {m, q}; +} + +double ComputeAreaUnderPiecewiseLinearApproximation( + const LimiterDbGainCurve* limiter, + const double x0, + const double x1) { + RTC_CHECK_LT(x0, x1); + + // Linear approximation in x0 and x1. + double m0, q0, m1, q1; + std::tie(m0, q0) = ComputeLinearApproximationParams(limiter, x0); + std::tie(m1, q1) = ComputeLinearApproximationParams(limiter, x1); + + // Intersection point between two adjacent linear pieces. + RTC_CHECK_NE(m1, m0); + const double x_split = (q0 - q1) / (m1 - m0); + RTC_CHECK_LT(x0, x_split); + RTC_CHECK_LT(x_split, x1); + + auto area_under_linear_piece = [](double x_l, double x_r, double m, + double q) { + return x_r * (m * x_r / 2.0 + q) - x_l * (m * x_l / 2.0 + q); + }; + return area_under_linear_piece(x0, x_split, m0, q0) + + area_under_linear_piece(x_split, x1, m1, q1); +} + +// Computes the approximation error in the limiter region for a given interval. +// The error is computed as the difference between the areas beneath the limiter +// curve to approximate and its linear under-approximation. +double LimiterUnderApproximationNegativeError(const LimiterDbGainCurve* limiter, + const double x0, + const double x1) { + const double area_limiter = limiter->GetGainIntegralLinear(x0, x1); + const double area_interpolated_curve = + ComputeAreaUnderPiecewiseLinearApproximation(limiter, x0, x1); + RTC_CHECK_GE(area_limiter, area_interpolated_curve); + return area_limiter - area_interpolated_curve; +} + +// Automatically finds where to sample the beyond-knee region of a limiter using +// a greedy optimization algorithm that iteratively decreases the approximation +// error. +// The solution is sub-optimal because the algorithm is greedy and the points +// are assigned by halving intervals (starting with the whole beyond-knee region +// as a single interval). However, even if sub-optimal, this algorithm works +// well in practice and it is efficiently implemented using priority queues. +std::vector SampleLimiterRegion(const LimiterDbGainCurve* limiter) { + static_assert(kInterpolatedGainCurveBeyondKneePoints > 2, ""); + + struct Interval { + Interval() = default; // Ctor required by std::priority_queue. + Interval(double l, double r, double e) : x0(l), x1(r), error(e) { + RTC_CHECK(x0 < x1); + } + bool operator<(const Interval& other) const { return error < other.error; } + + double x0; + double x1; + double error; + }; + + std::priority_queue> q; + q.emplace(limiter->limiter_start_linear(), limiter->max_input_level_linear(), + LimiterUnderApproximationNegativeError( + limiter, limiter->limiter_start_linear(), + limiter->max_input_level_linear())); + + // Iteratively find points by halving the interval with greatest error. + while (q.size() < kInterpolatedGainCurveBeyondKneePoints) { + // Get the interval with highest error. + const auto interval = q.top(); + q.pop(); + + // Split `interval` and enqueue. + double x_split = (interval.x0 + interval.x1) / 2.0; + q.emplace(interval.x0, x_split, + LimiterUnderApproximationNegativeError(limiter, interval.x0, + x_split)); // Left. + q.emplace(x_split, interval.x1, + LimiterUnderApproximationNegativeError(limiter, x_split, + interval.x1)); // Right. + } + + // Copy x1 values and sort them. + RTC_CHECK_EQ(q.size(), kInterpolatedGainCurveBeyondKneePoints); + std::vector samples(kInterpolatedGainCurveBeyondKneePoints); + for (size_t i = 0; i < kInterpolatedGainCurveBeyondKneePoints; ++i) { + const auto interval = q.top(); + q.pop(); + samples[i] = interval.x1; + } + RTC_CHECK(q.empty()); + std::sort(samples.begin(), samples.end()); + + return samples; +} + +// Compute the parameters to over-approximate the knee region via linear +// interpolation. Over-approximating is saturation-safe since the knee region is +// convex. +void PrecomputeKneeApproxParams(const LimiterDbGainCurve* limiter, + test::InterpolatedParameters* parameters) { + static_assert(kInterpolatedGainCurveKneePoints > 2, ""); + // Get `kInterpolatedGainCurveKneePoints` - 1 equally spaced points. + const std::vector points = test::LinSpace( + limiter->knee_start_linear(), limiter->limiter_start_linear(), + kInterpolatedGainCurveKneePoints - 1); + + // Set the first two points. The second is computed to help with the beginning + // of the knee region, which has high curvature. + parameters->computed_approximation_params_x[0] = points[0]; + parameters->computed_approximation_params_x[1] = + (points[0] + points[1]) / 2.0; + // Copy the remaining points. + std::copy(std::begin(points) + 1, std::end(points), + std::begin(parameters->computed_approximation_params_x) + 2); + + // Compute (m, q) pairs for each linear piece y = mx + q. + for (size_t i = 0; i < kInterpolatedGainCurveKneePoints - 1; ++i) { + const double x0 = parameters->computed_approximation_params_x[i]; + const double x1 = parameters->computed_approximation_params_x[i + 1]; + const double y0 = limiter->GetGainLinear(x0); + const double y1 = limiter->GetGainLinear(x1); + RTC_CHECK_NE(x1, x0); + parameters->computed_approximation_params_m[i] = (y1 - y0) / (x1 - x0); + parameters->computed_approximation_params_q[i] = + y0 - parameters->computed_approximation_params_m[i] * x0; + } +} + +// Compute the parameters to under-approximate the beyond-knee region via linear +// interpolation and greedy sampling. Under-approximating is saturation-safe +// since the beyond-knee region is concave. +void PrecomputeBeyondKneeApproxParams( + const LimiterDbGainCurve* limiter, + test::InterpolatedParameters* parameters) { + // Find points on which the linear pieces are tangent to the gain curve. + const auto samples = SampleLimiterRegion(limiter); + + // Parametrize each linear piece. + double m, q; + std::tie(m, q) = ComputeLinearApproximationParams( + limiter, + parameters + ->computed_approximation_params_x[kInterpolatedGainCurveKneePoints - + 1]); + parameters + ->computed_approximation_params_m[kInterpolatedGainCurveKneePoints - 1] = + m; + parameters + ->computed_approximation_params_q[kInterpolatedGainCurveKneePoints - 1] = + q; + for (size_t i = 0; i < samples.size(); ++i) { + std::tie(m, q) = ComputeLinearApproximationParams(limiter, samples[i]); + parameters + ->computed_approximation_params_m[i + + kInterpolatedGainCurveKneePoints] = m; + parameters + ->computed_approximation_params_q[i + + kInterpolatedGainCurveKneePoints] = q; + } + + // Find the point of intersection between adjacent linear pieces. They will be + // used as boundaries between adjacent linear pieces. + for (size_t i = kInterpolatedGainCurveKneePoints; + i < kInterpolatedGainCurveKneePoints + + kInterpolatedGainCurveBeyondKneePoints; + ++i) { + RTC_CHECK_NE(parameters->computed_approximation_params_m[i], + parameters->computed_approximation_params_m[i - 1]); + parameters->computed_approximation_params_x[i] = + ( // Formula: (q0 - q1) / (m1 - m0). + parameters->computed_approximation_params_q[i - 1] - + parameters->computed_approximation_params_q[i]) / + (parameters->computed_approximation_params_m[i] - + parameters->computed_approximation_params_m[i - 1]); + } +} + +} // namespace + +namespace test { + +InterpolatedParameters ComputeInterpolatedGainCurveApproximationParams() { + InterpolatedParameters parameters; + LimiterDbGainCurve limiter; + parameters.computed_approximation_params_x.fill(0.0f); + parameters.computed_approximation_params_m.fill(0.0f); + parameters.computed_approximation_params_q.fill(0.0f); + PrecomputeKneeApproxParams(&limiter, ¶meters); + PrecomputeBeyondKneeApproxParams(&limiter, ¶meters); + return parameters; +} +} // namespace test +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.h b/pkg/apm/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.h new file mode 100644 index 00000000..08b676f5 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_COMPUTE_INTERPOLATED_GAIN_CURVE_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_COMPUTE_INTERPOLATED_GAIN_CURVE_H_ + +#include + +#include "modules/audio_processing/agc2/agc2_common.h" + +namespace webrtc { + +namespace test { + +// Parameters for interpolated gain curve using under-approximation to +// avoid saturation. +// +// The saturation gain is defined in order to let hard-clipping occur for +// those samples having a level that falls in the saturation region. It is an +// upper bound of the actual gain to apply - i.e., that returned by the +// limiter. + +// Knee and beyond-knee regions approximation parameters. +// The gain curve is approximated as a piece-wise linear function. +// `approx_params_x_` are the boundaries between adjacent linear pieces, +// `approx_params_m_` and `approx_params_q_` are the slope and the y-intercept +// values of each piece. +struct InterpolatedParameters { + std::array + computed_approximation_params_x; + std::array + computed_approximation_params_m; + std::array + computed_approximation_params_q; +}; + +InterpolatedParameters ComputeInterpolatedGainCurveApproximationParams(); +} // namespace test +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_COMPUTE_INTERPOLATED_GAIN_CURVE_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/cpu_features.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/cpu_features.cc new file mode 100644 index 00000000..aa5a5783 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/cpu_features.cc @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/cpu_features.h" + +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" + +namespace webrtc { + +std::string AvailableCpuFeatures::ToString() const { + char buf[64]; + SimpleStringBuilder builder(buf); + bool first = true; + if (sse2) { + builder << (first ? "SSE2" : "_SSE2"); + first = false; + } + if (avx2) { + builder << (first ? "AVX2" : "_AVX2"); + first = false; + } + if (neon) { + builder << (first ? "NEON" : "_NEON"); + first = false; + } + if (first) { + return "none"; + } + return builder.str(); +} + +// Detects available CPU features. +AvailableCpuFeatures GetAvailableCpuFeatures() { +#if defined(WEBRTC_ARCH_X86_FAMILY) + return {/*sse2=*/GetCPUInfo(kSSE2) != 0, + /*avx2=*/GetCPUInfo(kAVX2) != 0, + /*neon=*/false}; +#elif defined(WEBRTC_HAS_NEON) + return {/*sse2=*/false, + /*avx2=*/false, + /*neon=*/true}; +#else + return {/*sse2=*/false, + /*avx2=*/false, + /*neon=*/false}; +#endif +} + +AvailableCpuFeatures NoAvailableCpuFeatures() { + return {/*sse2=*/false, /*avx2=*/false, /*neon=*/false}; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/cpu_features.h b/pkg/apm/webrtc/modules/audio_processing/agc2/cpu_features.h new file mode 100644 index 00000000..54ddfb30 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/cpu_features.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_CPU_FEATURES_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_CPU_FEATURES_H_ + +#include + +namespace webrtc { + +// Collection of flags indicating which CPU features are available on the +// current platform. True means available. +struct AvailableCpuFeatures { + AvailableCpuFeatures(bool sse2, bool avx2, bool neon) + : sse2(sse2), avx2(avx2), neon(neon) {} + // Intel. + bool sse2; + bool avx2; + // ARM. + bool neon; + std::string ToString() const; +}; + +// Detects what CPU features are available. +AvailableCpuFeatures GetAvailableCpuFeatures(); + +// Returns the CPU feature flags all set to false. +AvailableCpuFeatures NoAvailableCpuFeatures(); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_CPU_FEATURES_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.cc new file mode 100644 index 00000000..20e8491c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.cc @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/fixed_digital_level_estimator.h" + +#include +#include + +#include "api/array_view.h" +#include "api/audio/audio_frame.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr float kInitialFilterStateLevel = 0.0f; + +// Instant attack. +constexpr float kAttackFilterConstant = 0.0f; + +// Limiter decay constant. +// Computed as `10 ** (-1/20 * subframe_duration / kDecayMs)` where: +// - `subframe_duration` is `kFrameDurationMs / kSubFramesInFrame`; +// - `kDecayMs` is defined in agc2_testing_common.h. +constexpr float kDecayFilterConstant = 0.9971259f; + +} // namespace + +FixedDigitalLevelEstimator::FixedDigitalLevelEstimator( + size_t samples_per_channel, + ApmDataDumper* apm_data_dumper) + : apm_data_dumper_(apm_data_dumper), + filter_state_level_(kInitialFilterStateLevel) { + SetSamplesPerChannel(samples_per_channel); + CheckParameterCombination(); + RTC_DCHECK(apm_data_dumper_); + // Convert `samples_per_channel` to sample rate for + // `agc2_level_estimator_samplerate`. + apm_data_dumper_->DumpRaw("agc2_level_estimator_samplerate", + samples_per_channel * kDefaultAudioBuffersPerSec); +} + +void FixedDigitalLevelEstimator::CheckParameterCombination() { + RTC_DCHECK_GT(samples_in_frame_, 0); + RTC_DCHECK_LE(kSubFramesInFrame, samples_in_frame_); + RTC_DCHECK_EQ(samples_in_frame_ % kSubFramesInFrame, 0); + RTC_DCHECK_GT(samples_in_sub_frame_, 1); +} + +std::array FixedDigitalLevelEstimator::ComputeLevel( + DeinterleavedView float_frame) { + RTC_DCHECK_GT(float_frame.num_channels(), 0); + RTC_DCHECK_EQ(float_frame.samples_per_channel(), samples_in_frame_); + + // Compute max envelope without smoothing. + std::array envelope{}; + for (size_t channel_idx = 0; channel_idx < float_frame.num_channels(); + ++channel_idx) { + const auto channel = float_frame[channel_idx]; + for (int sub_frame = 0; sub_frame < kSubFramesInFrame; ++sub_frame) { + for (int sample_in_sub_frame = 0; + sample_in_sub_frame < samples_in_sub_frame_; ++sample_in_sub_frame) { + envelope[sub_frame] = + std::max(envelope[sub_frame], + std::abs(channel[sub_frame * samples_in_sub_frame_ + + sample_in_sub_frame])); + } + } + } + + // Make sure envelope increases happen one step earlier so that the + // corresponding *gain decrease* doesn't miss a sudden signal + // increase due to interpolation. + for (int sub_frame = 0; sub_frame < kSubFramesInFrame - 1; ++sub_frame) { + if (envelope[sub_frame] < envelope[sub_frame + 1]) { + envelope[sub_frame] = envelope[sub_frame + 1]; + } + } + + // Add attack / decay smoothing. + for (int sub_frame = 0; sub_frame < kSubFramesInFrame; ++sub_frame) { + const float envelope_value = envelope[sub_frame]; + if (envelope_value > filter_state_level_) { + envelope[sub_frame] = envelope_value * (1 - kAttackFilterConstant) + + filter_state_level_ * kAttackFilterConstant; + } else { + envelope[sub_frame] = envelope_value * (1 - kDecayFilterConstant) + + filter_state_level_ * kDecayFilterConstant; + } + filter_state_level_ = envelope[sub_frame]; + + // Dump data for debug. + RTC_DCHECK(apm_data_dumper_); + const auto channel = float_frame[0]; + apm_data_dumper_->DumpRaw("agc2_level_estimator_samples", + samples_in_sub_frame_, + &channel[sub_frame * samples_in_sub_frame_]); + apm_data_dumper_->DumpRaw("agc2_level_estimator_level", + envelope[sub_frame]); + } + + return envelope; +} + +void FixedDigitalLevelEstimator::SetSamplesPerChannel( + size_t samples_per_channel) { + samples_in_frame_ = static_cast(samples_per_channel); + samples_in_sub_frame_ = CheckedDivExact(samples_in_frame_, kSubFramesInFrame); + CheckParameterCombination(); +} + +void FixedDigitalLevelEstimator::Reset() { + filter_state_level_ = kInitialFilterStateLevel; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.h b/pkg/apm/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.h new file mode 100644 index 00000000..1669acdc --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_FIXED_DIGITAL_LEVEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_FIXED_DIGITAL_LEVEL_ESTIMATOR_H_ + +#include +#include + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { + +class ApmDataDumper; +// Produces a smooth signal level estimate from an input audio +// stream. The estimate smoothing is done through exponential +// filtering. +class FixedDigitalLevelEstimator { + public: + // `samples_per_channel` is expected to be derived from this formula: + // sample_rate_hz * kFrameDurationMs / 1000 + // or, for a 10ms duration: + // sample_rate_hz / 100 + // I.e. the number of samples for 10ms of the given sample rate. The + // expectation is that samples per channel is divisible by + // kSubFramesInSample. For kFrameDurationMs=10 and + // kSubFramesInSample=20, this means that the original sample rate has to be + // divisible by 2000 and therefore `samples_per_channel` by 20. + FixedDigitalLevelEstimator(size_t samples_per_channel, + ApmDataDumper* apm_data_dumper); + + FixedDigitalLevelEstimator(const FixedDigitalLevelEstimator&) = delete; + FixedDigitalLevelEstimator& operator=(const FixedDigitalLevelEstimator&) = + delete; + + // The input is assumed to be in FloatS16 format. Scaled input will + // produce similarly scaled output. A frame of with kFrameDurationMs + // ms of audio produces a level estimates in the same scale. The + // level estimate contains kSubFramesInFrame values. + std::array ComputeLevel( + DeinterleavedView float_frame); + + // Rate may be changed at any time (but not concurrently) from the + // value passed to the constructor. The class is not thread safe. + void SetSamplesPerChannel(size_t samples_per_channel); + + // Resets the level estimator internal state. + void Reset(); + + float LastAudioLevel() const { return filter_state_level_; } + + private: + void CheckParameterCombination(); + + ApmDataDumper* const apm_data_dumper_ = nullptr; + float filter_state_level_; + int samples_in_frame_; + int samples_in_sub_frame_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_FIXED_DIGITAL_LEVEL_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/gain_applier.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/gain_applier.cc new file mode 100644 index 00000000..927bb554 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/gain_applier.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/gain_applier.h" + +#include "api/audio/audio_view.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +// Returns true when the gain factor is so close to 1 that it would +// not affect int16 samples. +bool GainCloseToOne(float gain_factor) { + return 1.f - 1.f / kMaxFloatS16Value <= gain_factor && + gain_factor <= 1.f + 1.f / kMaxFloatS16Value; +} + +void ClipSignal(DeinterleavedView signal) { + for (size_t k = 0; k < signal.num_channels(); ++k) { + MonoView channel_view = signal[k]; + for (auto& sample : channel_view) { + sample = SafeClamp(sample, kMinFloatS16Value, kMaxFloatS16Value); + } + } +} + +void ApplyGainWithRamping(float last_gain_linear, + float gain_at_end_of_frame_linear, + float inverse_samples_per_channel, + DeinterleavedView float_frame) { + // Do not modify the signal. + if (last_gain_linear == gain_at_end_of_frame_linear && + GainCloseToOne(gain_at_end_of_frame_linear)) { + return; + } + + // Gain is constant and different from 1. + if (last_gain_linear == gain_at_end_of_frame_linear) { + for (size_t k = 0; k < float_frame.num_channels(); ++k) { + MonoView channel_view = float_frame[k]; + for (auto& sample : channel_view) { + sample *= gain_at_end_of_frame_linear; + } + } + return; + } + + // The gain changes. We have to change slowly to avoid discontinuities. + const float increment = (gain_at_end_of_frame_linear - last_gain_linear) * + inverse_samples_per_channel; + for (size_t ch = 0; ch < float_frame.num_channels(); ++ch) { + float gain = last_gain_linear; + for (float& sample : float_frame[ch]) { + sample *= gain; + gain += increment; + } + } +} + +} // namespace + +GainApplier::GainApplier(bool hard_clip_samples, float initial_gain_factor) + : hard_clip_samples_(hard_clip_samples), + last_gain_factor_(initial_gain_factor), + current_gain_factor_(initial_gain_factor) {} + +void GainApplier::ApplyGain(DeinterleavedView signal) { + if (static_cast(signal.samples_per_channel()) != samples_per_channel_) { + Initialize(signal.samples_per_channel()); + } + + ApplyGainWithRamping(last_gain_factor_, current_gain_factor_, + inverse_samples_per_channel_, signal); + + last_gain_factor_ = current_gain_factor_; + + if (hard_clip_samples_) { + ClipSignal(signal); + } +} + +// TODO(bugs.webrtc.org/7494): Remove once switched to gains in dB. +void GainApplier::SetGainFactor(float gain_factor) { + RTC_DCHECK_GT(gain_factor, 0.f); + current_gain_factor_ = gain_factor; +} + +void GainApplier::Initialize(int samples_per_channel) { + RTC_DCHECK_GT(samples_per_channel, 0); + samples_per_channel_ = static_cast(samples_per_channel); + inverse_samples_per_channel_ = 1.f / samples_per_channel_; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/gain_applier.h b/pkg/apm/webrtc/modules/audio_processing/agc2/gain_applier.h new file mode 100644 index 00000000..82ae82ee --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/gain_applier.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_GAIN_APPLIER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_GAIN_APPLIER_H_ + +#include + +#include "api/audio/audio_view.h" +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { +class GainApplier { + public: + GainApplier(bool hard_clip_samples, float initial_gain_factor); + + void ApplyGain(DeinterleavedView signal); + void SetGainFactor(float gain_factor); + float GetGainFactor() const { return current_gain_factor_; } + + [[deprecated("Use DeinterleavedView<> version")]] void ApplyGain( + AudioFrameView signal) { + ApplyGain(signal.view()); + } + + private: + void Initialize(int samples_per_channel); + + // Whether to clip samples after gain is applied. If 'true', result + // will fit in FloatS16 range. + const bool hard_clip_samples_; + float last_gain_factor_; + + // If this value is not equal to 'last_gain_factor', gain will be + // ramped from 'last_gain_factor_' to this value during the next + // 'ApplyGain'. + float current_gain_factor_; + int samples_per_channel_ = -1; + float inverse_samples_per_channel_ = -1.f; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_GAIN_APPLIER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/gain_map_internal.h b/pkg/apm/webrtc/modules/audio_processing/agc2/gain_map_internal.h new file mode 100644 index 00000000..7c669fc9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/gain_map_internal.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_GAIN_MAP_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_GAIN_MAP_INTERNAL_H_ + +namespace webrtc { + +static constexpr int kGainMapSize = 256; +// Maps input volumes, which are values in the [0, 255] range, to gains in dB. +// The values below are generated with numpy as follows: +// SI = 2 # Initial slope. +// SF = 0.25 # Final slope. +// D = 8/256 # Quantization factor. +// x = np.linspace(0, 255, 256) # Input volumes. +// y = (SF * x + (SI - SF) * (1 - np.exp(-D*x)) / D - 56).round() +static const int kGainMap[kGainMapSize] = { + -56, -54, -52, -50, -48, -47, -45, -43, -42, -40, -38, -37, -35, -34, -33, + -31, -30, -29, -27, -26, -25, -24, -23, -22, -20, -19, -18, -17, -16, -15, + -14, -14, -13, -12, -11, -10, -9, -8, -8, -7, -6, -5, -5, -4, -3, + -2, -2, -1, 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, + 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, 17, 18, 18, 18, 19, + 19, 19, 20, 20, 21, 21, 21, 22, 22, 22, 23, 23, 23, 24, 24, + 24, 24, 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 28, + 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 32, 32, 32, 32, 33, + 33, 33, 33, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, + 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, + 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 44, 44, 44, 44, 45, + 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, 48, + 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, + 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, + 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, + 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, + 64}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_GAIN_MAP_INTERNAL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_controller.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_controller.cc new file mode 100644 index 00000000..557c1e67 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_controller.cc @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/input_volume_controller.h" + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/gain_map_internal.h" +#include "modules/audio_processing/agc2/input_volume_stats_reporter.h" +#include "modules/audio_processing/include/audio_frame_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "system_wrappers/include/field_trial.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +namespace { + +// Amount of error we tolerate in the microphone input volume (presumably due to +// OS quantization) before we assume the user has manually adjusted the volume. +constexpr int kVolumeQuantizationSlack = 25; + +constexpr int kMaxInputVolume = 255; +static_assert(kGainMapSize > kMaxInputVolume, "gain map too small"); + +// Maximum absolute RMS error. +constexpr int KMaxAbsRmsErrorDbfs = 15; +static_assert(KMaxAbsRmsErrorDbfs > 0, ""); + +using Agc1ClippingPredictorConfig = AudioProcessing::Config::GainController1:: + AnalogGainController::ClippingPredictor; + +// TODO(webrtc:7494): Hardcode clipping predictor parameters and remove this +// function after no longer needed in the ctor. +Agc1ClippingPredictorConfig CreateClippingPredictorConfig(bool enabled) { + Agc1ClippingPredictorConfig config; + config.enabled = enabled; + + return config; +} + +// Returns an input volume in the [`min_input_volume`, `kMaxInputVolume`] range +// that reduces `gain_error_db`, which is a gain error estimated when +// `input_volume` was applied, according to a fixed gain map. +int ComputeVolumeUpdate(int gain_error_db, + int input_volume, + int min_input_volume) { + RTC_DCHECK_GE(input_volume, 0); + RTC_DCHECK_LE(input_volume, kMaxInputVolume); + if (gain_error_db == 0) { + return input_volume; + } + + int new_volume = input_volume; + if (gain_error_db > 0) { + while (kGainMap[new_volume] - kGainMap[input_volume] < gain_error_db && + new_volume < kMaxInputVolume) { + ++new_volume; + } + } else { + while (kGainMap[new_volume] - kGainMap[input_volume] > gain_error_db && + new_volume > min_input_volume) { + --new_volume; + } + } + return new_volume; +} + +// Returns the proportion of samples in the buffer which are at full-scale +// (and presumably clipped). +float ComputeClippedRatio(const float* const* audio, + size_t num_channels, + size_t samples_per_channel) { + RTC_DCHECK_GT(samples_per_channel, 0); + int num_clipped = 0; + for (size_t ch = 0; ch < num_channels; ++ch) { + int num_clipped_in_ch = 0; + for (size_t i = 0; i < samples_per_channel; ++i) { + RTC_DCHECK(audio[ch]); + if (audio[ch][i] >= 32767.0f || audio[ch][i] <= -32768.0f) { + ++num_clipped_in_ch; + } + } + num_clipped = std::max(num_clipped, num_clipped_in_ch); + } + return static_cast(num_clipped) / (samples_per_channel); +} + +void LogClippingMetrics(int clipping_rate) { + RTC_LOG(LS_INFO) << "[AGC2] Input clipping rate: " << clipping_rate << "%"; + RTC_HISTOGRAM_COUNTS_LINEAR(/*name=*/"WebRTC.Audio.Agc.InputClippingRate", + /*sample=*/clipping_rate, /*min=*/0, /*max=*/100, + /*bucket_count=*/50); +} + +// Compares `speech_level_dbfs` to the [`target_range_min_dbfs`, +// `target_range_max_dbfs`] range and returns the error to be compensated via +// input volume adjustment. Returns a positive value when the level is below +// the range, a negative value when the level is above the range, zero +// otherwise. +int GetSpeechLevelRmsErrorDb(float speech_level_dbfs, + int target_range_min_dbfs, + int target_range_max_dbfs) { + constexpr float kMinSpeechLevelDbfs = -90.0f; + constexpr float kMaxSpeechLevelDbfs = 30.0f; + RTC_DCHECK_GE(speech_level_dbfs, kMinSpeechLevelDbfs); + RTC_DCHECK_LE(speech_level_dbfs, kMaxSpeechLevelDbfs); + speech_level_dbfs = SafeClamp(speech_level_dbfs, kMinSpeechLevelDbfs, + kMaxSpeechLevelDbfs); + + int rms_error_db = 0; + if (speech_level_dbfs > target_range_max_dbfs) { + rms_error_db = std::round(target_range_max_dbfs - speech_level_dbfs); + } else if (speech_level_dbfs < target_range_min_dbfs) { + rms_error_db = std::round(target_range_min_dbfs - speech_level_dbfs); + } + + return rms_error_db; +} + +} // namespace + +MonoInputVolumeController::MonoInputVolumeController( + int min_input_volume_after_clipping, + int min_input_volume, + int update_input_volume_wait_frames, + float speech_probability_threshold, + float speech_ratio_threshold) + : min_input_volume_(min_input_volume), + min_input_volume_after_clipping_(min_input_volume_after_clipping), + max_input_volume_(kMaxInputVolume), + update_input_volume_wait_frames_( + std::max(update_input_volume_wait_frames, 1)), + speech_probability_threshold_(speech_probability_threshold), + speech_ratio_threshold_(speech_ratio_threshold) { + RTC_DCHECK_GE(min_input_volume_, 0); + RTC_DCHECK_LE(min_input_volume_, 255); + RTC_DCHECK_GE(min_input_volume_after_clipping_, 0); + RTC_DCHECK_LE(min_input_volume_after_clipping_, 255); + RTC_DCHECK_GE(max_input_volume_, 0); + RTC_DCHECK_LE(max_input_volume_, 255); + RTC_DCHECK_GE(update_input_volume_wait_frames_, 0); + RTC_DCHECK_GE(speech_probability_threshold_, 0.0f); + RTC_DCHECK_LE(speech_probability_threshold_, 1.0f); + RTC_DCHECK_GE(speech_ratio_threshold_, 0.0f); + RTC_DCHECK_LE(speech_ratio_threshold_, 1.0f); +} + +MonoInputVolumeController::~MonoInputVolumeController() = default; + +void MonoInputVolumeController::Initialize() { + max_input_volume_ = kMaxInputVolume; + capture_output_used_ = true; + check_volume_on_next_process_ = true; + frames_since_update_input_volume_ = 0; + speech_frames_since_update_input_volume_ = 0; + is_first_frame_ = true; +} + +// A speeh segment is considered active if at least +// `update_input_volume_wait_frames_` new frames have been processed since the +// previous update and the ratio of non-silence frames (i.e., frames with a +// `speech_probability` higher than `speech_probability_threshold_`) is at least +// `speech_ratio_threshold_`. +void MonoInputVolumeController::Process(std::optional rms_error_db, + float speech_probability) { + if (check_volume_on_next_process_) { + check_volume_on_next_process_ = false; + // We have to wait until the first process call to check the volume, + // because Chromium doesn't guarantee it to be valid any earlier. + CheckVolumeAndReset(); + } + + // Count frames with a high speech probability as speech. + if (speech_probability >= speech_probability_threshold_) { + ++speech_frames_since_update_input_volume_; + } + + // Reset the counters and maybe update the input volume. + if (++frames_since_update_input_volume_ >= update_input_volume_wait_frames_) { + const float speech_ratio = + static_cast(speech_frames_since_update_input_volume_) / + static_cast(update_input_volume_wait_frames_); + + // Always reset the counters regardless of whether the volume changes or + // not. + frames_since_update_input_volume_ = 0; + speech_frames_since_update_input_volume_ = 0; + + // Update the input volume if allowed. + if (!is_first_frame_ && speech_ratio >= speech_ratio_threshold_ && + rms_error_db.has_value()) { + UpdateInputVolume(*rms_error_db); + } + } + + is_first_frame_ = false; +} + +void MonoInputVolumeController::HandleClipping(int clipped_level_step) { + RTC_DCHECK_GT(clipped_level_step, 0); + // Always decrease the maximum input volume, even if the current input volume + // is below threshold. + SetMaxLevel(std::max(min_input_volume_after_clipping_, + max_input_volume_ - clipped_level_step)); + if (log_to_histograms_) { + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed", + last_recommended_input_volume_ - clipped_level_step >= + min_input_volume_after_clipping_); + } + if (last_recommended_input_volume_ > min_input_volume_after_clipping_) { + // Don't try to adjust the input volume if we're already below the limit. As + // a consequence, if the user has brought the input volume above the limit, + // we will still not react until the postproc updates the input volume. + SetInputVolume( + std::max(min_input_volume_after_clipping_, + last_recommended_input_volume_ - clipped_level_step)); + frames_since_update_input_volume_ = 0; + speech_frames_since_update_input_volume_ = 0; + is_first_frame_ = false; + } +} + +void MonoInputVolumeController::SetInputVolume(int new_volume) { + int applied_input_volume = recommended_input_volume_; + if (applied_input_volume == 0) { + RTC_DLOG(LS_INFO) + << "[AGC2] The applied input volume is zero, taking no action."; + return; + } + if (applied_input_volume < 0 || applied_input_volume > kMaxInputVolume) { + RTC_LOG(LS_ERROR) << "[AGC2] Invalid value for the applied input volume: " + << applied_input_volume; + return; + } + + // Detect manual input volume adjustments by checking if the + // `applied_input_volume` is outside of the `[last_recommended_input_volume_ - + // kVolumeQuantizationSlack, last_recommended_input_volume_ + + // kVolumeQuantizationSlack]` range. + if (applied_input_volume > + last_recommended_input_volume_ + kVolumeQuantizationSlack || + applied_input_volume < + last_recommended_input_volume_ - kVolumeQuantizationSlack) { + RTC_DLOG(LS_INFO) + << "[AGC2] The input volume was manually adjusted. Updating " + "stored input volume from " + << last_recommended_input_volume_ << " to " << applied_input_volume; + last_recommended_input_volume_ = applied_input_volume; + // Always allow the user to increase the volume. + if (last_recommended_input_volume_ > max_input_volume_) { + SetMaxLevel(last_recommended_input_volume_); + } + // Take no action in this case, since we can't be sure when the volume + // was manually adjusted. + frames_since_update_input_volume_ = 0; + speech_frames_since_update_input_volume_ = 0; + is_first_frame_ = false; + return; + } + + new_volume = std::min(new_volume, max_input_volume_); + if (new_volume == last_recommended_input_volume_) { + return; + } + + recommended_input_volume_ = new_volume; + RTC_DLOG(LS_INFO) << "[AGC2] Applied input volume: " << applied_input_volume + << " | last recommended input volume: " + << last_recommended_input_volume_ + << " | newly recommended input volume: " << new_volume; + last_recommended_input_volume_ = new_volume; +} + +void MonoInputVolumeController::SetMaxLevel(int input_volume) { + RTC_DCHECK_GE(input_volume, min_input_volume_after_clipping_); + max_input_volume_ = input_volume; + RTC_DLOG(LS_INFO) << "[AGC2] Maximum input volume updated: " + << max_input_volume_; +} + +void MonoInputVolumeController::HandleCaptureOutputUsedChange( + bool capture_output_used) { + if (capture_output_used_ == capture_output_used) { + return; + } + capture_output_used_ = capture_output_used; + + if (capture_output_used) { + // When we start using the output, we should reset things to be safe. + check_volume_on_next_process_ = true; + } +} + +int MonoInputVolumeController::CheckVolumeAndReset() { + int input_volume = recommended_input_volume_; + // Reasons for taking action at startup: + // 1) A person starting a call is expected to be heard. + // 2) Independent of interpretation of `input_volume` == 0 we should raise it + // so the AGC can do its job properly. + if (input_volume == 0 && !startup_) { + RTC_DLOG(LS_INFO) + << "[AGC2] The applied input volume is zero, taking no action."; + return 0; + } + if (input_volume < 0 || input_volume > kMaxInputVolume) { + RTC_LOG(LS_ERROR) << "[AGC2] Invalid value for the applied input volume: " + << input_volume; + return -1; + } + RTC_DLOG(LS_INFO) << "[AGC2] Initial input volume: " << input_volume; + + if (input_volume < min_input_volume_) { + input_volume = min_input_volume_; + RTC_DLOG(LS_INFO) + << "[AGC2] The initial input volume is too low, raising to " + << input_volume; + recommended_input_volume_ = input_volume; + } + + last_recommended_input_volume_ = input_volume; + startup_ = false; + frames_since_update_input_volume_ = 0; + speech_frames_since_update_input_volume_ = 0; + is_first_frame_ = true; + + return 0; +} + +void MonoInputVolumeController::UpdateInputVolume(int rms_error_db) { + RTC_DLOG(LS_INFO) << "[AGC2] RMS error: " << rms_error_db << " dB"; + // Prevent too large microphone input volume changes by clamping the RMS + // error. + rms_error_db = + SafeClamp(rms_error_db, -KMaxAbsRmsErrorDbfs, KMaxAbsRmsErrorDbfs); + if (rms_error_db == 0) { + return; + } + SetInputVolume(ComputeVolumeUpdate( + rms_error_db, last_recommended_input_volume_, min_input_volume_)); +} + +InputVolumeController::InputVolumeController(int num_capture_channels, + const Config& config) + : num_capture_channels_(num_capture_channels), + min_input_volume_(config.min_input_volume), + capture_output_used_(true), + clipped_level_step_(config.clipped_level_step), + clipped_ratio_threshold_(config.clipped_ratio_threshold), + clipped_wait_frames_(config.clipped_wait_frames), + clipping_predictor_(CreateClippingPredictor( + num_capture_channels, + CreateClippingPredictorConfig(config.enable_clipping_predictor))), + use_clipping_predictor_step_( + !!clipping_predictor_ && + CreateClippingPredictorConfig(config.enable_clipping_predictor) + .use_predicted_step), + frames_since_clipped_(config.clipped_wait_frames), + clipping_rate_log_counter_(0), + clipping_rate_log_(0.0f), + target_range_max_dbfs_(config.target_range_max_dbfs), + target_range_min_dbfs_(config.target_range_min_dbfs), + channel_controllers_(num_capture_channels) { + RTC_LOG(LS_INFO) + << "[AGC2] Input volume controller enabled. Minimum input volume: " + << min_input_volume_; + + for (auto& controller : channel_controllers_) { + controller = std::make_unique( + config.clipped_level_min, min_input_volume_, + config.update_input_volume_wait_frames, + config.speech_probability_threshold, config.speech_ratio_threshold); + } + + RTC_DCHECK(!channel_controllers_.empty()); + RTC_DCHECK_GT(clipped_level_step_, 0); + RTC_DCHECK_LE(clipped_level_step_, 255); + RTC_DCHECK_GT(clipped_ratio_threshold_, 0.0f); + RTC_DCHECK_LT(clipped_ratio_threshold_, 1.0f); + RTC_DCHECK_GT(clipped_wait_frames_, 0); + channel_controllers_[0]->ActivateLogging(); +} + +InputVolumeController::~InputVolumeController() {} + +void InputVolumeController::Initialize() { + for (auto& controller : channel_controllers_) { + controller->Initialize(); + } + capture_output_used_ = true; + + AggregateChannelLevels(); + clipping_rate_log_ = 0.0f; + clipping_rate_log_counter_ = 0; + + applied_input_volume_ = std::nullopt; +} + +void InputVolumeController::AnalyzeInputAudio(int applied_input_volume, + const AudioBuffer& audio_buffer) { + RTC_DCHECK_GE(applied_input_volume, 0); + RTC_DCHECK_LE(applied_input_volume, 255); + + SetAppliedInputVolume(applied_input_volume); + + RTC_DCHECK_EQ(audio_buffer.num_channels(), channel_controllers_.size()); + const float* const* audio = audio_buffer.channels_const(); + size_t samples_per_channel = audio_buffer.num_frames(); + RTC_DCHECK(audio); + + AggregateChannelLevels(); + if (!capture_output_used_) { + return; + } + + if (!!clipping_predictor_) { + AudioFrameView frame = AudioFrameView( + audio, num_capture_channels_, static_cast(samples_per_channel)); + clipping_predictor_->Analyze(frame); + } + + // Check for clipped samples. We do this in the preprocessing phase in order + // to catch clipped echo as well. + // + // If we find a sufficiently clipped frame, drop the current microphone + // input volume and enforce a new maximum input volume, dropped the same + // amount from the current maximum. This harsh treatment is an effort to avoid + // repeated clipped echo events. + float clipped_ratio = + ComputeClippedRatio(audio, num_capture_channels_, samples_per_channel); + clipping_rate_log_ = std::max(clipped_ratio, clipping_rate_log_); + clipping_rate_log_counter_++; + constexpr int kNumFramesIn30Seconds = 3000; + if (clipping_rate_log_counter_ == kNumFramesIn30Seconds) { + LogClippingMetrics(std::round(100.0f * clipping_rate_log_)); + clipping_rate_log_ = 0.0f; + clipping_rate_log_counter_ = 0; + } + + if (frames_since_clipped_ < clipped_wait_frames_) { + ++frames_since_clipped_; + return; + } + + const bool clipping_detected = clipped_ratio > clipped_ratio_threshold_; + bool clipping_predicted = false; + int predicted_step = 0; + if (!!clipping_predictor_) { + for (int channel = 0; channel < num_capture_channels_; ++channel) { + const auto step = clipping_predictor_->EstimateClippedLevelStep( + channel, recommended_input_volume_, clipped_level_step_, + channel_controllers_[channel]->min_input_volume_after_clipping(), + kMaxInputVolume); + if (step.has_value()) { + predicted_step = std::max(predicted_step, step.value()); + clipping_predicted = true; + } + } + } + + if (clipping_detected) { + RTC_DLOG(LS_INFO) << "[AGC2] Clipping detected (ratio: " << clipped_ratio + << ")"; + } + + int step = clipped_level_step_; + if (clipping_predicted) { + predicted_step = std::max(predicted_step, clipped_level_step_); + RTC_DLOG(LS_INFO) << "[AGC2] Clipping predicted (volume down step: " + << predicted_step << ")"; + if (use_clipping_predictor_step_) { + step = predicted_step; + } + } + + if (clipping_detected || + (clipping_predicted && use_clipping_predictor_step_)) { + for (auto& state_ch : channel_controllers_) { + state_ch->HandleClipping(step); + } + frames_since_clipped_ = 0; + if (!!clipping_predictor_) { + clipping_predictor_->Reset(); + } + } + + AggregateChannelLevels(); +} + +std::optional InputVolumeController::RecommendInputVolume( + float speech_probability, + std::optional speech_level_dbfs) { + // Only process if applied input volume is set. + if (!applied_input_volume_.has_value()) { + RTC_LOG(LS_ERROR) << "[AGC2] Applied input volume not set."; + return std::nullopt; + } + + AggregateChannelLevels(); + const int volume_after_clipping_handling = recommended_input_volume_; + + if (!capture_output_used_) { + return applied_input_volume_; + } + + std::optional rms_error_db; + if (speech_level_dbfs.has_value()) { + // Compute the error for all frames (both speech and non-speech frames). + rms_error_db = GetSpeechLevelRmsErrorDb( + *speech_level_dbfs, target_range_min_dbfs_, target_range_max_dbfs_); + } + + for (auto& controller : channel_controllers_) { + controller->Process(rms_error_db, speech_probability); + } + + AggregateChannelLevels(); + if (volume_after_clipping_handling != recommended_input_volume_) { + // The recommended input volume was adjusted in order to match the target + // level. + UpdateHistogramOnRecommendedInputVolumeChangeToMatchTarget( + recommended_input_volume_); + } + + applied_input_volume_ = std::nullopt; + return recommended_input_volume(); +} + +void InputVolumeController::HandleCaptureOutputUsedChange( + bool capture_output_used) { + for (auto& controller : channel_controllers_) { + controller->HandleCaptureOutputUsedChange(capture_output_used); + } + + capture_output_used_ = capture_output_used; +} + +void InputVolumeController::SetAppliedInputVolume(int input_volume) { + applied_input_volume_ = input_volume; + + for (auto& controller : channel_controllers_) { + controller->set_stream_analog_level(input_volume); + } + + AggregateChannelLevels(); +} + +void InputVolumeController::AggregateChannelLevels() { + int new_recommended_input_volume = + channel_controllers_[0]->recommended_analog_level(); + channel_controlling_gain_ = 0; + for (size_t ch = 1; ch < channel_controllers_.size(); ++ch) { + int input_volume = channel_controllers_[ch]->recommended_analog_level(); + if (input_volume < new_recommended_input_volume) { + new_recommended_input_volume = input_volume; + channel_controlling_gain_ = static_cast(ch); + } + } + + // Enforce the minimum input volume when a recommendation is made. + if (applied_input_volume_.has_value() && *applied_input_volume_ > 0) { + new_recommended_input_volume = + std::max(new_recommended_input_volume, min_input_volume_); + } + + recommended_input_volume_ = new_recommended_input_volume; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_controller.h b/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_controller.h new file mode 100644 index 00000000..60e76d8e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_controller.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_INPUT_VOLUME_CONTROLLER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_INPUT_VOLUME_CONTROLLER_H_ + +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/audio_processing.h" +#include "modules/audio_processing/agc2/clipping_predictor.h" +#include "modules/audio_processing/audio_buffer.h" +#include "rtc_base/gtest_prod_util.h" + +namespace webrtc { + +class MonoInputVolumeController; + +// The input volume controller recommends what volume to use, handles volume +// changes and clipping detection and prediction. In particular, it handles +// changes triggered by the user (e.g., volume set to zero by a HW mute button). +// This class is not thread-safe. +// TODO(bugs.webrtc.org/7494): Use applied/recommended input volume naming +// convention. +class InputVolumeController final { + public: + // Config for the constructor. + struct Config { + // Minimum input volume that can be recommended. Not enforced when the + // applied input volume is zero outside startup. + int min_input_volume = 20; + // Lowest input volume level that will be applied in response to clipping. + int clipped_level_min = 70; + // Amount input volume 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.0f, 1.0f). + 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. + bool enable_clipping_predictor = true; + // Speech level target range (dBFS). If the speech level is in the range + // [`target_range_min_dbfs`, `target_range_max_dbfs`], no input volume + // adjustments are done based on the speech level. For speech levels below + // and above the range, the targets `target_range_min_dbfs` and + // `target_range_max_dbfs` are used, respectively. + int target_range_max_dbfs = -30; + int target_range_min_dbfs = -50; + // Number of wait frames between the recommended input volume updates. + int update_input_volume_wait_frames = 100; + // Speech probability threshold: speech probabilities below the threshold + // are considered silence. Limited to [0.0f, 1.0f]. + float speech_probability_threshold = 0.7f; + // Minimum speech frame ratio for volume updates to be allowed. Limited to + // [0.0f, 1.0f]. + float speech_ratio_threshold = 0.6f; + }; + + // Ctor. `num_capture_channels` specifies the number of channels for the audio + // passed to `AnalyzePreProcess()` and `Process()`. Clamps + // `config.startup_min_level` in the [12, 255] range. + InputVolumeController(int num_capture_channels, const Config& config); + + ~InputVolumeController(); + InputVolumeController(const InputVolumeController&) = delete; + InputVolumeController& operator=(const InputVolumeController&) = delete; + + // TODO(webrtc:7494): Integrate initialization into ctor and remove. + void Initialize(); + + // Analyzes `audio_buffer` before `RecommendInputVolume()` is called so tha + // the analysis can be performed before digital processing operations take + // place (e.g., echo cancellation). The analysis consists of input clipping + // detection and prediction (if enabled). + void AnalyzeInputAudio(int applied_input_volume, + const AudioBuffer& audio_buffer); + + // Adjusts the recommended input volume upwards/downwards based on the result + // of `AnalyzeInputAudio()` and on `speech_level_dbfs` (if specified). Must + // be called after `AnalyzeInputAudio()`. The value of `speech_probability` + // is expected to be in the range [0, 1] and `speech_level_dbfs` in the range + // [-90, 30] and both should be estimated after echo cancellation and noise + // suppression are applied. Returns a non-empty input volume recommendation if + // available. If `capture_output_used_` is true, returns the applied input + // volume. + std::optional RecommendInputVolume( + float speech_probability, + std::optional speech_level_dbfs); + + // Stores whether the capture output will be used or not. Call when the + // capture stream output has been flagged to be used/not-used. If unused, the + // controller disregards all incoming audio. + void HandleCaptureOutputUsedChange(bool capture_output_used); + + // Returns true if clipping prediction is enabled. + // TODO(bugs.webrtc.org/7494): Deprecate this method. + bool clipping_predictor_enabled() const { return !!clipping_predictor_; } + + // Returns true if clipping prediction is used to adjust the input volume. + // TODO(bugs.webrtc.org/7494): Deprecate this method. + bool use_clipping_predictor_step() const { + return use_clipping_predictor_step_; + } + + // Only use for testing: Use `RecommendInputVolume()` elsewhere. + // Returns the value of a member variable, needed for testing + // `AnalyzeInputAudio()`. + int recommended_input_volume() const { return recommended_input_volume_; } + + // Only use for testing. + bool capture_output_used() const { return capture_output_used_; } + + private: + friend class InputVolumeControllerTestHelper; + + FRIEND_TEST_ALL_PREFIXES(InputVolumeControllerTest, MinInputVolumeDefault); + FRIEND_TEST_ALL_PREFIXES(InputVolumeControllerTest, MinInputVolumeDisabled); + FRIEND_TEST_ALL_PREFIXES(InputVolumeControllerTest, + MinInputVolumeOutOfRangeAbove); + FRIEND_TEST_ALL_PREFIXES(InputVolumeControllerTest, + MinInputVolumeOutOfRangeBelow); + FRIEND_TEST_ALL_PREFIXES(InputVolumeControllerTest, MinInputVolumeEnabled50); + FRIEND_TEST_ALL_PREFIXES(InputVolumeControllerParametrizedTest, + ClippingParametersVerified); + + // Sets the applied input volume and resets the recommended input volume. + void SetAppliedInputVolume(int level); + + void AggregateChannelLevels(); + + const int num_capture_channels_; + + // Minimum input volume that can be recommended. + const int min_input_volume_; + + // TODO(bugs.webrtc.org/7494): Once + // `AudioProcessingImpl::recommended_stream_analog_level()` becomes a trivial + // getter, leave uninitialized. + // Recommended input volume. After `SetAppliedInputVolume()` is called it + // holds holds the observed input volume. Possibly updated by + // `AnalyzePreProcess()` and `Process()`; after these calls, holds the + // recommended input volume. + int recommended_input_volume_ = 0; + // Applied input volume. After `SetAppliedInputVolume()` is called it holds + // the current applied volume. + std::optional applied_input_volume_; + + bool capture_output_used_; + + // Clipping detection and prediction. + const int clipped_level_step_; + const float clipped_ratio_threshold_; + const int clipped_wait_frames_; + const std::unique_ptr clipping_predictor_; + const bool use_clipping_predictor_step_; + int frames_since_clipped_; + int clipping_rate_log_counter_; + float clipping_rate_log_; + + // Target range minimum and maximum. If the seech level is in the range + // [`target_range_min_dbfs`, `target_range_max_dbfs`], no volume adjustments + // take place. Instead, the digital gain controller is assumed to adapt to + // compensate for the speech level RMS error. + const int target_range_max_dbfs_; + const int target_range_min_dbfs_; + + // Channel controllers updating the gain upwards/downwards. + std::vector> channel_controllers_; + int channel_controlling_gain_ = 0; +}; + +// TODO(bugs.webrtc.org/7494): Use applied/recommended input volume naming +// convention. +class MonoInputVolumeController { + public: + MonoInputVolumeController(int min_input_volume_after_clipping, + int min_input_volume, + int update_input_volume_wait_frames, + float speech_probability_threshold, + float speech_ratio_threshold); + ~MonoInputVolumeController(); + MonoInputVolumeController(const MonoInputVolumeController&) = delete; + MonoInputVolumeController& operator=(const MonoInputVolumeController&) = + delete; + + void Initialize(); + void HandleCaptureOutputUsedChange(bool capture_output_used); + + // Sets the current input volume. + void set_stream_analog_level(int input_volume) { + recommended_input_volume_ = input_volume; + } + + // Lowers the recommended input volume in response to clipping based on the + // suggested reduction `clipped_level_step`. Must be called after + // `set_stream_analog_level()`. + void HandleClipping(int clipped_level_step); + + // TODO(bugs.webrtc.org/7494): Rename, audio not passed to the method anymore. + // Adjusts the recommended input volume upwards/downwards depending on the + // result of `HandleClipping()` and on `rms_error_dbfs`. Updates are only + // allowed for active speech segments and when `rms_error_dbfs` is not empty. + // Must be called after `HandleClipping()`. + void Process(std::optional rms_error_dbfs, float speech_probability); + + // Returns the recommended input volume. Must be called after `Process()`. + int recommended_analog_level() const { return recommended_input_volume_; } + + void ActivateLogging() { log_to_histograms_ = true; } + + int min_input_volume_after_clipping() const { + return min_input_volume_after_clipping_; + } + + // Only used for testing. + int min_input_volume() const { return min_input_volume_; } + + private: + // Sets a new input volume, after first checking that it hasn't been updated + // by the user, in which case no action is taken. + void SetInputVolume(int new_volume); + + // Sets the maximum input volume that the input volume controller is allowed + // to apply. The volume must be at least `kClippedLevelMin`. + void SetMaxLevel(int level); + + int CheckVolumeAndReset(); + + // Updates the recommended input volume. If the volume slider needs to be + // moved, we check first if the user has adjusted it, in which case we take no + // action and cache the updated level. + void UpdateInputVolume(int rms_error_dbfs); + + const int min_input_volume_; + const int min_input_volume_after_clipping_; + int max_input_volume_; + + int last_recommended_input_volume_ = 0; + + bool capture_output_used_ = true; + bool check_volume_on_next_process_ = true; + bool startup_ = true; + + // TODO(bugs.webrtc.org/7494): Create a separate member for the applied + // input volume. + // Recommended input volume. After `set_stream_analog_level()` is + // called, it holds the observed applied input volume. Possibly updated by + // `HandleClipping()` and `Process()`; after these calls, holds the + // recommended input volume. + int recommended_input_volume_ = 0; + + bool log_to_histograms_ = false; + + // Counters for frames and speech frames since the last update in the + // recommended input volume. + const int update_input_volume_wait_frames_; + int frames_since_update_input_volume_ = 0; + int speech_frames_since_update_input_volume_ = 0; + bool is_first_frame_ = true; + + // Speech probability threshold for a frame to be considered speech (instead + // of silence). Limited to [0.0f, 1.0f]. + const float speech_probability_threshold_; + // Minimum ratio of speech frames. Limited to [0.0f, 1.0f]. + const float speech_ratio_threshold_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_INPUT_VOLUME_CONTROLLER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_stats_reporter.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_stats_reporter.cc new file mode 100644 index 00000000..a0f33a73 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_stats_reporter.cc @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/input_volume_stats_reporter.h" + +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "rtc_base/strings/string_builder.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { +namespace { + +using InputVolumeType = InputVolumeStatsReporter::InputVolumeType; + +constexpr int kFramesIn60Seconds = 6000; +constexpr int kMinInputVolume = 0; +constexpr int kMaxInputVolume = 255; +constexpr int kMaxUpdate = kMaxInputVolume - kMinInputVolume; + +int ComputeAverageUpdate(int sum_updates, int num_updates) { + RTC_DCHECK_GE(sum_updates, 0); + RTC_DCHECK_LE(sum_updates, kMaxUpdate * kFramesIn60Seconds); + RTC_DCHECK_GE(num_updates, 0); + RTC_DCHECK_LE(num_updates, kFramesIn60Seconds); + if (num_updates == 0) { + return 0; + } + return std::round(static_cast(sum_updates) / + static_cast(num_updates)); +} + +constexpr absl::string_view MetricNamePrefix( + InputVolumeType input_volume_type) { + switch (input_volume_type) { + case InputVolumeType::kApplied: + return "WebRTC.Audio.Apm.AppliedInputVolume."; + case InputVolumeType::kRecommended: + return "WebRTC.Audio.Apm.RecommendedInputVolume."; + } +} + +metrics::Histogram* CreateVolumeHistogram(InputVolumeType input_volume_type) { + char buffer[64]; + SimpleStringBuilder builder(buffer); + builder << MetricNamePrefix(input_volume_type) << "OnChange"; + return metrics::HistogramFactoryGetCountsLinear(/*name=*/builder.str(), + /*min=*/1, + /*max=*/kMaxInputVolume, + /*bucket_count=*/50); +} + +metrics::Histogram* CreateRateHistogram(InputVolumeType input_volume_type, + absl::string_view name) { + char buffer[64]; + SimpleStringBuilder builder(buffer); + builder << MetricNamePrefix(input_volume_type) << name; + return metrics::HistogramFactoryGetCountsLinear(/*name=*/builder.str(), + /*min=*/1, + /*max=*/kFramesIn60Seconds, + /*bucket_count=*/50); +} + +metrics::Histogram* CreateAverageHistogram(InputVolumeType input_volume_type, + absl::string_view name) { + char buffer[64]; + SimpleStringBuilder builder(buffer); + builder << MetricNamePrefix(input_volume_type) << name; + return metrics::HistogramFactoryGetCountsLinear(/*name=*/builder.str(), + /*min=*/1, + /*max=*/kMaxUpdate, + /*bucket_count=*/50); +} + +} // namespace + +InputVolumeStatsReporter::InputVolumeStatsReporter(InputVolumeType type) + : histograms_( + {.on_volume_change = CreateVolumeHistogram(type), + .decrease_rate = CreateRateHistogram(type, "DecreaseRate"), + .decrease_average = CreateAverageHistogram(type, "DecreaseAverage"), + .increase_rate = CreateRateHistogram(type, "IncreaseRate"), + .increase_average = CreateAverageHistogram(type, "IncreaseAverage"), + .update_rate = CreateRateHistogram(type, "UpdateRate"), + .update_average = CreateAverageHistogram(type, "UpdateAverage")}), + cannot_log_stats_(!histograms_.AllPointersSet()) { + if (cannot_log_stats_) { + RTC_LOG(LS_WARNING) << "Will not log any `" << MetricNamePrefix(type) + << "*` histogram stats."; + } +} + +InputVolumeStatsReporter::~InputVolumeStatsReporter() = default; + +void InputVolumeStatsReporter::UpdateStatistics(int input_volume) { + if (cannot_log_stats_) { + // Since the stats cannot be logged, do not bother updating them. + return; + } + + RTC_DCHECK_GE(input_volume, kMinInputVolume); + RTC_DCHECK_LE(input_volume, kMaxInputVolume); + if (previous_input_volume_.has_value() && + input_volume != previous_input_volume_.value()) { + // Update stats when the input volume changes. + metrics::HistogramAdd(histograms_.on_volume_change, input_volume); + // Update stats that are periodically logged. + const int volume_change = input_volume - previous_input_volume_.value(); + if (volume_change < 0) { + ++volume_update_stats_.num_decreases; + volume_update_stats_.sum_decreases -= volume_change; + } else { + ++volume_update_stats_.num_increases; + volume_update_stats_.sum_increases += volume_change; + } + } + // Periodically log input volume change metrics. + if (++log_volume_update_stats_counter_ >= kFramesIn60Seconds) { + LogVolumeUpdateStats(); + volume_update_stats_ = {}; + log_volume_update_stats_counter_ = 0; + } + previous_input_volume_ = input_volume; +} + +void InputVolumeStatsReporter::LogVolumeUpdateStats() const { + // Decrease rate and average. + metrics::HistogramAdd(histograms_.decrease_rate, + volume_update_stats_.num_decreases); + if (volume_update_stats_.num_decreases > 0) { + int average_decrease = ComputeAverageUpdate( + volume_update_stats_.sum_decreases, volume_update_stats_.num_decreases); + metrics::HistogramAdd(histograms_.decrease_average, average_decrease); + } + // Increase rate and average. + metrics::HistogramAdd(histograms_.increase_rate, + volume_update_stats_.num_increases); + if (volume_update_stats_.num_increases > 0) { + int average_increase = ComputeAverageUpdate( + volume_update_stats_.sum_increases, volume_update_stats_.num_increases); + metrics::HistogramAdd(histograms_.increase_average, average_increase); + } + // Update rate and average. + int num_updates = + volume_update_stats_.num_decreases + volume_update_stats_.num_increases; + metrics::HistogramAdd(histograms_.update_rate, num_updates); + if (num_updates > 0) { + int average_update = ComputeAverageUpdate( + volume_update_stats_.sum_decreases + volume_update_stats_.sum_increases, + num_updates); + metrics::HistogramAdd(histograms_.update_average, average_update); + } +} + +void UpdateHistogramOnRecommendedInputVolumeChangeToMatchTarget(int volume) { + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.Apm.RecommendedInputVolume.OnChangeToMatchTarget", volume, + 1, kMaxInputVolume, 50); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_stats_reporter.h b/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_stats_reporter.h new file mode 100644 index 00000000..2f31aa07 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/input_volume_stats_reporter.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_INPUT_VOLUME_STATS_REPORTER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_INPUT_VOLUME_STATS_REPORTER_H_ + +#include + +#include "rtc_base/gtest_prod_util.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +// Input volume statistics calculator. Computes aggregate stats based on the +// framewise input volume observed by `UpdateStatistics()`. Periodically logs +// the statistics into a histogram. +class InputVolumeStatsReporter { + public: + enum class InputVolumeType { + kApplied = 0, + kRecommended = 1, + }; + + explicit InputVolumeStatsReporter(InputVolumeType input_volume_type); + InputVolumeStatsReporter(const InputVolumeStatsReporter&) = delete; + InputVolumeStatsReporter operator=(const InputVolumeStatsReporter&) = delete; + ~InputVolumeStatsReporter(); + + // Updates the stats based on `input_volume`. Periodically logs the stats into + // a histogram. + void UpdateStatistics(int input_volume); + + private: + FRIEND_TEST_ALL_PREFIXES(InputVolumeStatsReporterTest, + CheckVolumeUpdateStatsForEmptyStats); + FRIEND_TEST_ALL_PREFIXES(InputVolumeStatsReporterTest, + CheckVolumeUpdateStatsAfterNoVolumeChange); + FRIEND_TEST_ALL_PREFIXES(InputVolumeStatsReporterTest, + CheckVolumeUpdateStatsAfterVolumeIncrease); + FRIEND_TEST_ALL_PREFIXES(InputVolumeStatsReporterTest, + CheckVolumeUpdateStatsAfterVolumeDecrease); + FRIEND_TEST_ALL_PREFIXES(InputVolumeStatsReporterTest, + CheckVolumeUpdateStatsAfterReset); + + // Stores input volume update stats to enable calculation of update rate and + // average update separately for volume increases and decreases. + struct VolumeUpdateStats { + int num_decreases = 0; + int num_increases = 0; + int sum_decreases = 0; + int sum_increases = 0; + } volume_update_stats_; + + // Returns a copy of the stored statistics. Use only for testing. + VolumeUpdateStats volume_update_stats() const { return volume_update_stats_; } + + // Computes aggregate stat and logs them into a histogram. + void LogVolumeUpdateStats() const; + + // Histograms. + struct Histograms { + metrics::Histogram* const on_volume_change; + metrics::Histogram* const decrease_rate; + metrics::Histogram* const decrease_average; + metrics::Histogram* const increase_rate; + metrics::Histogram* const increase_average; + metrics::Histogram* const update_rate; + metrics::Histogram* const update_average; + bool AllPointersSet() const { + return !!on_volume_change && !!decrease_rate && !!decrease_average && + !!increase_rate && !!increase_average && !!update_rate && + !!update_average; + } + } histograms_; + + // True if the stats cannot be logged. + const bool cannot_log_stats_; + + int log_volume_update_stats_counter_ = 0; + std::optional previous_input_volume_ = std::nullopt; +}; + +// Updates the histogram that keeps track of recommended input volume changes +// required in order to match the target level in the input volume adaptation +// process. +void UpdateHistogramOnRecommendedInputVolumeChangeToMatchTarget(int volume); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_INPUT_VOLUME_STATS_REPORTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.cc new file mode 100644 index 00000000..d7651ddd --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.cc @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/interpolated_gain_curve.h" + +#include +#include + +#include "absl/strings/string_view.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +constexpr std::array + InterpolatedGainCurve::approximation_params_x_; + +constexpr std::array + InterpolatedGainCurve::approximation_params_m_; + +constexpr std::array + InterpolatedGainCurve::approximation_params_q_; + +InterpolatedGainCurve::InterpolatedGainCurve( + ApmDataDumper* apm_data_dumper, + absl::string_view histogram_name_prefix) + : region_logger_( + (StringBuilder("WebRTC.Audio.") + << histogram_name_prefix << ".FixedDigitalGainCurveRegion.Identity") + .str(), + (StringBuilder("WebRTC.Audio.") + << histogram_name_prefix << ".FixedDigitalGainCurveRegion.Knee") + .str(), + (StringBuilder("WebRTC.Audio.") + << histogram_name_prefix << ".FixedDigitalGainCurveRegion.Limiter") + .str(), + (StringBuilder("WebRTC.Audio.") + << histogram_name_prefix + << ".FixedDigitalGainCurveRegion.Saturation") + .str()), + apm_data_dumper_(apm_data_dumper) {} + +InterpolatedGainCurve::~InterpolatedGainCurve() { + if (stats_.available) { + RTC_DCHECK(apm_data_dumper_); + apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_identity", + stats_.look_ups_identity_region); + apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_knee", + stats_.look_ups_knee_region); + apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_limiter", + stats_.look_ups_limiter_region); + apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_saturation", + stats_.look_ups_saturation_region); + region_logger_.LogRegionStats(stats_); + } +} + +InterpolatedGainCurve::RegionLogger::RegionLogger( + absl::string_view identity_histogram_name, + absl::string_view knee_histogram_name, + absl::string_view limiter_histogram_name, + absl::string_view saturation_histogram_name) + : identity_histogram( + metrics::HistogramFactoryGetCounts(identity_histogram_name, + 1, + 10000, + 50)), + knee_histogram(metrics::HistogramFactoryGetCounts(knee_histogram_name, + 1, + 10000, + 50)), + limiter_histogram( + metrics::HistogramFactoryGetCounts(limiter_histogram_name, + 1, + 10000, + 50)), + saturation_histogram( + metrics::HistogramFactoryGetCounts(saturation_histogram_name, + 1, + 10000, + 50)) {} + +InterpolatedGainCurve::RegionLogger::~RegionLogger() = default; + +void InterpolatedGainCurve::RegionLogger::LogRegionStats( + const InterpolatedGainCurve::Stats& stats) const { + using Region = InterpolatedGainCurve::GainCurveRegion; + const int duration_s = + stats.region_duration_frames / (1000 / kFrameDurationMs); + + switch (stats.region) { + case Region::kIdentity: { + if (identity_histogram) { + metrics::HistogramAdd(identity_histogram, duration_s); + } + break; + } + case Region::kKnee: { + if (knee_histogram) { + metrics::HistogramAdd(knee_histogram, duration_s); + } + break; + } + case Region::kLimiter: { + if (limiter_histogram) { + metrics::HistogramAdd(limiter_histogram, duration_s); + } + break; + } + case Region::kSaturation: { + if (saturation_histogram) { + metrics::HistogramAdd(saturation_histogram, duration_s); + } + break; + } + default: { + RTC_DCHECK_NOTREACHED(); + } + } +} + +void InterpolatedGainCurve::UpdateStats(float input_level) const { + stats_.available = true; + + GainCurveRegion region; + + if (input_level < approximation_params_x_[0]) { + stats_.look_ups_identity_region++; + region = GainCurveRegion::kIdentity; + } else if (input_level < + approximation_params_x_[kInterpolatedGainCurveKneePoints - 1]) { + stats_.look_ups_knee_region++; + region = GainCurveRegion::kKnee; + } else if (input_level < kMaxInputLevelLinear) { + stats_.look_ups_limiter_region++; + region = GainCurveRegion::kLimiter; + } else { + stats_.look_ups_saturation_region++; + region = GainCurveRegion::kSaturation; + } + + if (region == stats_.region) { + ++stats_.region_duration_frames; + } else { + region_logger_.LogRegionStats(stats_); + + stats_.region_duration_frames = 0; + stats_.region = region; + } +} + +// Looks up a gain to apply given a non-negative input level. +// The cost of this operation depends on the region in which `input_level` +// falls. +// For the identity and the saturation regions the cost is O(1). +// For the other regions, namely knee and limiter, the cost is +// O(2 + log2(`LightkInterpolatedGainCurveTotalPoints`), plus O(1) for the +// linear interpolation (one product and one sum). +float InterpolatedGainCurve::LookUpGainToApply(float input_level) const { + UpdateStats(input_level); + + if (input_level <= approximation_params_x_[0]) { + // Identity region. + return 1.0f; + } + + if (input_level >= kMaxInputLevelLinear) { + // Saturating lower bound. The saturing samples exactly hit the clipping + // level. This method achieves has the lowest harmonic distorsion, but it + // may reduce the amplitude of the non-saturating samples too much. + return 32768.f / input_level; + } + + // Knee and limiter regions; find the linear piece index. Spelling + // out the complete type was the only way to silence both the clang + // plugin and the windows compilers. + std::array::const_iterator it = + std::lower_bound(approximation_params_x_.begin(), + approximation_params_x_.end(), input_level); + const size_t index = std::distance(approximation_params_x_.begin(), it) - 1; + RTC_DCHECK_LE(0, index); + RTC_DCHECK_LT(index, approximation_params_m_.size()); + RTC_DCHECK_LE(approximation_params_x_[index], input_level); + if (index < approximation_params_m_.size() - 1) { + RTC_DCHECK_LE(input_level, approximation_params_x_[index + 1]); + } + + // Piece-wise linear interploation. + const float gain = approximation_params_m_[index] * input_level + + approximation_params_q_[index]; + RTC_DCHECK_LE(0.f, gain); + return gain; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.h b/pkg/apm/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.h new file mode 100644 index 00000000..8dd3e48f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_INTERPOLATED_GAIN_CURVE_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_INTERPOLATED_GAIN_CURVE_H_ + +#include + +#include "absl/strings/string_view.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "rtc_base/gtest_prod_util.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +class ApmDataDumper; + +constexpr float kInputLevelScalingFactor = 32768.0f; + +// Defined as DbfsToLinear(kLimiterMaxInputLevelDbFs) +constexpr float kMaxInputLevelLinear = static_cast(36766.300710566735); + +// Interpolated gain curve using under-approximation to avoid saturation. +// +// The goal of this class is allowing fast look ups to get an accurate +// estimates of the gain to apply given an estimated input level. +class InterpolatedGainCurve { + public: + enum class GainCurveRegion { + kIdentity = 0, + kKnee = 1, + kLimiter = 2, + kSaturation = 3 + }; + + struct Stats { + // Region in which the output level equals the input one. + size_t look_ups_identity_region = 0; + // Smoothing between the identity and the limiter regions. + size_t look_ups_knee_region = 0; + // Limiter region in which the output and input levels are linearly related. + size_t look_ups_limiter_region = 0; + // Region in which saturation may occur since the input level is beyond the + // maximum expected by the limiter. + size_t look_ups_saturation_region = 0; + // True if stats have been populated. + bool available = false; + + // The current region, and for how many frames the level has been + // in that region. + GainCurveRegion region = GainCurveRegion::kIdentity; + int64_t region_duration_frames = 0; + }; + + InterpolatedGainCurve(ApmDataDumper* apm_data_dumper, + absl::string_view histogram_name_prefix); + ~InterpolatedGainCurve(); + + InterpolatedGainCurve(const InterpolatedGainCurve&) = delete; + InterpolatedGainCurve& operator=(const InterpolatedGainCurve&) = delete; + + Stats get_stats() const { return stats_; } + + // Given a non-negative input level (linear scale), a scalar factor to apply + // to a sub-frame is returned. + // Levels above kLimiterMaxInputLevelDbFs will be reduced to 0 dBFS + // after applying this gain + float LookUpGainToApply(float input_level) const; + + private: + // For comparing 'approximation_params_*_' with ones computed by + // ComputeInterpolatedGainCurve. + FRIEND_TEST_ALL_PREFIXES(GainController2InterpolatedGainCurve, + CheckApproximationParams); + + struct RegionLogger { + metrics::Histogram* identity_histogram; + metrics::Histogram* knee_histogram; + metrics::Histogram* limiter_histogram; + metrics::Histogram* saturation_histogram; + + RegionLogger(absl::string_view identity_histogram_name, + absl::string_view knee_histogram_name, + absl::string_view limiter_histogram_name, + absl::string_view saturation_histogram_name); + + ~RegionLogger(); + + void LogRegionStats(const InterpolatedGainCurve::Stats& stats) const; + } region_logger_; + + void UpdateStats(float input_level) const; + + ApmDataDumper* const apm_data_dumper_; + + static constexpr std::array + approximation_params_x_ = { + {30057.296875, 30148.986328125, 30240.67578125, 30424.052734375, + 30607.4296875, 30790.806640625, 30974.18359375, 31157.560546875, + 31340.939453125, 31524.31640625, 31707.693359375, 31891.0703125, + 32074.447265625, 32257.82421875, 32441.201171875, 32624.580078125, + 32807.95703125, 32991.33203125, 33174.7109375, 33358.08984375, + 33541.46484375, 33724.84375, 33819.53515625, 34009.5390625, + 34200.05859375, 34389.81640625, 34674.48828125, 35054.375, + 35434.86328125, 35814.81640625, 36195.16796875, 36575.03125}}; + static constexpr std::array + approximation_params_m_ = { + {-3.515235675877192989e-07, -1.050251626111275982e-06, + -2.085213736791047268e-06, -3.443004743530764244e-06, + -4.773849468620028347e-06, -6.077375928725814447e-06, + -7.353257842623861507e-06, -8.601219633419532329e-06, + -9.821013009059242904e-06, -1.101243378798244521e-05, + -1.217532644659513608e-05, -1.330956911260727793e-05, + -1.441507538402220234e-05, -1.549179251014720649e-05, + -1.653970684856176376e-05, -1.755882840370759368e-05, + -1.854918446042574942e-05, -1.951086778717581183e-05, + -2.044398024736437947e-05, -2.1348627342376858e-05, + -2.222496914328075945e-05, -2.265374678245279938e-05, + -2.242570917587727308e-05, -2.220122041762806475e-05, + -2.19802095671184361e-05, -2.176260204578284174e-05, + -2.133731686626560986e-05, -2.092481918225530535e-05, + -2.052459603874012828e-05, -2.013615448959171772e-05, + -1.975903069251216948e-05, -1.939277899509761482e-05}}; + + static constexpr std::array + approximation_params_q_ = { + {1.010565876960754395, 1.031631827354431152, 1.062929749488830566, + 1.104239225387573242, 1.144973039627075195, 1.185109615325927734, + 1.224629044532775879, 1.263512492179870605, 1.301741957664489746, + 1.339300632476806641, 1.376173257827758789, 1.412345528602600098, + 1.447803974151611328, 1.482536554336547852, 1.516532182693481445, + 1.549780607223510742, 1.582272171974182129, 1.613999366760253906, + 1.644955039024353027, 1.675132393836975098, 1.704526185989379883, + 1.718986630439758301, 1.711274504661560059, 1.703639745712280273, + 1.696081161499023438, 1.688597679138183594, 1.673851132392883301, + 1.659391283988952637, 1.645209431648254395, 1.631297469139099121, + 1.617647409439086914, 1.604251742362976074}}; + + // Stats. + mutable Stats stats_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_INTERPOLATED_GAIN_CURVE_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/limiter.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/limiter.cc new file mode 100644 index 00000000..072b79ed --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/limiter.cc @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/limiter.h" + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +// This constant affects the way scaling factors are interpolated for the first +// sub-frame of a frame. Only in the case in which the first sub-frame has an +// estimated level which is greater than the that of the previous analyzed +// sub-frame, linear interpolation is replaced with a power function which +// reduces the chances of over-shooting (and hence saturation), however reducing +// the fixed gain effectiveness. +constexpr float kAttackFirstSubframeInterpolationPower = 8.0f; + +void InterpolateFirstSubframe(float last_factor, + float current_factor, + ArrayView subframe) { + const int n = dchecked_cast(subframe.size()); + constexpr float p = kAttackFirstSubframeInterpolationPower; + for (int i = 0; i < n; ++i) { + subframe[i] = std::pow(1.f - i / n, p) * (last_factor - current_factor) + + current_factor; + } +} + +void ComputePerSampleSubframeFactors( + const std::array& scaling_factors, + MonoView per_sample_scaling_factors) { + const size_t num_subframes = scaling_factors.size() - 1; + const int subframe_size = CheckedDivExact( + SamplesPerChannel(per_sample_scaling_factors), num_subframes); + + // Handle first sub-frame differently in case of attack. + const bool is_attack = scaling_factors[0] > scaling_factors[1]; + if (is_attack) { + InterpolateFirstSubframe( + scaling_factors[0], scaling_factors[1], + per_sample_scaling_factors.subview(0, subframe_size)); + } + + for (size_t i = is_attack ? 1 : 0; i < num_subframes; ++i) { + const int subframe_start = i * subframe_size; + const float scaling_start = scaling_factors[i]; + const float scaling_end = scaling_factors[i + 1]; + const float scaling_diff = (scaling_end - scaling_start) / subframe_size; + for (int j = 0; j < subframe_size; ++j) { + per_sample_scaling_factors[subframe_start + j] = + scaling_start + scaling_diff * j; + } + } +} + +void ScaleSamples(MonoView per_sample_scaling_factors, + DeinterleavedView signal) { + const int samples_per_channel = signal.samples_per_channel(); + RTC_DCHECK_EQ(samples_per_channel, + SamplesPerChannel(per_sample_scaling_factors)); + for (size_t i = 0; i < signal.num_channels(); ++i) { + MonoView channel = signal[i]; + for (int j = 0; j < samples_per_channel; ++j) { + channel[j] = SafeClamp(channel[j] * per_sample_scaling_factors[j], + kMinFloatS16Value, kMaxFloatS16Value); + } + } +} +} // namespace + +Limiter::Limiter(ApmDataDumper* apm_data_dumper, + size_t samples_per_channel, + absl::string_view histogram_name) + : interp_gain_curve_(apm_data_dumper, histogram_name), + level_estimator_(samples_per_channel, apm_data_dumper), + apm_data_dumper_(apm_data_dumper) { + RTC_DCHECK_LE(samples_per_channel, kMaximalNumberOfSamplesPerChannel); +} + +Limiter::~Limiter() = default; + +void Limiter::Process(DeinterleavedView signal) { + RTC_DCHECK_LE(signal.samples_per_channel(), + kMaximalNumberOfSamplesPerChannel); + + const std::array level_estimate = + level_estimator_.ComputeLevel(signal); + + RTC_DCHECK_EQ(level_estimate.size() + 1, scaling_factors_.size()); + scaling_factors_[0] = last_scaling_factor_; + std::transform(level_estimate.begin(), level_estimate.end(), + scaling_factors_.begin() + 1, [this](float x) { + return interp_gain_curve_.LookUpGainToApply(x); + }); + + MonoView per_sample_scaling_factors(&per_sample_scaling_factors_[0], + signal.samples_per_channel()); + ComputePerSampleSubframeFactors(scaling_factors_, per_sample_scaling_factors); + ScaleSamples(per_sample_scaling_factors, signal); + + last_scaling_factor_ = scaling_factors_.back(); + + // Dump data for debug. + apm_data_dumper_->DumpRaw("agc2_limiter_last_scaling_factor", + last_scaling_factor_); + apm_data_dumper_->DumpRaw( + "agc2_limiter_region", + static_cast(interp_gain_curve_.get_stats().region)); +} + +InterpolatedGainCurve::Stats Limiter::GetGainCurveStats() const { + return interp_gain_curve_.get_stats(); +} + +void Limiter::SetSamplesPerChannel(size_t samples_per_channel) { + RTC_DCHECK_LE(samples_per_channel, kMaximalNumberOfSamplesPerChannel); + level_estimator_.SetSamplesPerChannel(samples_per_channel); +} + +void Limiter::Reset() { + level_estimator_.Reset(); +} + +float Limiter::LastAudioLevel() const { + return level_estimator_.LastAudioLevel(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/limiter.h b/pkg/apm/webrtc/modules/audio_processing/agc2/limiter.h new file mode 100644 index 00000000..55cb1a5b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/limiter.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_LIMITER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_LIMITER_H_ + +#include + +#include "absl/strings/string_view.h" +#include "api/audio/audio_frame.h" +#include "modules/audio_processing/agc2/fixed_digital_level_estimator.h" +#include "modules/audio_processing/agc2/interpolated_gain_curve.h" +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { +class ApmDataDumper; + +class Limiter { + public: + // See `SetSamplesPerChannel()` for valid values for `samples_per_channel`. + Limiter(ApmDataDumper* apm_data_dumper, + size_t samples_per_channel, + absl::string_view histogram_name_prefix); + + Limiter(const Limiter& limiter) = delete; + Limiter& operator=(const Limiter& limiter) = delete; + ~Limiter(); + + // Applies limiter and hard-clipping to `signal`. + void Process(DeinterleavedView signal); + + InterpolatedGainCurve::Stats GetGainCurveStats() const; + + // Supported values must be + // * Supported by FixedDigitalLevelEstimator + // * Below or equal to kMaximalNumberOfSamplesPerChannel so that samples + // fit in the per_sample_scaling_factors_ array. + void SetSamplesPerChannel(size_t samples_per_channel); + + // Resets the internal state. + void Reset(); + + float LastAudioLevel() const; + + private: + const InterpolatedGainCurve interp_gain_curve_; + FixedDigitalLevelEstimator level_estimator_; + ApmDataDumper* const apm_data_dumper_ = nullptr; + + // Work array containing the sub-frame scaling factors to be interpolated. + std::array scaling_factors_ = {}; + std::array + per_sample_scaling_factors_ = {}; + float last_scaling_factor_ = 1.f; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_LIMITER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.cc new file mode 100644 index 00000000..d47c0b2e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/limiter_db_gain_curve.h" + +#include + +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +double ComputeKneeStart(double max_input_level_db, + double knee_smoothness_db, + double compression_ratio) { + RTC_CHECK_LT((compression_ratio - 1.0) * knee_smoothness_db / + (2.0 * compression_ratio), + max_input_level_db); + return -knee_smoothness_db / 2.0 - + max_input_level_db / (compression_ratio - 1.0); +} + +std::array ComputeKneeRegionPolynomial(double knee_start_dbfs, + double knee_smoothness_db, + double compression_ratio) { + const double a = (1.0 - compression_ratio) / + (2.0 * knee_smoothness_db * compression_ratio); + const double b = 1.0 - 2.0 * a * knee_start_dbfs; + const double c = a * knee_start_dbfs * knee_start_dbfs; + return {{a, b, c}}; +} + +double ComputeLimiterD1(double max_input_level_db, double compression_ratio) { + return (std::pow(10.0, -max_input_level_db / (20.0 * compression_ratio)) * + (1.0 - compression_ratio) / compression_ratio) / + kMaxAbsFloatS16Value; +} + +constexpr double ComputeLimiterD2(double compression_ratio) { + return (1.0 - 2.0 * compression_ratio) / compression_ratio; +} + +double ComputeLimiterI2(double max_input_level_db, + double compression_ratio, + double gain_curve_limiter_i1) { + RTC_CHECK_NE(gain_curve_limiter_i1, 0.f); + return std::pow(10.0, -max_input_level_db / (20.0 * compression_ratio)) / + gain_curve_limiter_i1 / + std::pow(kMaxAbsFloatS16Value, gain_curve_limiter_i1 - 1); +} + +} // namespace + +LimiterDbGainCurve::LimiterDbGainCurve() + : max_input_level_linear_(DbfsToFloatS16(max_input_level_db_)), + knee_start_dbfs_(ComputeKneeStart(max_input_level_db_, + knee_smoothness_db_, + compression_ratio_)), + knee_start_linear_(DbfsToFloatS16(knee_start_dbfs_)), + limiter_start_dbfs_(knee_start_dbfs_ + knee_smoothness_db_), + limiter_start_linear_(DbfsToFloatS16(limiter_start_dbfs_)), + knee_region_polynomial_(ComputeKneeRegionPolynomial(knee_start_dbfs_, + knee_smoothness_db_, + compression_ratio_)), + gain_curve_limiter_d1_( + ComputeLimiterD1(max_input_level_db_, compression_ratio_)), + gain_curve_limiter_d2_(ComputeLimiterD2(compression_ratio_)), + gain_curve_limiter_i1_(1.0 / compression_ratio_), + gain_curve_limiter_i2_(ComputeLimiterI2(max_input_level_db_, + compression_ratio_, + gain_curve_limiter_i1_)) { + static_assert(knee_smoothness_db_ > 0.0f, ""); + static_assert(compression_ratio_ > 1.0f, ""); + RTC_CHECK_GE(max_input_level_db_, knee_start_dbfs_ + knee_smoothness_db_); +} + +constexpr double LimiterDbGainCurve::max_input_level_db_; +constexpr double LimiterDbGainCurve::knee_smoothness_db_; +constexpr double LimiterDbGainCurve::compression_ratio_; + +double LimiterDbGainCurve::GetOutputLevelDbfs(double input_level_dbfs) const { + if (input_level_dbfs < knee_start_dbfs_) { + return input_level_dbfs; + } else if (input_level_dbfs < limiter_start_dbfs_) { + return GetKneeRegionOutputLevelDbfs(input_level_dbfs); + } + return GetCompressorRegionOutputLevelDbfs(input_level_dbfs); +} + +double LimiterDbGainCurve::GetGainLinear(double input_level_linear) const { + if (input_level_linear < knee_start_linear_) { + return 1.0; + } + return DbfsToFloatS16( + GetOutputLevelDbfs(FloatS16ToDbfs(input_level_linear))) / + input_level_linear; +} + +// Computes the first derivative of GetGainLinear() in `x`. +double LimiterDbGainCurve::GetGainFirstDerivativeLinear(double x) const { + // Beyond-knee region only. + RTC_CHECK_GE(x, limiter_start_linear_ - 1e-7 * kMaxAbsFloatS16Value); + return gain_curve_limiter_d1_ * + std::pow(x / kMaxAbsFloatS16Value, gain_curve_limiter_d2_); +} + +// Computes the integral of GetGainLinear() in the range [x0, x1]. +double LimiterDbGainCurve::GetGainIntegralLinear(double x0, double x1) const { + RTC_CHECK_LE(x0, x1); // Valid interval. + RTC_CHECK_GE(x0, limiter_start_linear_); // Beyond-knee region only. + auto limiter_integral = [this](const double& x) { + return gain_curve_limiter_i2_ * std::pow(x, gain_curve_limiter_i1_); + }; + return limiter_integral(x1) - limiter_integral(x0); +} + +double LimiterDbGainCurve::GetKneeRegionOutputLevelDbfs( + double input_level_dbfs) const { + return knee_region_polynomial_[0] * input_level_dbfs * input_level_dbfs + + knee_region_polynomial_[1] * input_level_dbfs + + knee_region_polynomial_[2]; +} + +double LimiterDbGainCurve::GetCompressorRegionOutputLevelDbfs( + double input_level_dbfs) const { + return (input_level_dbfs - max_input_level_db_) / compression_ratio_; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.h b/pkg/apm/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.h new file mode 100644 index 00000000..9086e267 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_LIMITER_DB_GAIN_CURVE_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_LIMITER_DB_GAIN_CURVE_H_ + +#include + +#include "modules/audio_processing/agc2/agc2_testing_common.h" + +namespace webrtc { + +// A class for computing a limiter gain curve (in dB scale) given a set of +// hard-coded parameters (namely, kLimiterDbGainCurveMaxInputLevelDbFs, +// kLimiterDbGainCurveKneeSmoothnessDb, and +// kLimiterDbGainCurveCompressionRatio). The generated curve consists of four +// regions: identity (linear), knee (quadratic polynomial), compression +// (linear), saturation (linear). The aforementioned constants are used to shape +// the different regions. +class LimiterDbGainCurve { + public: + LimiterDbGainCurve(); + + double max_input_level_db() const { return max_input_level_db_; } + double max_input_level_linear() const { return max_input_level_linear_; } + double knee_start_linear() const { return knee_start_linear_; } + double limiter_start_linear() const { return limiter_start_linear_; } + + // These methods can be marked 'constexpr' in C++ 14. + double GetOutputLevelDbfs(double input_level_dbfs) const; + double GetGainLinear(double input_level_linear) const; + double GetGainFirstDerivativeLinear(double x) const; + double GetGainIntegralLinear(double x0, double x1) const; + + private: + double GetKneeRegionOutputLevelDbfs(double input_level_dbfs) const; + double GetCompressorRegionOutputLevelDbfs(double input_level_dbfs) const; + + static constexpr double max_input_level_db_ = test::kLimiterMaxInputLevelDbFs; + static constexpr double knee_smoothness_db_ = test::kLimiterKneeSmoothnessDb; + static constexpr double compression_ratio_ = test::kLimiterCompressionRatio; + + const double max_input_level_linear_; + + // Do not modify signal with level <= knee_start_dbfs_. + const double knee_start_dbfs_; + const double knee_start_linear_; + + // The upper end of the knee region, which is between knee_start_dbfs_ and + // limiter_start_dbfs_. + const double limiter_start_dbfs_; + const double limiter_start_linear_; + + // Coefficients {a, b, c} of the knee region polynomial + // ax^2 + bx + c in the DB scale. + const std::array knee_region_polynomial_; + + // Parameters for the computation of the first derivative of GetGainLinear(). + const double gain_curve_limiter_d1_; + const double gain_curve_limiter_d2_; + + // Parameters for the computation of the integral of GetGainLinear(). + const double gain_curve_limiter_i1_; + const double gain_curve_limiter_i2_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_LIMITER_DB_GAIN_CURVE_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/noise_level_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/noise_level_estimator.cc new file mode 100644 index 00000000..c43738aa --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/noise_level_estimator.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/noise_level_estimator.h" + +#include + +#include +#include +#include + +#include "api/audio/audio_view.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr int kFramesPerSecond = 100; + +float FrameEnergy(DeinterleavedView audio) { + float energy = 0.0f; + for (size_t k = 0; k < audio.num_channels(); ++k) { + MonoView ch = audio[k]; + float channel_energy = + std::accumulate(ch.begin(), ch.end(), 0.0f, + [](float a, float b) -> float { return a + b * b; }); + energy = std::max(channel_energy, energy); + } + return energy; +} + +float EnergyToDbfs(float signal_energy, int num_samples) { + RTC_DCHECK_GE(signal_energy, 0.0f); + const float rms_square = signal_energy / num_samples; + constexpr float kMinDbfs = -90.30899869919436f; + if (rms_square <= 1.0f) { + return kMinDbfs; + } + return 10.0f * std::log10(rms_square) + kMinDbfs; +} + +// Updates the noise floor with instant decay and slow attack. This tuning is +// specific for AGC2, so that (i) it can promptly increase the gain if the noise +// floor drops (instant decay) and (ii) in case of music or fast speech, due to +// which the noise floor can be overestimated, the gain reduction is slowed +// down. +float SmoothNoiseFloorEstimate(float current_estimate, float new_estimate) { + constexpr float kAttack = 0.5f; + if (current_estimate < new_estimate) { + // Attack phase. + return kAttack * new_estimate + (1.0f - kAttack) * current_estimate; + } + // Instant attack. + return new_estimate; +} + +class NoiseFloorEstimator : public NoiseLevelEstimator { + public: + // Update the noise floor every 5 seconds. + static constexpr int kUpdatePeriodNumFrames = 500; + static_assert(kUpdatePeriodNumFrames >= 200, + "A too small value may cause noise level overestimation."); + static_assert(kUpdatePeriodNumFrames <= 1500, + "A too large value may make AGC2 slow at reacting to increased " + "noise levels."); + + NoiseFloorEstimator(ApmDataDumper* data_dumper) : data_dumper_(data_dumper) { + RTC_DCHECK(data_dumper_); + // Initially assume that 48 kHz will be used. `Analyze()` will detect the + // used sample rate and call `Initialize()` again if needed. + Initialize(/*sample_rate_hz=*/48000); + } + NoiseFloorEstimator(const NoiseFloorEstimator&) = delete; + NoiseFloorEstimator& operator=(const NoiseFloorEstimator&) = delete; + ~NoiseFloorEstimator() = default; + + float Analyze(DeinterleavedView frame) override { + // Detect sample rate changes. + const int sample_rate_hz = + static_cast(frame.samples_per_channel() * kFramesPerSecond); + if (sample_rate_hz != sample_rate_hz_) { + Initialize(sample_rate_hz); + } + + const float frame_energy = FrameEnergy(frame); + if (frame_energy <= min_noise_energy_) { + // Ignore frames when muted or below the minimum measurable energy. + if (data_dumper_) + data_dumper_->DumpRaw("agc2_noise_floor_estimator_preliminary_level", + noise_energy_); + return EnergyToDbfs(noise_energy_, + static_cast(frame.samples_per_channel())); + } + + if (preliminary_noise_energy_set_) { + preliminary_noise_energy_ = + std::min(preliminary_noise_energy_, frame_energy); + } else { + preliminary_noise_energy_ = frame_energy; + preliminary_noise_energy_set_ = true; + } + if (data_dumper_) + data_dumper_->DumpRaw("agc2_noise_floor_estimator_preliminary_level", + preliminary_noise_energy_); + + if (counter_ == 0) { + // Full period observed. + first_period_ = false; + // Update the estimated noise floor energy with the preliminary + // estimation. + noise_energy_ = SmoothNoiseFloorEstimate( + /*current_estimate=*/noise_energy_, + /*new_estimate=*/preliminary_noise_energy_); + // Reset for a new observation period. + counter_ = kUpdatePeriodNumFrames; + preliminary_noise_energy_set_ = false; + } else if (first_period_) { + // While analyzing the signal during the initial period, continuously + // update the estimated noise energy, which is monotonic. + noise_energy_ = preliminary_noise_energy_; + counter_--; + } else { + // During the observation period it's only allowed to lower the energy. + noise_energy_ = std::min(noise_energy_, preliminary_noise_energy_); + counter_--; + } + + float noise_rms_dbfs = EnergyToDbfs( + noise_energy_, static_cast(frame.samples_per_channel())); + if (data_dumper_) + data_dumper_->DumpRaw("agc2_noise_rms_dbfs", noise_rms_dbfs); + + return noise_rms_dbfs; + } + + private: + void Initialize(int sample_rate_hz) { + sample_rate_hz_ = sample_rate_hz; + first_period_ = true; + preliminary_noise_energy_set_ = false; + // Initialize the minimum noise energy to -84 dBFS. + min_noise_energy_ = sample_rate_hz * 2.0f * 2.0f / kFramesPerSecond; + preliminary_noise_energy_ = min_noise_energy_; + noise_energy_ = min_noise_energy_; + counter_ = kUpdatePeriodNumFrames; + } + + ApmDataDumper* const data_dumper_; + int sample_rate_hz_; + float min_noise_energy_; + bool first_period_; + bool preliminary_noise_energy_set_; + float preliminary_noise_energy_; + float noise_energy_; + int counter_; +}; + +} // namespace + +std::unique_ptr CreateNoiseFloorEstimator( + ApmDataDumper* data_dumper) { + return std::make_unique(data_dumper); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/noise_level_estimator.h b/pkg/apm/webrtc/modules/audio_processing/agc2/noise_level_estimator.h new file mode 100644 index 00000000..8df4cbc9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/noise_level_estimator.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_NOISE_LEVEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_NOISE_LEVEL_ESTIMATOR_H_ + +#include + +#include "api/audio/audio_view.h" + +namespace webrtc { +class ApmDataDumper; + +// Noise level estimator interface. +class NoiseLevelEstimator { + public: + virtual ~NoiseLevelEstimator() = default; + // Analyzes a 10 ms `frame`, updates the noise level estimation and returns + // the value for the latter in dBFS. + virtual float Analyze(DeinterleavedView frame) = 0; +}; + +// Creates a noise level estimator based on noise floor detection. +std::unique_ptr CreateNoiseFloorEstimator( + ApmDataDumper* data_dumper); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_NOISE_LEVEL_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc new file mode 100644 index 00000000..f079b010 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/rnn_vad/auto_correlation.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +constexpr int kAutoCorrelationFftOrder = 9; // Length-512 FFT. +static_assert(1 << kAutoCorrelationFftOrder > + kNumLags12kHz + kBufSize12kHz - kMaxPitch12kHz, + ""); + +} // namespace + +AutoCorrelationCalculator::AutoCorrelationCalculator() + : fft_(1 << kAutoCorrelationFftOrder, Pffft::FftType::kReal), + tmp_(fft_.CreateBuffer()), + X_(fft_.CreateBuffer()), + H_(fft_.CreateBuffer()) {} + +AutoCorrelationCalculator::~AutoCorrelationCalculator() = default; + +// The auto-correlations coefficients are computed as follows: +// |.........|...........| <- pitch buffer +// [ x (fixed) ] +// [ y_0 ] +// [ y_{m-1} ] +// x and y are sub-array of equal length; x is never moved, whereas y slides. +// The cross-correlation between y_0 and x corresponds to the auto-correlation +// for the maximum pitch period. Hence, the first value in `auto_corr` has an +// inverted lag equal to 0 that corresponds to a lag equal to the maximum +// pitch period. +void AutoCorrelationCalculator::ComputeOnPitchBuffer( + ArrayView pitch_buf, + ArrayView auto_corr) { + RTC_DCHECK_LT(auto_corr.size(), kMaxPitch12kHz); + RTC_DCHECK_GT(pitch_buf.size(), kMaxPitch12kHz); + constexpr int kFftFrameSize = 1 << kAutoCorrelationFftOrder; + constexpr int kConvolutionLength = kBufSize12kHz - kMaxPitch12kHz; + static_assert(kConvolutionLength == kFrameSize20ms12kHz, + "Mismatch between pitch buffer size, frame size and maximum " + "pitch period."); + static_assert(kFftFrameSize > kNumLags12kHz + kConvolutionLength, + "The FFT length is not sufficiently big to avoid cyclic " + "convolution errors."); + auto tmp = tmp_->GetView(); + + // Compute the FFT for the reversed reference frame - i.e., + // pitch_buf[-kConvolutionLength:]. + std::reverse_copy(pitch_buf.end() - kConvolutionLength, pitch_buf.end(), + tmp.begin()); + std::fill(tmp.begin() + kConvolutionLength, tmp.end(), 0.f); + fft_.ForwardTransform(*tmp_, H_.get(), /*ordered=*/false); + + // Compute the FFT for the sliding frames chunk. The sliding frames are + // defined as pitch_buf[i:i+kConvolutionLength] where i in + // [0, kNumLags12kHz). The chunk includes all of them, hence it is + // defined as pitch_buf[:kNumLags12kHz+kConvolutionLength]. + std::copy(pitch_buf.begin(), + pitch_buf.begin() + kConvolutionLength + kNumLags12kHz, + tmp.begin()); + std::fill(tmp.begin() + kNumLags12kHz + kConvolutionLength, tmp.end(), 0.f); + fft_.ForwardTransform(*tmp_, X_.get(), /*ordered=*/false); + + // Convolve in the frequency domain. + constexpr float kScalingFactor = 1.f / static_cast(kFftFrameSize); + std::fill(tmp.begin(), tmp.end(), 0.f); + fft_.FrequencyDomainConvolve(*X_, *H_, tmp_.get(), kScalingFactor); + fft_.BackwardTransform(*tmp_, tmp_.get(), /*ordered=*/false); + + // Extract the auto-correlation coefficients. + std::copy(tmp.begin() + kConvolutionLength - 1, + tmp.begin() + kConvolutionLength + kNumLags12kHz - 1, + auto_corr.begin()); +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.h new file mode 100644 index 00000000..127b2598 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_AUTO_CORRELATION_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_AUTO_CORRELATION_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/utility/pffft_wrapper.h" + +namespace webrtc { +namespace rnn_vad { + +// Class to compute the auto correlation on the pitch buffer for a target pitch +// interval. +class AutoCorrelationCalculator { + public: + AutoCorrelationCalculator(); + AutoCorrelationCalculator(const AutoCorrelationCalculator&) = delete; + AutoCorrelationCalculator& operator=(const AutoCorrelationCalculator&) = + delete; + ~AutoCorrelationCalculator(); + + // Computes the auto-correlation coefficients for a target pitch interval. + // `auto_corr` indexes are inverted lags. + void ComputeOnPitchBuffer(ArrayView pitch_buf, + ArrayView auto_corr); + + private: + Pffft fft_; + std::unique_ptr tmp_; + std::unique_ptr X_; + std::unique_ptr H_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_AUTO_CORRELATION_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/avx2/avx2.go b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/avx2/avx2.go new file mode 100644 index 00000000..2217ca34 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/avx2/avx2.go @@ -0,0 +1,8 @@ +//go:build console && amd64 + +package avx2 + +// #cgo CXXFLAGS: -I${SRCDIR}/../../../../.. -I${SRCDIR}/../../../../../third_party/abseil-cpp -std=c++17 -fno-rtti -march=haswell -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/avx2/vector_math_avx2.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/avx2/vector_math_avx2.cc new file mode 100644 index 00000000..71466dab --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/avx2/vector_math_avx2.cc @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/rnn_vad/vector_math.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { +namespace rnn_vad { + +float VectorMath::DotProductAvx2(ArrayView x, + ArrayView y) const { + RTC_DCHECK(cpu_features_.avx2); + RTC_DCHECK_EQ(x.size(), y.size()); + __m256 accumulator = _mm256_setzero_ps(); + constexpr int kBlockSizeLog2 = 3; + constexpr int kBlockSize = 1 << kBlockSizeLog2; + const int incomplete_block_index = (x.size() >> kBlockSizeLog2) + << kBlockSizeLog2; + for (int i = 0; i < incomplete_block_index; i += kBlockSize) { + RTC_DCHECK_LE(i + kBlockSize, x.size()); + const __m256 x_i = _mm256_loadu_ps(&x[i]); + const __m256 y_i = _mm256_loadu_ps(&y[i]); + accumulator = _mm256_fmadd_ps(x_i, y_i, accumulator); + } + // Reduce `accumulator` by addition. + __m128 high = _mm256_extractf128_ps(accumulator, 1); + __m128 low = _mm256_extractf128_ps(accumulator, 0); + low = _mm_add_ps(high, low); + high = _mm_movehl_ps(high, low); + low = _mm_add_ps(high, low); + high = _mm_shuffle_ps(low, low, 1); + low = _mm_add_ss(high, low); + float dot_product = _mm_cvtss_f32(low); + // Add the result for the last block if incomplete. + for (int i = incomplete_block_index; i < dchecked_cast(x.size()); ++i) { + dot_product += x[i] * y[i]; + } + return dot_product; +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/common.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/common.h new file mode 100644 index 00000000..c0993732 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/common.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_COMMON_H_ + +#include + +namespace webrtc { +namespace rnn_vad { + +constexpr double kPi = 3.14159265358979323846; + +constexpr int kSampleRate24kHz = 24000; +constexpr int kFrameSize10ms24kHz = kSampleRate24kHz / 100; +constexpr int kFrameSize20ms24kHz = kFrameSize10ms24kHz * 2; + +// Pitch buffer. +constexpr int kMinPitch24kHz = kSampleRate24kHz / 800; // 0.00125 s. +constexpr int kMaxPitch24kHz = kSampleRate24kHz / 62.5; // 0.016 s. +constexpr int kBufSize24kHz = kMaxPitch24kHz + kFrameSize20ms24kHz; +static_assert((kBufSize24kHz & 1) == 0, "The buffer size must be even."); + +// 24 kHz analysis. +// Define a higher minimum pitch period for the initial search. This is used to +// avoid searching for very short periods, for which a refinement step is +// responsible. +constexpr int kInitialMinPitch24kHz = 3 * kMinPitch24kHz; +static_assert(kMinPitch24kHz < kInitialMinPitch24kHz, ""); +static_assert(kInitialMinPitch24kHz < kMaxPitch24kHz, ""); +static_assert(kMaxPitch24kHz > kInitialMinPitch24kHz, ""); +// Number of (inverted) lags during the initial pitch search phase at 24 kHz. +constexpr int kInitialNumLags24kHz = kMaxPitch24kHz - kInitialMinPitch24kHz; +// Number of (inverted) lags during the pitch search refinement phase at 24 kHz. +constexpr int kRefineNumLags24kHz = kMaxPitch24kHz + 1; +static_assert( + kRefineNumLags24kHz > kInitialNumLags24kHz, + "The refinement step must search the pitch in an extended pitch range."); + +// 12 kHz analysis. +constexpr int kSampleRate12kHz = 12000; +constexpr int kFrameSize10ms12kHz = kSampleRate12kHz / 100; +constexpr int kFrameSize20ms12kHz = kFrameSize10ms12kHz * 2; +constexpr int kBufSize12kHz = kBufSize24kHz / 2; +constexpr int kInitialMinPitch12kHz = kInitialMinPitch24kHz / 2; +constexpr int kMaxPitch12kHz = kMaxPitch24kHz / 2; +static_assert(kMaxPitch12kHz > kInitialMinPitch12kHz, ""); +// The inverted lags for the pitch interval [`kInitialMinPitch12kHz`, +// `kMaxPitch12kHz`] are in the range [0, `kNumLags12kHz`]. +constexpr int kNumLags12kHz = kMaxPitch12kHz - kInitialMinPitch12kHz; + +// 48 kHz constants. +constexpr int kMinPitch48kHz = kMinPitch24kHz * 2; +constexpr int kMaxPitch48kHz = kMaxPitch24kHz * 2; + +// Spectral features. +constexpr int kNumBands = 22; +constexpr int kNumLowerBands = 6; +static_assert((0 < kNumLowerBands) && (kNumLowerBands < kNumBands), ""); +constexpr int kCepstralCoeffsHistorySize = 8; +static_assert(kCepstralCoeffsHistorySize > 2, + "The history size must at least be 3 to compute first and second " + "derivatives."); + +constexpr int kFeatureVectorSize = 42; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_COMMON_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.cc new file mode 100644 index 00000000..b0902814 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.cc @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/rnn_vad/features_extraction.h" + +#include + +#include "modules/audio_processing/agc2/rnn_vad/lp_residual.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +// Computed as `scipy.signal.butter(N=2, Wn=60/24000, btype='highpass')`. +constexpr BiQuadFilter::Config kHpfConfig24k{ + {0.99446179f, -1.98892358f, 0.99446179f}, + {-1.98889291f, 0.98895425f}}; + +} // namespace + +FeaturesExtractor::FeaturesExtractor(const AvailableCpuFeatures& cpu_features) + : use_high_pass_filter_(false), + hpf_(kHpfConfig24k), + pitch_buf_24kHz_(), + pitch_buf_24kHz_view_(pitch_buf_24kHz_.GetBufferView()), + lp_residual_(kBufSize24kHz), + lp_residual_view_(lp_residual_.data(), kBufSize24kHz), + pitch_estimator_(cpu_features), + reference_frame_view_(pitch_buf_24kHz_.GetMostRecentValuesView()) { + RTC_DCHECK_EQ(kBufSize24kHz, lp_residual_.size()); + Reset(); +} + +FeaturesExtractor::~FeaturesExtractor() = default; + +void FeaturesExtractor::Reset() { + pitch_buf_24kHz_.Reset(); + spectral_features_extractor_.Reset(); + if (use_high_pass_filter_) { + hpf_.Reset(); + } +} + +bool FeaturesExtractor::CheckSilenceComputeFeatures( + ArrayView samples, + ArrayView feature_vector) { + // Pre-processing. + if (use_high_pass_filter_) { + std::array samples_filtered; + hpf_.Process(samples, samples_filtered); + // Feed buffer with the pre-processed version of `samples`. + pitch_buf_24kHz_.Push(samples_filtered); + } else { + // Feed buffer with `samples`. + pitch_buf_24kHz_.Push(samples); + } + // Extract the LP residual. + float lpc_coeffs[kNumLpcCoefficients]; + ComputeAndPostProcessLpcCoefficients(pitch_buf_24kHz_view_, lpc_coeffs); + ComputeLpResidual(lpc_coeffs, pitch_buf_24kHz_view_, lp_residual_view_); + // Estimate pitch on the LP-residual and write the normalized pitch period + // into the output vector (normalization based on training data stats). + pitch_period_48kHz_ = pitch_estimator_.Estimate(lp_residual_view_); + feature_vector[kFeatureVectorSize - 2] = 0.01f * (pitch_period_48kHz_ - 300); + // Extract lagged frames (according to the estimated pitch period). + RTC_DCHECK_LE(pitch_period_48kHz_ / 2, kMaxPitch24kHz); + auto lagged_frame = pitch_buf_24kHz_view_.subview( + kMaxPitch24kHz - pitch_period_48kHz_ / 2, kFrameSize20ms24kHz); + // Analyze reference and lagged frames checking if silence has been detected + // and write the feature vector. + return spectral_features_extractor_.CheckSilenceComputeFeatures( + reference_frame_view_, {lagged_frame.data(), kFrameSize20ms24kHz}, + {feature_vector.data() + kNumLowerBands, kNumBands - kNumLowerBands}, + {feature_vector.data(), kNumLowerBands}, + {feature_vector.data() + kNumBands, kNumLowerBands}, + {feature_vector.data() + kNumBands + kNumLowerBands, kNumLowerBands}, + {feature_vector.data() + kNumBands + 2 * kNumLowerBands, kNumLowerBands}, + &feature_vector[kFeatureVectorSize - 1]); +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.h new file mode 100644 index 00000000..3fe14a49 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_FEATURES_EXTRACTION_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_FEATURES_EXTRACTION_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/biquad_filter.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/pitch_search.h" +#include "modules/audio_processing/agc2/rnn_vad/sequence_buffer.h" +#include "modules/audio_processing/agc2/rnn_vad/spectral_features.h" + +namespace webrtc { +namespace rnn_vad { + +// Feature extractor to feed the VAD RNN. +class FeaturesExtractor { + public: + explicit FeaturesExtractor(const AvailableCpuFeatures& cpu_features); + FeaturesExtractor(const FeaturesExtractor&) = delete; + FeaturesExtractor& operator=(const FeaturesExtractor&) = delete; + ~FeaturesExtractor(); + void Reset(); + // Analyzes the samples, computes the feature vector and returns true if + // silence is detected (false if not). When silence is detected, + // `feature_vector` is partially written and therefore must not be used to + // feed the VAD RNN. + bool CheckSilenceComputeFeatures( + ArrayView samples, + ArrayView feature_vector); + + private: + const bool use_high_pass_filter_; + // TODO(bugs.webrtc.org/7494): Remove HPF depending on how AGC2 is used in APM + // and on whether an HPF is already used as pre-processing step in APM. + BiQuadFilter hpf_; + SequenceBuffer + pitch_buf_24kHz_; + ArrayView pitch_buf_24kHz_view_; + std::vector lp_residual_; + ArrayView lp_residual_view_; + PitchEstimator pitch_estimator_; + ArrayView reference_frame_view_; + SpectralFeaturesExtractor spectral_features_extractor_; + int pitch_period_48kHz_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_FEATURES_EXTRACTION_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.cc new file mode 100644 index 00000000..f942099b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/rnn_vad/lp_residual.h" + +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_compare.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +// Computes auto-correlation coefficients for `x` and writes them in +// `auto_corr`. The lag values are in {0, ..., max_lag - 1}, where max_lag +// equals the size of `auto_corr`. +void ComputeAutoCorrelation(ArrayView x, + ArrayView auto_corr) { + constexpr int max_lag = auto_corr.size(); + RTC_DCHECK_LT(max_lag, x.size()); + for (int lag = 0; lag < max_lag; ++lag) { + auto_corr[lag] = + std::inner_product(x.begin(), x.end() - lag, x.begin() + lag, 0.f); + } +} + +// Applies denoising to the auto-correlation coefficients. +void DenoiseAutoCorrelation(ArrayView auto_corr) { + // Assume -40 dB white noise floor. + auto_corr[0] *= 1.0001f; + // Hard-coded values obtained as + // [np.float32((0.008*0.008*i*i)) for i in range(1,5)]. + auto_corr[1] -= auto_corr[1] * 0.000064f; + auto_corr[2] -= auto_corr[2] * 0.000256f; + auto_corr[3] -= auto_corr[3] * 0.000576f; + auto_corr[4] -= auto_corr[4] * 0.001024f; + static_assert(kNumLpcCoefficients == 5, "Update `auto_corr`."); +} + +// Computes the initial inverse filter coefficients given the auto-correlation +// coefficients of an input frame. +void ComputeInitialInverseFilterCoefficients( + ArrayView auto_corr, + ArrayView lpc_coeffs) { + float error = auto_corr[0]; + for (int i = 0; i < kNumLpcCoefficients - 1; ++i) { + float reflection_coeff = 0.f; + for (int j = 0; j < i; ++j) { + reflection_coeff += lpc_coeffs[j] * auto_corr[i - j]; + } + reflection_coeff += auto_corr[i + 1]; + + // Avoid division by numbers close to zero. + constexpr float kMinErrorMagnitude = 1e-6f; + if (std::fabs(error) < kMinErrorMagnitude) { + error = std::copysign(kMinErrorMagnitude, error); + } + + reflection_coeff /= -error; + // Update LPC coefficients and total error. + lpc_coeffs[i] = reflection_coeff; + for (int j = 0; j < ((i + 1) >> 1); ++j) { + const float tmp1 = lpc_coeffs[j]; + const float tmp2 = lpc_coeffs[i - 1 - j]; + lpc_coeffs[j] = tmp1 + reflection_coeff * tmp2; + lpc_coeffs[i - 1 - j] = tmp2 + reflection_coeff * tmp1; + } + error -= reflection_coeff * reflection_coeff * error; + if (error < 0.001f * auto_corr[0]) { + break; + } + } +} + +} // namespace + +void ComputeAndPostProcessLpcCoefficients( + ArrayView x, + ArrayView lpc_coeffs) { + std::array auto_corr; + ComputeAutoCorrelation(x, auto_corr); + if (auto_corr[0] == 0.f) { // Empty frame. + std::fill(lpc_coeffs.begin(), lpc_coeffs.end(), 0); + return; + } + DenoiseAutoCorrelation(auto_corr); + std::array lpc_coeffs_pre{}; + ComputeInitialInverseFilterCoefficients(auto_corr, lpc_coeffs_pre); + // LPC coefficients post-processing. + // TODO(bugs.webrtc.org/9076): Consider removing these steps. + lpc_coeffs_pre[0] *= 0.9f; + lpc_coeffs_pre[1] *= 0.9f * 0.9f; + lpc_coeffs_pre[2] *= 0.9f * 0.9f * 0.9f; + lpc_coeffs_pre[3] *= 0.9f * 0.9f * 0.9f * 0.9f; + constexpr float kC = 0.8f; + lpc_coeffs[0] = lpc_coeffs_pre[0] + kC; + lpc_coeffs[1] = lpc_coeffs_pre[1] + kC * lpc_coeffs_pre[0]; + lpc_coeffs[2] = lpc_coeffs_pre[2] + kC * lpc_coeffs_pre[1]; + lpc_coeffs[3] = lpc_coeffs_pre[3] + kC * lpc_coeffs_pre[2]; + lpc_coeffs[4] = kC * lpc_coeffs_pre[3]; + static_assert(kNumLpcCoefficients == 5, "Update `lpc_coeffs(_pre)`."); +} + +void ComputeLpResidual(ArrayView lpc_coeffs, + ArrayView x, + ArrayView y) { + RTC_DCHECK_GT(x.size(), kNumLpcCoefficients); + RTC_DCHECK_EQ(x.size(), y.size()); + // The code below implements the following operation: + // y[i] = x[i] + dot_product({x[i], ..., x[i - kNumLpcCoefficients + 1]}, + // lpc_coeffs) + // Edge case: i < kNumLpcCoefficients. + y[0] = x[0]; + for (int i = 1; i < kNumLpcCoefficients; ++i) { + y[i] = + std::inner_product(x.crend() - i, x.crend(), lpc_coeffs.cbegin(), x[i]); + } + // Regular case. + auto last = x.crend(); + for (int i = kNumLpcCoefficients; SafeLt(i, y.size()); ++i, --last) { + y[i] = std::inner_product(last - kNumLpcCoefficients, last, + lpc_coeffs.cbegin(), x[i]); + } +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.h new file mode 100644 index 00000000..f29dfe03 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_LP_RESIDUAL_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_LP_RESIDUAL_H_ + +#include + +#include "api/array_view.h" + +namespace webrtc { +namespace rnn_vad { + +// Linear predictive coding (LPC) inverse filter length. +constexpr int kNumLpcCoefficients = 5; + +// Given a frame `x`, computes a post-processed version of LPC coefficients +// tailored for pitch estimation. +void ComputeAndPostProcessLpcCoefficients( + ArrayView x, + ArrayView lpc_coeffs); + +// Computes the LP residual for the input frame `x` and the LPC coefficients +// `lpc_coeffs`. `y` and `x` can point to the same array for in-place +// computation. +void ComputeLpResidual(ArrayView lpc_coeffs, + ArrayView x, + ArrayView y); + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_LP_RESIDUAL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.cc new file mode 100644 index 00000000..3d4bb3f8 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/rnn_vad/pitch_search.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { + +PitchEstimator::PitchEstimator(const AvailableCpuFeatures& cpu_features) + : cpu_features_(cpu_features), + y_energy_24kHz_(kRefineNumLags24kHz, 0.f), + pitch_buffer_12kHz_(kBufSize12kHz), + auto_correlation_12kHz_(kNumLags12kHz) {} + +PitchEstimator::~PitchEstimator() = default; + +int PitchEstimator::Estimate( + ArrayView pitch_buffer) { + ArrayView pitch_buffer_12kHz_view( + pitch_buffer_12kHz_.data(), kBufSize12kHz); + RTC_DCHECK_EQ(pitch_buffer_12kHz_.size(), pitch_buffer_12kHz_view.size()); + ArrayView auto_correlation_12kHz_view( + auto_correlation_12kHz_.data(), kNumLags12kHz); + RTC_DCHECK_EQ(auto_correlation_12kHz_.size(), + auto_correlation_12kHz_view.size()); + + // TODO(bugs.chromium.org/10480): Use `cpu_features_` to estimate pitch. + // Perform the initial pitch search at 12 kHz. + Decimate2x(pitch_buffer, pitch_buffer_12kHz_view); + auto_corr_calculator_.ComputeOnPitchBuffer(pitch_buffer_12kHz_view, + auto_correlation_12kHz_view); + CandidatePitchPeriods pitch_periods = ComputePitchPeriod12kHz( + pitch_buffer_12kHz_view, auto_correlation_12kHz_view, cpu_features_); + // The refinement is done using the pitch buffer that contains 24 kHz samples. + // Therefore, adapt the inverted lags in `pitch_candidates_inv_lags` from 12 + // to 24 kHz. + pitch_periods.best *= 2; + pitch_periods.second_best *= 2; + + // Refine the initial pitch period estimation from 12 kHz to 48 kHz. + // Pre-compute frame energies at 24 kHz. + ArrayView y_energy_24kHz_view( + y_energy_24kHz_.data(), kRefineNumLags24kHz); + RTC_DCHECK_EQ(y_energy_24kHz_.size(), y_energy_24kHz_view.size()); + ComputeSlidingFrameSquareEnergies24kHz(pitch_buffer, y_energy_24kHz_view, + cpu_features_); + // Estimation at 48 kHz. + const int pitch_lag_48kHz = ComputePitchPeriod48kHz( + pitch_buffer, y_energy_24kHz_view, pitch_periods, cpu_features_); + last_pitch_48kHz_ = ComputeExtendedPitchPeriod48kHz( + pitch_buffer, y_energy_24kHz_view, + /*initial_pitch_period_48kHz=*/kMaxPitch48kHz - pitch_lag_48kHz, + last_pitch_48kHz_, cpu_features_); + return last_pitch_48kHz_.period; +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.h new file mode 100644 index 00000000..28f755e3 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/cpu_features.h" +#include "modules/audio_processing/agc2/rnn_vad/auto_correlation.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h" +#include "rtc_base/gtest_prod_util.h" + +namespace webrtc { +namespace rnn_vad { + +// Pitch estimator. +class PitchEstimator { + public: + explicit PitchEstimator(const AvailableCpuFeatures& cpu_features); + PitchEstimator(const PitchEstimator&) = delete; + PitchEstimator& operator=(const PitchEstimator&) = delete; + ~PitchEstimator(); + // Returns the estimated pitch period at 48 kHz. + int Estimate(ArrayView pitch_buffer); + + private: + FRIEND_TEST_ALL_PREFIXES(RnnVadTest, PitchSearchWithinTolerance); + float GetLastPitchStrengthForTesting() const { + return last_pitch_48kHz_.strength; + } + + const AvailableCpuFeatures cpu_features_; + PitchInfo last_pitch_48kHz_{}; + AutoCorrelationCalculator auto_corr_calculator_; + std::vector y_energy_24kHz_; + std::vector pitch_buffer_12kHz_; + std::vector auto_correlation_12kHz_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc new file mode 100644 index 00000000..ebb03bd8 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h" + +#include + +#include +#include +#include +#include + +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/vector_math.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_compare.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +float ComputeAutoCorrelation(int inverted_lag, + ArrayView pitch_buffer, + const VectorMath& vector_math) { + RTC_DCHECK_LT(inverted_lag, kBufSize24kHz); + RTC_DCHECK_LT(inverted_lag, kRefineNumLags24kHz); + static_assert(kMaxPitch24kHz < kBufSize24kHz, ""); + return vector_math.DotProduct( + pitch_buffer.subview(/*offset=*/kMaxPitch24kHz), + pitch_buffer.subview(inverted_lag, kFrameSize20ms24kHz)); +} + +// Given an auto-correlation coefficient `curr_auto_correlation` and its +// neighboring values `prev_auto_correlation` and `next_auto_correlation` +// computes a pseudo-interpolation offset to be applied to the pitch period +// associated to `curr`. The output is a lag in {-1, 0, +1}. +// TODO(bugs.webrtc.org/9076): Consider removing this method. +// `GetPitchPseudoInterpolationOffset()` it is relevant only if the spectral +// analysis works at a sample rate that is twice as that of the pitch buffer; +// In particular, it is not relevant for the estimated pitch period feature fed +// into the RNN. +int GetPitchPseudoInterpolationOffset(float prev_auto_correlation, + float curr_auto_correlation, + float next_auto_correlation) { + if ((next_auto_correlation - prev_auto_correlation) > + 0.7f * (curr_auto_correlation - prev_auto_correlation)) { + return 1; // `next_auto_correlation` is the largest auto-correlation + // coefficient. + } else if ((prev_auto_correlation - next_auto_correlation) > + 0.7f * (curr_auto_correlation - next_auto_correlation)) { + return -1; // `prev_auto_correlation` is the largest auto-correlation + // coefficient. + } + return 0; +} + +// Refines a pitch period `lag` encoded as lag with pseudo-interpolation. The +// output sample rate is twice as that of `lag`. +int PitchPseudoInterpolationLagPitchBuf( + int lag, + ArrayView pitch_buffer, + const VectorMath& vector_math) { + int offset = 0; + // Cannot apply pseudo-interpolation at the boundaries. + if (lag > 0 && lag < kMaxPitch24kHz) { + const int inverted_lag = kMaxPitch24kHz - lag; + offset = GetPitchPseudoInterpolationOffset( + ComputeAutoCorrelation(inverted_lag + 1, pitch_buffer, vector_math), + ComputeAutoCorrelation(inverted_lag, pitch_buffer, vector_math), + ComputeAutoCorrelation(inverted_lag - 1, pitch_buffer, vector_math)); + } + return 2 * lag + offset; +} + +// Integer multipliers used in ComputeExtendedPitchPeriod48kHz() when +// looking for sub-harmonics. +// The values have been chosen to serve the following algorithm. Given the +// initial pitch period T, we examine whether one of its harmonics is the true +// fundamental frequency. We consider T/k with k in {2, ..., 15}. For each of +// these harmonics, in addition to the pitch strength of itself, we choose one +// multiple of its pitch period, n*T/k, to validate it (by averaging their pitch +// strengths). The multiplier n is chosen so that n*T/k is used only one time +// over all k. When for example k = 4, we should also expect a peak at 3*T/4. +// When k = 8 instead we don't want to look at 2*T/8, since we have already +// checked T/4 before. Instead, we look at T*3/8. +// The array can be generate in Python as follows: +// from fractions import Fraction +// # Smallest positive integer not in X. +// def mex(X): +// for i in range(1, int(max(X)+2)): +// if i not in X: +// return i +// # Visited multiples of the period. +// S = {1} +// for n in range(2, 16): +// sn = mex({n * i for i in S} | {1}) +// S = S | {Fraction(1, n), Fraction(sn, n)} +// print(sn, end=', ') +constexpr std::array kSubHarmonicMultipliers = { + {3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2}}; + +struct Range { + int min; + int max; +}; + +// Number of analyzed pitches to the left(right) of a pitch candidate. +constexpr int kPitchNeighborhoodRadius = 2; + +// Creates a pitch period interval centered in `inverted_lag` with hard-coded +// radius. Clipping is applied so that the interval is always valid for a 24 kHz +// pitch buffer. +Range CreateInvertedLagRange(int inverted_lag) { + return {std::max(inverted_lag - kPitchNeighborhoodRadius, 0), + std::min(inverted_lag + kPitchNeighborhoodRadius, + kInitialNumLags24kHz - 1)}; +} + +constexpr int kNumPitchCandidates = 2; // Best and second best. +// Maximum number of analyzed pitch periods. +constexpr int kMaxPitchPeriods24kHz = + kNumPitchCandidates * (2 * kPitchNeighborhoodRadius + 1); + +// Collection of inverted lags. +class InvertedLagsIndex { + public: + InvertedLagsIndex() : num_entries_(0) {} + // Adds an inverted lag to the index. Cannot add more than + // `kMaxPitchPeriods24kHz` values. + void Append(int inverted_lag) { + RTC_DCHECK_LT(num_entries_, kMaxPitchPeriods24kHz); + inverted_lags_[num_entries_++] = inverted_lag; + } + const int* data() const { return inverted_lags_.data(); } + int size() const { return num_entries_; } + + private: + std::array inverted_lags_; + int num_entries_; +}; + +// Computes the auto correlation coefficients for the inverted lags in the +// closed interval `inverted_lags`. Updates `inverted_lags_index` by appending +// the inverted lags for the computed auto correlation values. +void ComputeAutoCorrelation( + Range inverted_lags, + ArrayView pitch_buffer, + ArrayView auto_correlation, + InvertedLagsIndex& inverted_lags_index, + const VectorMath& vector_math) { + // Check valid range. + RTC_DCHECK_LE(inverted_lags.min, inverted_lags.max); + // Trick to avoid zero initialization of `auto_correlation`. + // Needed by the pseudo-interpolation. + if (inverted_lags.min > 0) { + auto_correlation[inverted_lags.min - 1] = 0.f; + } + if (inverted_lags.max < kInitialNumLags24kHz - 1) { + auto_correlation[inverted_lags.max + 1] = 0.f; + } + // Check valid `inverted_lag` indexes. + RTC_DCHECK_GE(inverted_lags.min, 0); + RTC_DCHECK_LT(inverted_lags.max, kInitialNumLags24kHz); + for (int inverted_lag = inverted_lags.min; inverted_lag <= inverted_lags.max; + ++inverted_lag) { + auto_correlation[inverted_lag] = + ComputeAutoCorrelation(inverted_lag, pitch_buffer, vector_math); + inverted_lags_index.Append(inverted_lag); + } +} + +// Searches the strongest pitch period at 24 kHz and returns its inverted lag at +// 48 kHz. +int ComputePitchPeriod48kHz( + ArrayView /* pitch_buffer */, + ArrayView inverted_lags, + ArrayView auto_correlation, + ArrayView y_energy, + const VectorMath& /* vector_math */) { + static_assert(kMaxPitch24kHz > kInitialNumLags24kHz, ""); + static_assert(kMaxPitch24kHz < kBufSize24kHz, ""); + int best_inverted_lag = 0; // Pitch period. + float best_numerator = -1.f; // Pitch strength numerator. + float best_denominator = 0.f; // Pitch strength denominator. + for (int inverted_lag : inverted_lags) { + // A pitch candidate must have positive correlation. + if (auto_correlation[inverted_lag] > 0.f) { + // Auto-correlation energy normalized by frame energy. + const float numerator = + auto_correlation[inverted_lag] * auto_correlation[inverted_lag]; + const float denominator = y_energy[inverted_lag]; + // Compare numerator/denominator ratios without using divisions. + if (numerator * best_denominator > best_numerator * denominator) { + best_inverted_lag = inverted_lag; + best_numerator = numerator; + best_denominator = denominator; + } + } + } + // Pseudo-interpolation to transform `best_inverted_lag` (24 kHz pitch) to a + // 48 kHz pitch period. + if (best_inverted_lag == 0 || best_inverted_lag >= kInitialNumLags24kHz - 1) { + // Cannot apply pseudo-interpolation at the boundaries. + return best_inverted_lag * 2; + } + int offset = GetPitchPseudoInterpolationOffset( + auto_correlation[best_inverted_lag + 1], + auto_correlation[best_inverted_lag], + auto_correlation[best_inverted_lag - 1]); + // TODO(bugs.webrtc.org/9076): When retraining, check if `offset` below should + // be subtracted since `inverted_lag` is an inverted lag but offset is a lag. + return 2 * best_inverted_lag + offset; +} + +// Returns an alternative pitch period for `pitch_period` given a `multiplier` +// and a `divisor` of the period. +constexpr int GetAlternativePitchPeriod(int pitch_period, + int multiplier, + int divisor) { + RTC_DCHECK_GT(divisor, 0); + // Same as `round(multiplier * pitch_period / divisor)`. + return (2 * multiplier * pitch_period + divisor) / (2 * divisor); +} + +// Returns true if the alternative pitch period is stronger than the initial one +// given the last estimated pitch and the value of `period_divisor` used to +// compute the alternative pitch period via `GetAlternativePitchPeriod()`. +bool IsAlternativePitchStrongerThanInitial(PitchInfo last, + PitchInfo initial, + PitchInfo alternative, + int period_divisor) { + // Initial pitch period candidate thresholds for a sample rate of 24 kHz. + // Computed as [5*k*k for k in range(16)]. + constexpr std::array kInitialPitchPeriodThresholds = { + {20, 45, 80, 125, 180, 245, 320, 405, 500, 605, 720, 845, 980, 1125}}; + static_assert( + kInitialPitchPeriodThresholds.size() == kSubHarmonicMultipliers.size(), + ""); + RTC_DCHECK_GE(last.period, 0); + RTC_DCHECK_GE(initial.period, 0); + RTC_DCHECK_GE(alternative.period, 0); + RTC_DCHECK_GE(period_divisor, 2); + // Compute a term that lowers the threshold when `alternative.period` is close + // to the last estimated period `last.period` - i.e., pitch tracking. + float lower_threshold_term = 0.f; + if (std::abs(alternative.period - last.period) <= 1) { + // The candidate pitch period is within 1 sample from the last one. + // Make the candidate at `alternative.period` very easy to be accepted. + lower_threshold_term = last.strength; + } else if (std::abs(alternative.period - last.period) == 2 && + initial.period > + kInitialPitchPeriodThresholds[period_divisor - 2]) { + // The candidate pitch period is 2 samples far from the last one and the + // period `initial.period` (from which `alternative.period` has been + // derived) is greater than a threshold. Make `alternative.period` easy to + // be accepted. + lower_threshold_term = 0.5f * last.strength; + } + // Set the threshold based on the strength of the initial estimate + // `initial.period`. Also reduce the chance of false positives caused by a + // bias towards high frequencies (originating from short-term correlations). + float threshold = + std::max(0.3f, 0.7f * initial.strength - lower_threshold_term); + if (alternative.period < 3 * kMinPitch24kHz) { + // High frequency. + threshold = std::max(0.4f, 0.85f * initial.strength - lower_threshold_term); + } else if (alternative.period < 2 * kMinPitch24kHz) { + // Even higher frequency. + threshold = std::max(0.5f, 0.9f * initial.strength - lower_threshold_term); + } + return alternative.strength > threshold; +} + +} // namespace + +void Decimate2x(ArrayView src, + ArrayView dst) { + // TODO(bugs.webrtc.org/9076): Consider adding anti-aliasing filter. + static_assert(2 * kBufSize12kHz == kBufSize24kHz, ""); + for (int i = 0; i < kBufSize12kHz; ++i) { + dst[i] = src[2 * i]; + } +} + +void ComputeSlidingFrameSquareEnergies24kHz( + ArrayView pitch_buffer, + ArrayView y_energy, + AvailableCpuFeatures cpu_features) { + VectorMath vector_math(cpu_features); + static_assert(kFrameSize20ms24kHz < kBufSize24kHz, ""); + const auto frame_20ms_view = pitch_buffer.subview(0, kFrameSize20ms24kHz); + float yy = vector_math.DotProduct(frame_20ms_view, frame_20ms_view); + y_energy[0] = yy; + static_assert(kMaxPitch24kHz - 1 + kFrameSize20ms24kHz < kBufSize24kHz, ""); + static_assert(kMaxPitch24kHz < kRefineNumLags24kHz, ""); + for (int inverted_lag = 0; inverted_lag < kMaxPitch24kHz; ++inverted_lag) { + yy -= pitch_buffer[inverted_lag] * pitch_buffer[inverted_lag]; + yy += pitch_buffer[inverted_lag + kFrameSize20ms24kHz] * + pitch_buffer[inverted_lag + kFrameSize20ms24kHz]; + yy = std::max(1.f, yy); + y_energy[inverted_lag + 1] = yy; + } +} + +CandidatePitchPeriods ComputePitchPeriod12kHz( + ArrayView pitch_buffer, + ArrayView auto_correlation, + AvailableCpuFeatures cpu_features) { + static_assert(kMaxPitch12kHz > kNumLags12kHz, ""); + static_assert(kMaxPitch12kHz < kBufSize12kHz, ""); + + // Stores a pitch candidate period and strength information. + struct PitchCandidate { + // Pitch period encoded as inverted lag. + int period_inverted_lag = 0; + // Pitch strength encoded as a ratio. + float strength_numerator = -1.f; + float strength_denominator = 0.f; + // Compare the strength of two pitch candidates. + bool HasStrongerPitchThan(const PitchCandidate& b) const { + // Comparing the numerator/denominator ratios without using divisions. + return strength_numerator * b.strength_denominator > + b.strength_numerator * strength_denominator; + } + }; + + VectorMath vector_math(cpu_features); + static_assert(kFrameSize20ms12kHz + 1 < kBufSize12kHz, ""); + const auto frame_view = pitch_buffer.subview(0, kFrameSize20ms12kHz + 1); + float denominator = 1.f + vector_math.DotProduct(frame_view, frame_view); + // Search best and second best pitches by looking at the scaled + // auto-correlation. + PitchCandidate best; + PitchCandidate second_best; + second_best.period_inverted_lag = 1; + for (int inverted_lag = 0; inverted_lag < kNumLags12kHz; ++inverted_lag) { + // A pitch candidate must have positive correlation. + if (auto_correlation[inverted_lag] > 0.f) { + PitchCandidate candidate{ + inverted_lag, + auto_correlation[inverted_lag] * auto_correlation[inverted_lag], + denominator}; + if (candidate.HasStrongerPitchThan(second_best)) { + if (candidate.HasStrongerPitchThan(best)) { + second_best = best; + best = candidate; + } else { + second_best = candidate; + } + } + } + // Update `squared_energy_y` for the next inverted lag. + const float y_old = pitch_buffer[inverted_lag]; + const float y_new = pitch_buffer[inverted_lag + kFrameSize20ms12kHz]; + denominator -= y_old * y_old; + denominator += y_new * y_new; + denominator = std::max(0.f, denominator); + } + return {best.period_inverted_lag, second_best.period_inverted_lag}; +} + +int ComputePitchPeriod48kHz( + ArrayView pitch_buffer, + ArrayView y_energy, + CandidatePitchPeriods pitch_candidates, + AvailableCpuFeatures cpu_features) { + // Compute the auto-correlation terms only for neighbors of the two pitch + // candidates (best and second best). + std::array auto_correlation; + InvertedLagsIndex inverted_lags_index; + // Create two inverted lag ranges so that `r1` precedes `r2`. + const bool swap_candidates = + pitch_candidates.best > pitch_candidates.second_best; + const Range r1 = CreateInvertedLagRange( + swap_candidates ? pitch_candidates.second_best : pitch_candidates.best); + const Range r2 = CreateInvertedLagRange( + swap_candidates ? pitch_candidates.best : pitch_candidates.second_best); + // Check valid ranges. + RTC_DCHECK_LE(r1.min, r1.max); + RTC_DCHECK_LE(r2.min, r2.max); + // Check `r1` precedes `r2`. + RTC_DCHECK_LE(r1.min, r2.min); + RTC_DCHECK_LE(r1.max, r2.max); + VectorMath vector_math(cpu_features); + if (r1.max + 1 >= r2.min) { + // Overlapping or adjacent ranges. + ComputeAutoCorrelation({r1.min, r2.max}, pitch_buffer, auto_correlation, + inverted_lags_index, vector_math); + } else { + // Disjoint ranges. + ComputeAutoCorrelation(r1, pitch_buffer, auto_correlation, + inverted_lags_index, vector_math); + ComputeAutoCorrelation(r2, pitch_buffer, auto_correlation, + inverted_lags_index, vector_math); + } + return ComputePitchPeriod48kHz(pitch_buffer, inverted_lags_index, + auto_correlation, y_energy, vector_math); +} + +PitchInfo ComputeExtendedPitchPeriod48kHz( + ArrayView pitch_buffer, + ArrayView y_energy, + int initial_pitch_period_48kHz, + PitchInfo last_pitch_48kHz, + AvailableCpuFeatures cpu_features) { + RTC_DCHECK_LE(kMinPitch48kHz, initial_pitch_period_48kHz); + RTC_DCHECK_LE(initial_pitch_period_48kHz, kMaxPitch48kHz); + + // Stores information for a refined pitch candidate. + struct RefinedPitchCandidate { + int period; + float strength; + // Additional strength data used for the final pitch estimation. + float xy; // Auto-correlation. + float y_energy; // Energy of the sliding frame `y`. + }; + + const float x_energy = y_energy[kMaxPitch24kHz]; + const auto pitch_strength = [x_energy](float xy, float y_energy) { + RTC_DCHECK_GE(x_energy * y_energy, 0.f); + return xy / std::sqrt(1.f + x_energy * y_energy); + }; + VectorMath vector_math(cpu_features); + + // Initialize the best pitch candidate with `initial_pitch_period_48kHz`. + RefinedPitchCandidate best_pitch; + best_pitch.period = + std::min(initial_pitch_period_48kHz / 2, kMaxPitch24kHz - 1); + best_pitch.xy = ComputeAutoCorrelation(kMaxPitch24kHz - best_pitch.period, + pitch_buffer, vector_math); + best_pitch.y_energy = y_energy[kMaxPitch24kHz - best_pitch.period]; + best_pitch.strength = pitch_strength(best_pitch.xy, best_pitch.y_energy); + // Keep a copy of the initial pitch candidate. + const PitchInfo initial_pitch{best_pitch.period, best_pitch.strength}; + // 24 kHz version of the last estimated pitch. + const PitchInfo last_pitch{last_pitch_48kHz.period / 2, + last_pitch_48kHz.strength}; + + // Find `max_period_divisor` such that the result of + // `GetAlternativePitchPeriod(initial_pitch_period, 1, max_period_divisor)` + // equals `kMinPitch24kHz`. + const int max_period_divisor = + (2 * initial_pitch.period) / (2 * kMinPitch24kHz - 1); + for (int period_divisor = 2; period_divisor <= max_period_divisor; + ++period_divisor) { + PitchInfo alternative_pitch; + alternative_pitch.period = GetAlternativePitchPeriod( + initial_pitch.period, /*multiplier=*/1, period_divisor); + RTC_DCHECK_GE(alternative_pitch.period, kMinPitch24kHz); + // When looking at `alternative_pitch.period`, we also look at one of its + // sub-harmonics. `kSubHarmonicMultipliers` is used to know where to look. + // `period_divisor` == 2 is a special case since `dual_alternative_period` + // might be greater than the maximum pitch period. + int dual_alternative_period = GetAlternativePitchPeriod( + initial_pitch.period, kSubHarmonicMultipliers[period_divisor - 2], + period_divisor); + RTC_DCHECK_GT(dual_alternative_period, 0); + if (period_divisor == 2 && dual_alternative_period > kMaxPitch24kHz) { + dual_alternative_period = initial_pitch.period; + } + RTC_DCHECK_NE(alternative_pitch.period, dual_alternative_period) + << "The lower pitch period and the additional sub-harmonic must not " + "coincide."; + // Compute an auto-correlation score for the primary pitch candidate + // `alternative_pitch.period` by also looking at its possible sub-harmonic + // `dual_alternative_period`. + const float xy_primary_period = ComputeAutoCorrelation( + kMaxPitch24kHz - alternative_pitch.period, pitch_buffer, vector_math); + // TODO(webrtc:10480): Copy `xy_primary_period` if the secondary period is + // equal to the primary one. + const float xy_secondary_period = ComputeAutoCorrelation( + kMaxPitch24kHz - dual_alternative_period, pitch_buffer, vector_math); + const float xy = 0.5f * (xy_primary_period + xy_secondary_period); + const float yy = + 0.5f * (y_energy[kMaxPitch24kHz - alternative_pitch.period] + + y_energy[kMaxPitch24kHz - dual_alternative_period]); + alternative_pitch.strength = pitch_strength(xy, yy); + + // Maybe update best period. + if (IsAlternativePitchStrongerThanInitial( + last_pitch, initial_pitch, alternative_pitch, period_divisor)) { + best_pitch = {alternative_pitch.period, alternative_pitch.strength, xy, + yy}; + } + } + + // Final pitch strength and period. + best_pitch.xy = std::max(0.f, best_pitch.xy); + RTC_DCHECK_LE(0.f, best_pitch.y_energy); + float final_pitch_strength = + (best_pitch.y_energy <= best_pitch.xy) + ? 1.f + : best_pitch.xy / (best_pitch.y_energy + 1.f); + final_pitch_strength = std::min(best_pitch.strength, final_pitch_strength); + int final_pitch_period_48kHz = std::max( + kMinPitch48kHz, PitchPseudoInterpolationLagPitchBuf( + best_pitch.period, pitch_buffer, vector_math)); + + return {final_pitch_period_48kHz, final_pitch_strength}; +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h new file mode 100644 index 00000000..2366c5aa --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_INTERNAL_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/cpu_features.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" + +namespace webrtc { +namespace rnn_vad { + +// Performs 2x decimation without any anti-aliasing filter. +void Decimate2x(ArrayView src, + ArrayView dst); + +// Key concepts and keywords used below in this file. +// +// The pitch estimation relies on a pitch buffer, which is an array-like data +// structured designed as follows: +// +// |....A....|.....B.....| +// +// The part on the left, named `A` contains the oldest samples, whereas `B` +// contains the most recent ones. The size of `A` corresponds to the maximum +// pitch period, that of `B` to the analysis frame size (e.g., 16 ms and 20 ms +// respectively). +// +// Pitch estimation is essentially based on the analysis of two 20 ms frames +// extracted from the pitch buffer. One frame, called `x`, is kept fixed and +// corresponds to `B` - i.e., the most recent 20 ms. The other frame, called +// `y`, is extracted from different parts of the buffer instead. +// +// The offset between `x` and `y` corresponds to a specific pitch period. +// For instance, if `y` is positioned at the beginning of the pitch buffer, then +// the cross-correlation between `x` and `y` can be used as an indication of the +// strength for the maximum pitch. +// +// Such an offset can be encoded in two ways: +// - As a lag, which is the index in the pitch buffer for the first item in `y` +// - As an inverted lag, which is the number of samples from the beginning of +// `x` and the end of `y` +// +// |---->| lag +// |....A....|.....B.....| +// |<--| inverted lag +// |.....y.....| `y` 20 ms frame +// +// The inverted lag has the advantage of being directly proportional to the +// corresponding pitch period. + +// Computes the sum of squared samples for every sliding frame `y` in the pitch +// buffer. The indexes of `y_energy` are inverted lags. +void ComputeSlidingFrameSquareEnergies24kHz( + ArrayView pitch_buffer, + ArrayView y_energy, + AvailableCpuFeatures cpu_features); + +// Top-2 pitch period candidates. Unit: number of samples - i.e., inverted lags. +struct CandidatePitchPeriods { + int best; + int second_best; +}; + +// Computes the candidate pitch periods at 12 kHz given a view on the 12 kHz +// pitch buffer and the auto-correlation values (having inverted lags as +// indexes). +CandidatePitchPeriods ComputePitchPeriod12kHz( + ArrayView pitch_buffer, + ArrayView auto_correlation, + AvailableCpuFeatures cpu_features); + +// Computes the pitch period at 48 kHz given a view on the 24 kHz pitch buffer, +// the energies for the sliding frames `y` at 24 kHz and the pitch period +// candidates at 24 kHz (encoded as inverted lag). +int ComputePitchPeriod48kHz( + ArrayView pitch_buffer, + ArrayView y_energy, + CandidatePitchPeriods pitch_candidates_24kHz, + AvailableCpuFeatures cpu_features); + +struct PitchInfo { + int period; + float strength; +}; + +// Computes the pitch period at 48 kHz searching in an extended pitch range +// given a view on the 24 kHz pitch buffer, the energies for the sliding frames +// `y` at 24 kHz, the initial 48 kHz estimation (computed by +// `ComputePitchPeriod48kHz()`) and the last estimated pitch. +PitchInfo ComputeExtendedPitchPeriod48kHz( + ArrayView pitch_buffer, + ArrayView y_energy, + int initial_pitch_period_48kHz, + PitchInfo last_pitch_48kHz, + AvailableCpuFeatures cpu_features); + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_INTERNAL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/ring_buffer.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/ring_buffer.h new file mode 100644 index 00000000..a37df4e1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/ring_buffer.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RING_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RING_BUFFER_H_ + +#include +#include +#include + +#include "api/array_view.h" + +namespace webrtc { +namespace rnn_vad { + +// Ring buffer for N arrays of type T each one with size S. +template +class RingBuffer { + static_assert(S > 0, ""); + static_assert(N > 0, ""); + static_assert(std::is_arithmetic::value, + "Integral or floating point required."); + + public: + RingBuffer() : tail_(0) {} + RingBuffer(const RingBuffer&) = delete; + RingBuffer& operator=(const RingBuffer&) = delete; + ~RingBuffer() = default; + // Set the ring buffer values to zero. + void Reset() { buffer_.fill(0); } + // Replace the least recently pushed array in the buffer with `new_values`. + void Push(ArrayView new_values) { + std::memcpy(buffer_.data() + S * tail_, new_values.data(), S * sizeof(T)); + tail_ += 1; + if (tail_ == N) + tail_ = 0; + } + // Return an array view onto the array with a given delay. A view on the last + // and least recently push array is returned when `delay` is 0 and N - 1 + // respectively. + ArrayView GetArrayView(int delay) const { + RTC_DCHECK_LE(0, delay); + RTC_DCHECK_LT(delay, N); + int offset = tail_ - 1 - delay; + if (offset < 0) + offset += N; + return {buffer_.data() + S * offset, S}; + } + + private: + int tail_; // Index of the least recently pushed sub-array. + std::array buffer_{}; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RING_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.cc new file mode 100644 index 00000000..394aea9a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.cc @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/rnn_vad/rnn.h" + +#include "rtc_base/checks.h" +#include "third_party/rnnoise/src/rnn_vad_weights.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +using ::rnnoise::kInputLayerInputSize; +static_assert(kFeatureVectorSize == kInputLayerInputSize, ""); +using ::rnnoise::kInputDenseBias; +using ::rnnoise::kInputDenseWeights; +using ::rnnoise::kInputLayerOutputSize; +static_assert(kInputLayerOutputSize <= kFullyConnectedLayerMaxUnits, ""); + +using ::rnnoise::kHiddenGruBias; +using ::rnnoise::kHiddenGruRecurrentWeights; +using ::rnnoise::kHiddenGruWeights; +using ::rnnoise::kHiddenLayerOutputSize; +static_assert(kHiddenLayerOutputSize <= kGruLayerMaxUnits, ""); + +using ::rnnoise::kOutputDenseBias; +using ::rnnoise::kOutputDenseWeights; +using ::rnnoise::kOutputLayerOutputSize; +static_assert(kOutputLayerOutputSize <= kFullyConnectedLayerMaxUnits, ""); + +} // namespace + +RnnVad::RnnVad(const AvailableCpuFeatures& cpu_features) + : input_(kInputLayerInputSize, + kInputLayerOutputSize, + kInputDenseBias, + kInputDenseWeights, + ActivationFunction::kTansigApproximated, + cpu_features, + /*layer_name=*/"FC1"), + hidden_(kInputLayerOutputSize, + kHiddenLayerOutputSize, + kHiddenGruBias, + kHiddenGruWeights, + kHiddenGruRecurrentWeights, + cpu_features, + /*layer_name=*/"GRU1"), + output_(kHiddenLayerOutputSize, + kOutputLayerOutputSize, + kOutputDenseBias, + kOutputDenseWeights, + ActivationFunction::kSigmoidApproximated, + // The output layer is just 24x1. The unoptimized code is faster. + NoAvailableCpuFeatures(), + /*layer_name=*/"FC2") { + // Input-output chaining size checks. + RTC_DCHECK_EQ(input_.size(), hidden_.input_size()) + << "The input and the hidden layers sizes do not match."; + RTC_DCHECK_EQ(hidden_.size(), output_.input_size()) + << "The hidden and the output layers sizes do not match."; +} + +RnnVad::~RnnVad() = default; + +void RnnVad::Reset() { + hidden_.Reset(); +} + +float RnnVad::ComputeVadProbability( + ArrayView feature_vector, + bool is_silence) { + if (is_silence) { + Reset(); + return 0.f; + } + input_.ComputeOutput(feature_vector); + hidden_.ComputeOutput(input_); + output_.ComputeOutput(hidden_); + RTC_DCHECK_EQ(output_.size(), 1); + return output_.data()[0]; +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.h new file mode 100644 index 00000000..2f9063a1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_H_ + +#include +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/cpu_features.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/rnn_fc.h" +#include "modules/audio_processing/agc2/rnn_vad/rnn_gru.h" + +namespace webrtc { +namespace rnn_vad { + +// Recurrent network with hard-coded architecture and weights for voice activity +// detection. +class RnnVad { + public: + explicit RnnVad(const AvailableCpuFeatures& cpu_features); + RnnVad(const RnnVad&) = delete; + RnnVad& operator=(const RnnVad&) = delete; + ~RnnVad(); + void Reset(); + // Observes `feature_vector` and `is_silence`, updates the RNN and returns the + // current voice probability. + float ComputeVadProbability( + ArrayView feature_vector, + bool is_silence); + + private: + FullyConnectedLayer input_; + GatedRecurrentLayer hidden_; + FullyConnectedLayer output_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_fc.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_fc.cc new file mode 100644 index 00000000..b5a35b2a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_fc.cc @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/rnn_vad/rnn_fc.h" + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "third_party/rnnoise/src/rnn_activations.h" +#include "third_party/rnnoise/src/rnn_vad_weights.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +std::vector GetScaledParams(ArrayView params) { + std::vector scaled_params(params.size()); + std::transform(params.begin(), params.end(), scaled_params.begin(), + [](int8_t x) -> float { + return ::rnnoise::kWeightsScale * static_cast(x); + }); + return scaled_params; +} + +// TODO(bugs.chromium.org/10480): Hard-code optimized layout and remove this +// function to improve setup time. +// Casts and scales `weights` and re-arranges the layout. +std::vector PreprocessWeights(ArrayView weights, + int output_size) { + if (output_size == 1) { + return GetScaledParams(weights); + } + // Transpose, scale and cast. + const int input_size = + CheckedDivExact(dchecked_cast(weights.size()), output_size); + std::vector w(weights.size()); + for (int o = 0; o < output_size; ++o) { + for (int i = 0; i < input_size; ++i) { + w[o * input_size + i] = rnnoise::kWeightsScale * + static_cast(weights[i * output_size + o]); + } + } + return w; +} + +FunctionView GetActivationFunction( + ActivationFunction activation_function) { + switch (activation_function) { + case ActivationFunction::kTansigApproximated: + return ::rnnoise::TansigApproximated; + case ActivationFunction::kSigmoidApproximated: + return ::rnnoise::SigmoidApproximated; + } +} + +} // namespace + +FullyConnectedLayer::FullyConnectedLayer( + const int input_size, + const int output_size, + const ArrayView bias, + const ArrayView weights, + ActivationFunction activation_function, + const AvailableCpuFeatures& cpu_features, + absl::string_view layer_name) + : input_size_(input_size), + output_size_(output_size), + bias_(GetScaledParams(bias)), + weights_(PreprocessWeights(weights, output_size)), + vector_math_(cpu_features), + activation_function_(GetActivationFunction(activation_function)) { + RTC_DCHECK_LE(output_size_, kFullyConnectedLayerMaxUnits) + << "Insufficient FC layer over-allocation (" << layer_name << ")."; + RTC_DCHECK_EQ(output_size_, bias_.size()) + << "Mismatching output size and bias terms array size (" << layer_name + << ")."; + RTC_DCHECK_EQ(input_size_ * output_size_, weights_.size()) + << "Mismatching input-output size and weight coefficients array size (" + << layer_name << ")."; +} + +FullyConnectedLayer::~FullyConnectedLayer() = default; + +void FullyConnectedLayer::ComputeOutput(ArrayView input) { + RTC_DCHECK_EQ(input.size(), input_size_); + ArrayView weights(weights_); + for (int o = 0; o < output_size_; ++o) { + output_[o] = activation_function_( + bias_[o] + vector_math_.DotProduct( + input, weights.subview(o * input_size_, input_size_))); + } +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_fc.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_fc.h new file mode 100644 index 00000000..b802ea78 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_fc.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_FC_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_FC_H_ + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/function_view.h" +#include "modules/audio_processing/agc2/cpu_features.h" +#include "modules/audio_processing/agc2/rnn_vad/vector_math.h" + +namespace webrtc { +namespace rnn_vad { + +// Activation function for a neural network cell. +enum class ActivationFunction { kTansigApproximated, kSigmoidApproximated }; + +// Maximum number of units for an FC layer. +constexpr int kFullyConnectedLayerMaxUnits = 24; + +// Fully-connected layer with a custom activation function which owns the output +// buffer. +class FullyConnectedLayer { + public: + // Ctor. `output_size` cannot be greater than `kFullyConnectedLayerMaxUnits`. + FullyConnectedLayer(int input_size, + int output_size, + ArrayView bias, + ArrayView weights, + ActivationFunction activation_function, + const AvailableCpuFeatures& cpu_features, + absl::string_view layer_name); + FullyConnectedLayer(const FullyConnectedLayer&) = delete; + FullyConnectedLayer& operator=(const FullyConnectedLayer&) = delete; + ~FullyConnectedLayer(); + + // Returns the size of the input vector. + int input_size() const { return input_size_; } + // Returns the pointer to the first element of the output buffer. + const float* data() const { return output_.data(); } + // Returns the size of the output buffer. + int size() const { return output_size_; } + + // Computes the fully-connected layer output. + void ComputeOutput(ArrayView input); + + private: + const int input_size_; + const int output_size_; + const std::vector bias_; + const std::vector weights_; + const VectorMath vector_math_; + FunctionView activation_function_; + // Over-allocated array with size equal to `output_size_`. + std::array output_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_FC_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_gru.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_gru.cc new file mode 100644 index 00000000..e463045a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_gru.cc @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/rnn_vad/rnn_gru.h" + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "third_party/rnnoise/src/rnn_activations.h" +#include "third_party/rnnoise/src/rnn_vad_weights.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +constexpr int kNumGruGates = 3; // Update, reset, output. + +std::vector PreprocessGruTensor(ArrayView tensor_src, + int output_size) { + // Transpose, cast and scale. + // `n` is the size of the first dimension of the 3-dim tensor `weights`. + const int n = CheckedDivExact(dchecked_cast(tensor_src.size()), + output_size * kNumGruGates); + const int stride_src = kNumGruGates * output_size; + const int stride_dst = n * output_size; + std::vector tensor_dst(tensor_src.size()); + for (int g = 0; g < kNumGruGates; ++g) { + for (int o = 0; o < output_size; ++o) { + for (int i = 0; i < n; ++i) { + tensor_dst[g * stride_dst + o * n + i] = + ::rnnoise::kWeightsScale * + static_cast( + tensor_src[i * stride_src + g * output_size + o]); + } + } + } + return tensor_dst; +} + +// Computes the output for the update or the reset gate. +// Operation: `g = sigmoid(W^T∙i + R^T∙s + b)` where +// - `g`: output gate vector +// - `W`: weights matrix +// - `i`: input vector +// - `R`: recurrent weights matrix +// - `s`: state gate vector +// - `b`: bias vector +void ComputeUpdateResetGate(int input_size, + int output_size, + const VectorMath& vector_math, + ArrayView input, + ArrayView state, + ArrayView bias, + ArrayView weights, + ArrayView recurrent_weights, + ArrayView gate) { + RTC_DCHECK_EQ(input.size(), input_size); + RTC_DCHECK_EQ(state.size(), output_size); + RTC_DCHECK_EQ(bias.size(), output_size); + RTC_DCHECK_EQ(weights.size(), input_size * output_size); + RTC_DCHECK_EQ(recurrent_weights.size(), output_size * output_size); + RTC_DCHECK_GE(gate.size(), output_size); // `gate` is over-allocated. + for (int o = 0; o < output_size; ++o) { + float x = bias[o]; + x += vector_math.DotProduct(input, + weights.subview(o * input_size, input_size)); + x += vector_math.DotProduct( + state, recurrent_weights.subview(o * output_size, output_size)); + gate[o] = ::rnnoise::SigmoidApproximated(x); + } +} + +// Computes the output for the state gate. +// Operation: `s' = u .* s + (1 - u) .* ReLU(W^T∙i + R^T∙(s .* r) + b)` where +// - `s'`: output state gate vector +// - `s`: previous state gate vector +// - `u`: update gate vector +// - `W`: weights matrix +// - `i`: input vector +// - `R`: recurrent weights matrix +// - `r`: reset gate vector +// - `b`: bias vector +// - `.*` element-wise product +void ComputeStateGate(int input_size, + int output_size, + const VectorMath& vector_math, + ArrayView input, + ArrayView update, + ArrayView reset, + ArrayView bias, + ArrayView weights, + ArrayView recurrent_weights, + ArrayView state) { + RTC_DCHECK_EQ(input.size(), input_size); + RTC_DCHECK_GE(update.size(), output_size); // `update` is over-allocated. + RTC_DCHECK_GE(reset.size(), output_size); // `reset` is over-allocated. + RTC_DCHECK_EQ(bias.size(), output_size); + RTC_DCHECK_EQ(weights.size(), input_size * output_size); + RTC_DCHECK_EQ(recurrent_weights.size(), output_size * output_size); + RTC_DCHECK_EQ(state.size(), output_size); + std::array reset_x_state; + for (int o = 0; o < output_size; ++o) { + reset_x_state[o] = state[o] * reset[o]; + } + for (int o = 0; o < output_size; ++o) { + float x = bias[o]; + x += vector_math.DotProduct(input, + weights.subview(o * input_size, input_size)); + x += vector_math.DotProduct( + {reset_x_state.data(), static_cast(output_size)}, + recurrent_weights.subview(o * output_size, output_size)); + state[o] = update[o] * state[o] + (1.f - update[o]) * std::max(0.f, x); + } +} + +} // namespace + +GatedRecurrentLayer::GatedRecurrentLayer( + const int input_size, + const int output_size, + const ArrayView bias, + const ArrayView weights, + const ArrayView recurrent_weights, + const AvailableCpuFeatures& cpu_features, + absl::string_view layer_name) + : input_size_(input_size), + output_size_(output_size), + bias_(PreprocessGruTensor(bias, output_size)), + weights_(PreprocessGruTensor(weights, output_size)), + recurrent_weights_(PreprocessGruTensor(recurrent_weights, output_size)), + vector_math_(cpu_features) { + RTC_DCHECK_LE(output_size_, kGruLayerMaxUnits) + << "Insufficient GRU layer over-allocation (" << layer_name << ")."; + RTC_DCHECK_EQ(kNumGruGates * output_size_, bias_.size()) + << "Mismatching output size and bias terms array size (" << layer_name + << ")."; + RTC_DCHECK_EQ(kNumGruGates * input_size_ * output_size_, weights_.size()) + << "Mismatching input-output size and weight coefficients array size (" + << layer_name << ")."; + RTC_DCHECK_EQ(kNumGruGates * output_size_ * output_size_, + recurrent_weights_.size()) + << "Mismatching input-output size and recurrent weight coefficients array" + " size (" + << layer_name << ")."; + Reset(); +} + +GatedRecurrentLayer::~GatedRecurrentLayer() = default; + +void GatedRecurrentLayer::Reset() { + state_.fill(0.f); +} + +void GatedRecurrentLayer::ComputeOutput(ArrayView input) { + RTC_DCHECK_EQ(input.size(), input_size_); + + // The tensors below are organized as a sequence of flattened tensors for the + // `update`, `reset` and `state` gates. + ArrayView bias(bias_); + ArrayView weights(weights_); + ArrayView recurrent_weights(recurrent_weights_); + // Strides to access to the flattened tensors for a specific gate. + const int stride_weights = input_size_ * output_size_; + const int stride_recurrent_weights = output_size_ * output_size_; + + ArrayView state(state_.data(), output_size_); + + // Update gate. + std::array update; + ComputeUpdateResetGate( + input_size_, output_size_, vector_math_, input, state, + bias.subview(0, output_size_), weights.subview(0, stride_weights), + recurrent_weights.subview(0, stride_recurrent_weights), update); + // Reset gate. + std::array reset; + ComputeUpdateResetGate(input_size_, output_size_, vector_math_, input, state, + bias.subview(output_size_, output_size_), + weights.subview(stride_weights, stride_weights), + recurrent_weights.subview(stride_recurrent_weights, + stride_recurrent_weights), + reset); + // State gate. + ComputeStateGate(input_size_, output_size_, vector_math_, input, update, + reset, bias.subview(2 * output_size_, output_size_), + weights.subview(2 * stride_weights, stride_weights), + recurrent_weights.subview(2 * stride_recurrent_weights, + stride_recurrent_weights), + state); +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_gru.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_gru.h new file mode 100644 index 00000000..ae3063b3 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_gru.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_GRU_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_GRU_H_ + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "modules/audio_processing/agc2/cpu_features.h" +#include "modules/audio_processing/agc2/rnn_vad/vector_math.h" + +namespace webrtc { +namespace rnn_vad { + +// Maximum number of units for a GRU layer. +constexpr int kGruLayerMaxUnits = 24; + +// Recurrent layer with gated recurrent units (GRUs) with sigmoid and ReLU as +// activation functions for the update/reset and output gates respectively. +class GatedRecurrentLayer { + public: + // Ctor. `output_size` cannot be greater than `kGruLayerMaxUnits`. + GatedRecurrentLayer(int input_size, + int output_size, + ArrayView bias, + ArrayView weights, + ArrayView recurrent_weights, + const AvailableCpuFeatures& cpu_features, + absl::string_view layer_name); + GatedRecurrentLayer(const GatedRecurrentLayer&) = delete; + GatedRecurrentLayer& operator=(const GatedRecurrentLayer&) = delete; + ~GatedRecurrentLayer(); + + // Returns the size of the input vector. + int input_size() const { return input_size_; } + // Returns the pointer to the first element of the output buffer. + const float* data() const { return state_.data(); } + // Returns the size of the output buffer. + int size() const { return output_size_; } + + // Resets the GRU state. + void Reset(); + // Computes the recurrent layer output and updates the status. + void ComputeOutput(ArrayView input); + + private: + const int input_size_; + const int output_size_; + const std::vector bias_; + const std::vector weights_; + const std::vector recurrent_weights_; + const VectorMath vector_math_; + // Over-allocated array with size equal to `output_size_`. + std::array state_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_GRU_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad.go b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad.go new file mode 100644 index 00000000..6a1b5f4a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad.go @@ -0,0 +1,10 @@ +//go:build console + +package rnn_vad + +// #cgo CXXFLAGS: -I${SRCDIR}/../../../.. -I${SRCDIR}/../../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h new file mode 100644 index 00000000..c88683a2 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SEQUENCE_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SEQUENCE_BUFFER_H_ + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { + +// Linear buffer implementation to (i) push fixed size chunks of sequential data +// and (ii) view contiguous parts of the buffer. The buffer and the pushed +// chunks have size S and N respectively. For instance, when S = 2N the first +// half of the sequence buffer is replaced with its second half, and the new N +// values are written at the end of the buffer. +// The class also provides a view on the most recent M values, where 0 < M <= S +// and by default M = N. +template +class SequenceBuffer { + static_assert(N <= S, + "The new chunk size cannot be larger than the sequence buffer " + "size."); + static_assert(std::is_arithmetic::value, + "Integral or floating point required."); + + public: + SequenceBuffer() : buffer_(S) { + RTC_DCHECK_EQ(S, buffer_.size()); + Reset(); + } + SequenceBuffer(const SequenceBuffer&) = delete; + SequenceBuffer& operator=(const SequenceBuffer&) = delete; + ~SequenceBuffer() = default; + int size() const { return S; } + int chunks_size() const { return N; } + // Sets the sequence buffer values to zero. + void Reset() { std::fill(buffer_.begin(), buffer_.end(), 0); } + // Returns a view on the whole buffer. + ArrayView GetBufferView() const { return {buffer_.data(), S}; } + // Returns a view on the M most recent values of the buffer. + ArrayView GetMostRecentValuesView() const { + static_assert(M <= S, + "The number of most recent values cannot be larger than the " + "sequence buffer size."); + return {buffer_.data() + S - M, M}; + } + // Shifts left the buffer by N items and add new N items at the end. + void Push(ArrayView new_values) { + // Make space for the new values. + if (S > N) + std::memmove(buffer_.data(), buffer_.data() + N, (S - N) * sizeof(T)); + // Copy the new values at the end of the buffer. + std::memcpy(buffer_.data() + S - N, new_values.data(), N * sizeof(T)); + } + + private: + std::vector buffer_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SEQUENCE_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.cc new file mode 100644 index 00000000..3ad4cae4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.cc @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/rnn_vad/spectral_features.h" + +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_compare.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +constexpr float kSilenceThreshold = 0.04f; + +// Computes the new cepstral difference stats and pushes them into the passed +// symmetric matrix buffer. +void UpdateCepstralDifferenceStats( + ArrayView new_cepstral_coeffs, + const RingBuffer& ring_buf, + SymmetricMatrixBuffer* sym_matrix_buf) { + RTC_DCHECK(sym_matrix_buf); + // Compute the new cepstral distance stats. + std::array distances; + for (int i = 0; i < kCepstralCoeffsHistorySize - 1; ++i) { + const int delay = i + 1; + auto old_cepstral_coeffs = ring_buf.GetArrayView(delay); + distances[i] = 0.f; + for (int k = 0; k < kNumBands; ++k) { + const float c = new_cepstral_coeffs[k] - old_cepstral_coeffs[k]; + distances[i] += c * c; + } + } + // Push the new spectral distance stats into the symmetric matrix buffer. + sym_matrix_buf->Push(distances); +} + +// Computes the first half of the Vorbis window. +std::array ComputeScaledHalfVorbisWindow( + float scaling = 1.f) { + constexpr int kHalfSize = kFrameSize20ms24kHz / 2; + std::array half_window{}; + for (int i = 0; i < kHalfSize; ++i) { + half_window[i] = + scaling * + std::sin(0.5 * kPi * std::sin(0.5 * kPi * (i + 0.5) / kHalfSize) * + std::sin(0.5 * kPi * (i + 0.5) / kHalfSize)); + } + return half_window; +} + +// Computes the forward FFT on a 20 ms frame to which a given window function is +// applied. The Fourier coefficient corresponding to the Nyquist frequency is +// set to zero (it is never used and this allows to simplify the code). +void ComputeWindowedForwardFft( + ArrayView frame, + const std::array& half_window, + Pffft::FloatBuffer* fft_input_buffer, + Pffft::FloatBuffer* fft_output_buffer, + Pffft* fft) { + RTC_DCHECK_EQ(frame.size(), 2 * half_window.size()); + // Apply windowing. + auto in = fft_input_buffer->GetView(); + for (int i = 0, j = kFrameSize20ms24kHz - 1; SafeLt(i, half_window.size()); + ++i, --j) { + in[i] = frame[i] * half_window[i]; + in[j] = frame[j] * half_window[i]; + } + fft->ForwardTransform(*fft_input_buffer, fft_output_buffer, /*ordered=*/true); + // Set the Nyquist frequency coefficient to zero. + auto out = fft_output_buffer->GetView(); + out[1] = 0.f; +} + +} // namespace + +SpectralFeaturesExtractor::SpectralFeaturesExtractor() + : half_window_(ComputeScaledHalfVorbisWindow( + 1.f / static_cast(kFrameSize20ms24kHz))), + fft_(kFrameSize20ms24kHz, Pffft::FftType::kReal), + fft_buffer_(fft_.CreateBuffer()), + reference_frame_fft_(fft_.CreateBuffer()), + lagged_frame_fft_(fft_.CreateBuffer()), + dct_table_(ComputeDctTable()) {} + +SpectralFeaturesExtractor::~SpectralFeaturesExtractor() = default; + +void SpectralFeaturesExtractor::Reset() { + cepstral_coeffs_ring_buf_.Reset(); + cepstral_diffs_buf_.Reset(); +} + +bool SpectralFeaturesExtractor::CheckSilenceComputeFeatures( + ArrayView reference_frame, + ArrayView lagged_frame, + ArrayView higher_bands_cepstrum, + ArrayView average, + ArrayView first_derivative, + ArrayView second_derivative, + ArrayView bands_cross_corr, + float* variability) { + // Compute the Opus band energies for the reference frame. + ComputeWindowedForwardFft(reference_frame, half_window_, fft_buffer_.get(), + reference_frame_fft_.get(), &fft_); + spectral_correlator_.ComputeAutoCorrelation( + reference_frame_fft_->GetConstView(), reference_frame_bands_energy_); + // Check if the reference frame has silence. + const float tot_energy = + std::accumulate(reference_frame_bands_energy_.begin(), + reference_frame_bands_energy_.end(), 0.f); + if (tot_energy < kSilenceThreshold) { + return true; + } + // Compute the Opus band energies for the lagged frame. + ComputeWindowedForwardFft(lagged_frame, half_window_, fft_buffer_.get(), + lagged_frame_fft_.get(), &fft_); + spectral_correlator_.ComputeAutoCorrelation(lagged_frame_fft_->GetConstView(), + lagged_frame_bands_energy_); + // Log of the band energies for the reference frame. + std::array log_bands_energy; + ComputeSmoothedLogMagnitudeSpectrum(reference_frame_bands_energy_, + log_bands_energy); + // Reference frame cepstrum. + std::array cepstrum; + ComputeDct(log_bands_energy, dct_table_, cepstrum); + // Ad-hoc correction terms for the first two cepstral coefficients. + cepstrum[0] -= 12.f; + cepstrum[1] -= 4.f; + // Update the ring buffer and the cepstral difference stats. + cepstral_coeffs_ring_buf_.Push(cepstrum); + UpdateCepstralDifferenceStats(cepstrum, cepstral_coeffs_ring_buf_, + &cepstral_diffs_buf_); + // Write the higher bands cepstral coefficients. + RTC_DCHECK_EQ(cepstrum.size() - kNumLowerBands, higher_bands_cepstrum.size()); + std::copy(cepstrum.begin() + kNumLowerBands, cepstrum.end(), + higher_bands_cepstrum.begin()); + // Compute and write remaining features. + ComputeAvgAndDerivatives(average, first_derivative, second_derivative); + ComputeNormalizedCepstralCorrelation(bands_cross_corr); + RTC_DCHECK(variability); + *variability = ComputeVariability(); + return false; +} + +void SpectralFeaturesExtractor::ComputeAvgAndDerivatives( + ArrayView average, + ArrayView first_derivative, + ArrayView second_derivative) const { + auto curr = cepstral_coeffs_ring_buf_.GetArrayView(0); + auto prev1 = cepstral_coeffs_ring_buf_.GetArrayView(1); + auto prev2 = cepstral_coeffs_ring_buf_.GetArrayView(2); + RTC_DCHECK_EQ(average.size(), first_derivative.size()); + RTC_DCHECK_EQ(first_derivative.size(), second_derivative.size()); + RTC_DCHECK_LE(average.size(), curr.size()); + for (int i = 0; SafeLt(i, average.size()); ++i) { + // Average, kernel: [1, 1, 1]. + average[i] = curr[i] + prev1[i] + prev2[i]; + // First derivative, kernel: [1, 0, - 1]. + first_derivative[i] = curr[i] - prev2[i]; + // Second derivative, Laplacian kernel: [1, -2, 1]. + second_derivative[i] = curr[i] - 2 * prev1[i] + prev2[i]; + } +} + +void SpectralFeaturesExtractor::ComputeNormalizedCepstralCorrelation( + ArrayView bands_cross_corr) { + spectral_correlator_.ComputeCrossCorrelation( + reference_frame_fft_->GetConstView(), lagged_frame_fft_->GetConstView(), + bands_cross_corr_); + // Normalize. + for (int i = 0; SafeLt(i, bands_cross_corr_.size()); ++i) { + bands_cross_corr_[i] = + bands_cross_corr_[i] / + std::sqrt(0.001f + reference_frame_bands_energy_[i] * + lagged_frame_bands_energy_[i]); + } + // Cepstrum. + ComputeDct(bands_cross_corr_, dct_table_, bands_cross_corr); + // Ad-hoc correction terms for the first two cepstral coefficients. + bands_cross_corr[0] -= 1.3f; + bands_cross_corr[1] -= 0.9f; +} + +float SpectralFeaturesExtractor::ComputeVariability() const { + // Compute cepstral variability score. + float variability = 0.f; + for (int delay1 = 0; delay1 < kCepstralCoeffsHistorySize; ++delay1) { + float min_dist = std::numeric_limits::max(); + for (int delay2 = 0; delay2 < kCepstralCoeffsHistorySize; ++delay2) { + if (delay1 == delay2) // The distance would be 0. + continue; + min_dist = + std::min(min_dist, cepstral_diffs_buf_.GetValue(delay1, delay2)); + } + variability += min_dist; + } + // Normalize (based on training set stats). + // TODO(bugs.webrtc.org/10480): Isolate normalization from feature extraction. + return variability / kCepstralCoeffsHistorySize - 2.1f; +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.h new file mode 100644 index 00000000..714999c4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_H_ + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/ring_buffer.h" +#include "modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h" +#include "modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h" +#include "modules/audio_processing/utility/pffft_wrapper.h" + +namespace webrtc { +namespace rnn_vad { + +// Class to compute spectral features. +class SpectralFeaturesExtractor { + public: + SpectralFeaturesExtractor(); + SpectralFeaturesExtractor(const SpectralFeaturesExtractor&) = delete; + SpectralFeaturesExtractor& operator=(const SpectralFeaturesExtractor&) = + delete; + ~SpectralFeaturesExtractor(); + // Resets the internal state of the feature extractor. + void Reset(); + // Analyzes a pair of reference and lagged frames from the pitch buffer, + // detects silence and computes features. If silence is detected, the output + // is neither computed nor written. + bool CheckSilenceComputeFeatures( + ArrayView reference_frame, + ArrayView lagged_frame, + ArrayView higher_bands_cepstrum, + ArrayView average, + ArrayView first_derivative, + ArrayView second_derivative, + ArrayView bands_cross_corr, + float* variability); + + private: + void ComputeAvgAndDerivatives( + ArrayView average, + ArrayView first_derivative, + ArrayView second_derivative) const; + void ComputeNormalizedCepstralCorrelation( + ArrayView bands_cross_corr); + float ComputeVariability() const; + + const std::array half_window_; + Pffft fft_; + std::unique_ptr fft_buffer_; + std::unique_ptr reference_frame_fft_; + std::unique_ptr lagged_frame_fft_; + SpectralCorrelator spectral_correlator_; + std::array reference_frame_bands_energy_; + std::array lagged_frame_bands_energy_; + std::array bands_cross_corr_; + const std::array dct_table_; + RingBuffer + cepstral_coeffs_ring_buf_; + SymmetricMatrixBuffer cepstral_diffs_buf_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc new file mode 100644 index 00000000..c9aa26a7 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h" + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_compare.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +// Weights for each FFT coefficient for each Opus band (Nyquist frequency +// excluded). The size of each band is specified in +// `kOpusScaleNumBins24kHz20ms`. +constexpr std::array kOpusBandWeights24kHz20ms = + {{ + 0.f, 0.25f, 0.5f, 0.75f, // Band 0 + 0.f, 0.25f, 0.5f, 0.75f, // Band 1 + 0.f, 0.25f, 0.5f, 0.75f, // Band 2 + 0.f, 0.25f, 0.5f, 0.75f, // Band 3 + 0.f, 0.25f, 0.5f, 0.75f, // Band 4 + 0.f, 0.25f, 0.5f, 0.75f, // Band 5 + 0.f, 0.25f, 0.5f, 0.75f, // Band 6 + 0.f, 0.25f, 0.5f, 0.75f, // Band 7 + 0.f, 0.125f, 0.25f, 0.375f, 0.5f, + 0.625f, 0.75f, 0.875f, // Band 8 + 0.f, 0.125f, 0.25f, 0.375f, 0.5f, + 0.625f, 0.75f, 0.875f, // Band 9 + 0.f, 0.125f, 0.25f, 0.375f, 0.5f, + 0.625f, 0.75f, 0.875f, // Band 10 + 0.f, 0.125f, 0.25f, 0.375f, 0.5f, + 0.625f, 0.75f, 0.875f, // Band 11 + 0.f, 0.0625f, 0.125f, 0.1875f, 0.25f, + 0.3125f, 0.375f, 0.4375f, 0.5f, 0.5625f, + 0.625f, 0.6875f, 0.75f, 0.8125f, 0.875f, + 0.9375f, // Band 12 + 0.f, 0.0625f, 0.125f, 0.1875f, 0.25f, + 0.3125f, 0.375f, 0.4375f, 0.5f, 0.5625f, + 0.625f, 0.6875f, 0.75f, 0.8125f, 0.875f, + 0.9375f, // Band 13 + 0.f, 0.0625f, 0.125f, 0.1875f, 0.25f, + 0.3125f, 0.375f, 0.4375f, 0.5f, 0.5625f, + 0.625f, 0.6875f, 0.75f, 0.8125f, 0.875f, + 0.9375f, // Band 14 + 0.f, 0.0416667f, 0.0833333f, 0.125f, 0.166667f, + 0.208333f, 0.25f, 0.291667f, 0.333333f, 0.375f, + 0.416667f, 0.458333f, 0.5f, 0.541667f, 0.583333f, + 0.625f, 0.666667f, 0.708333f, 0.75f, 0.791667f, + 0.833333f, 0.875f, 0.916667f, 0.958333f, // Band 15 + 0.f, 0.0416667f, 0.0833333f, 0.125f, 0.166667f, + 0.208333f, 0.25f, 0.291667f, 0.333333f, 0.375f, + 0.416667f, 0.458333f, 0.5f, 0.541667f, 0.583333f, + 0.625f, 0.666667f, 0.708333f, 0.75f, 0.791667f, + 0.833333f, 0.875f, 0.916667f, 0.958333f, // Band 16 + 0.f, 0.03125f, 0.0625f, 0.09375f, 0.125f, + 0.15625f, 0.1875f, 0.21875f, 0.25f, 0.28125f, + 0.3125f, 0.34375f, 0.375f, 0.40625f, 0.4375f, + 0.46875f, 0.5f, 0.53125f, 0.5625f, 0.59375f, + 0.625f, 0.65625f, 0.6875f, 0.71875f, 0.75f, + 0.78125f, 0.8125f, 0.84375f, 0.875f, 0.90625f, + 0.9375f, 0.96875f, // Band 17 + 0.f, 0.0208333f, 0.0416667f, 0.0625f, 0.0833333f, + 0.104167f, 0.125f, 0.145833f, 0.166667f, 0.1875f, + 0.208333f, 0.229167f, 0.25f, 0.270833f, 0.291667f, + 0.3125f, 0.333333f, 0.354167f, 0.375f, 0.395833f, + 0.416667f, 0.4375f, 0.458333f, 0.479167f, 0.5f, + 0.520833f, 0.541667f, 0.5625f, 0.583333f, 0.604167f, + 0.625f, 0.645833f, 0.666667f, 0.6875f, 0.708333f, + 0.729167f, 0.75f, 0.770833f, 0.791667f, 0.8125f, + 0.833333f, 0.854167f, 0.875f, 0.895833f, 0.916667f, + 0.9375f, 0.958333f, 0.979167f // Band 18 + }}; + +} // namespace + +SpectralCorrelator::SpectralCorrelator() + : weights_(kOpusBandWeights24kHz20ms.begin(), + kOpusBandWeights24kHz20ms.end()) {} + +SpectralCorrelator::~SpectralCorrelator() = default; + +void SpectralCorrelator::ComputeAutoCorrelation( + ArrayView x, + ArrayView auto_corr) const { + ComputeCrossCorrelation(x, x, auto_corr); +} + +void SpectralCorrelator::ComputeCrossCorrelation( + ArrayView x, + ArrayView y, + ArrayView cross_corr) const { + RTC_DCHECK_EQ(x.size(), kFrameSize20ms24kHz); + RTC_DCHECK_EQ(x.size(), y.size()); + RTC_DCHECK_EQ(x[1], 0.f) << "The Nyquist coefficient must be zeroed."; + RTC_DCHECK_EQ(y[1], 0.f) << "The Nyquist coefficient must be zeroed."; + constexpr auto kOpusScaleNumBins24kHz20ms = GetOpusScaleNumBins24kHz20ms(); + int k = 0; // Next Fourier coefficient index. + cross_corr[0] = 0.f; + for (int i = 0; i < kOpusBands24kHz - 1; ++i) { + cross_corr[i + 1] = 0.f; + for (int j = 0; j < kOpusScaleNumBins24kHz20ms[i]; ++j) { // Band size. + const float v = x[2 * k] * y[2 * k] + x[2 * k + 1] * y[2 * k + 1]; + const float tmp = weights_[k] * v; + cross_corr[i] += v - tmp; + cross_corr[i + 1] += tmp; + k++; + } + } + cross_corr[0] *= 2.f; // The first band only gets half contribution. + RTC_DCHECK_EQ(k, kFrameSize20ms24kHz / 2); // Nyquist coefficient never used. +} + +void ComputeSmoothedLogMagnitudeSpectrum( + ArrayView bands_energy, + ArrayView log_bands_energy) { + RTC_DCHECK_LE(bands_energy.size(), kNumBands); + constexpr float kOneByHundred = 1e-2f; + constexpr float kLogOneByHundred = -2.f; + // Init. + float log_max = kLogOneByHundred; + float follow = kLogOneByHundred; + const auto smooth = [&log_max, &follow](float x) { + x = std::max(log_max - 7.f, std::max(follow - 1.5f, x)); + log_max = std::max(log_max, x); + follow = std::max(follow - 1.5f, x); + return x; + }; + // Smoothing over the bands for which the band energy is defined. + for (int i = 0; SafeLt(i, bands_energy.size()); ++i) { + log_bands_energy[i] = smooth(std::log10(kOneByHundred + bands_energy[i])); + } + // Smoothing over the remaining bands (zero energy). + for (int i = bands_energy.size(); i < kNumBands; ++i) { + log_bands_energy[i] = smooth(kLogOneByHundred); + } +} + +std::array ComputeDctTable() { + std::array dct_table; + const double k = std::sqrt(0.5); + for (int i = 0; i < kNumBands; ++i) { + for (int j = 0; j < kNumBands; ++j) + dct_table[i * kNumBands + j] = std::cos((i + 0.5) * j * kPi / kNumBands); + dct_table[i * kNumBands] *= k; + } + return dct_table; +} + +void ComputeDct(ArrayView in, + ArrayView dct_table, + ArrayView out) { + // DCT scaling factor - i.e., sqrt(2 / kNumBands). + constexpr float kDctScalingFactor = 0.301511345f; + constexpr float kDctScalingFactorError = + kDctScalingFactor * kDctScalingFactor - + 2.f / static_cast(kNumBands); + static_assert( + (kDctScalingFactorError >= 0.f && kDctScalingFactorError < 1e-1f) || + (kDctScalingFactorError < 0.f && kDctScalingFactorError > -1e-1f), + "kNumBands changed and kDctScalingFactor has not been updated."); + RTC_DCHECK_NE(in.data(), out.data()) << "In-place DCT is not supported."; + RTC_DCHECK_LE(in.size(), kNumBands); + RTC_DCHECK_LE(1, out.size()); + RTC_DCHECK_LE(out.size(), in.size()); + for (int i = 0; SafeLt(i, out.size()); ++i) { + out[i] = 0.f; + for (int j = 0; SafeLt(j, in.size()); ++j) { + out[i] += in[j] * dct_table[j * kNumBands + i]; + } + // TODO(bugs.webrtc.org/10480): Scaling factor in the DCT table. + out[i] *= kDctScalingFactor; + } +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h new file mode 100644 index 00000000..4d9fd52b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_INTERNAL_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" + +namespace webrtc { +namespace rnn_vad { + +// At a sample rate of 24 kHz, the last 3 Opus bands are beyond the Nyquist +// frequency. However, band #19 gets the contributions from band #18 because +// of the symmetric triangular filter with peak response at 12 kHz. +constexpr int kOpusBands24kHz = 20; +static_assert(kOpusBands24kHz < kNumBands, + "The number of bands at 24 kHz must be less than those defined " + "in the Opus scale at 48 kHz."); + +// Number of FFT frequency bins covered by each band in the Opus scale at a +// sample rate of 24 kHz for 20 ms frames. +// Declared here for unit testing. +constexpr std::array GetOpusScaleNumBins24kHz20ms() { + return {4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 16, 16, 16, 24, 24, 32, 48}; +} + +// TODO(bugs.webrtc.org/10480): Move to a separate file. +// Class to compute band-wise spectral features in the Opus perceptual scale +// for 20 ms frames sampled at 24 kHz. The analysis methods apply triangular +// filters with peak response at the each band boundary. +class SpectralCorrelator { + public: + // Ctor. + SpectralCorrelator(); + SpectralCorrelator(const SpectralCorrelator&) = delete; + SpectralCorrelator& operator=(const SpectralCorrelator&) = delete; + ~SpectralCorrelator(); + + // Computes the band-wise spectral auto-correlations. + // `x` must: + // - have size equal to `kFrameSize20ms24kHz`; + // - be encoded as vectors of interleaved real-complex FFT coefficients + // where x[1] = y[1] = 0 (the Nyquist frequency coefficient is omitted). + void ComputeAutoCorrelation( + ArrayView x, + ArrayView auto_corr) const; + + // Computes the band-wise spectral cross-correlations. + // `x` and `y` must: + // - have size equal to `kFrameSize20ms24kHz`; + // - be encoded as vectors of interleaved real-complex FFT coefficients where + // x[1] = y[1] = 0 (the Nyquist frequency coefficient is omitted). + void ComputeCrossCorrelation( + ArrayView x, + ArrayView y, + ArrayView cross_corr) const; + + private: + const std::vector weights_; // Weights for each Fourier coefficient. +}; + +// TODO(bugs.webrtc.org/10480): Move to anonymous namespace in +// spectral_features.cc. Given a vector of Opus-bands energy coefficients, +// computes the log magnitude spectrum applying smoothing both over time and +// over frequency. Declared here for unit testing. +void ComputeSmoothedLogMagnitudeSpectrum( + ArrayView bands_energy, + ArrayView log_bands_energy); + +// TODO(bugs.webrtc.org/10480): Move to anonymous namespace in +// spectral_features.cc. Creates a DCT table for arrays having size equal to +// `kNumBands`. Declared here for unit testing. +std::array ComputeDctTable(); + +// TODO(bugs.webrtc.org/10480): Move to anonymous namespace in +// spectral_features.cc. Computes DCT for `in` given a pre-computed DCT table. +// In-place computation is not allowed and `out` can be smaller than `in` in +// order to only compute the first DCT coefficients. Declared here for unit +// testing. +void ComputeDct(ArrayView in, + ArrayView dct_table, + ArrayView out); + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_INTERNAL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h new file mode 100644 index 00000000..a84fa32b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SYMMETRIC_MATRIX_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SYMMETRIC_MATRIX_BUFFER_H_ + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_compare.h" + +namespace webrtc { +namespace rnn_vad { + +// Data structure to buffer the results of pair-wise comparisons between items +// stored in a ring buffer. Every time that the oldest item is replaced in the +// ring buffer, the new one is compared to the remaining items in the ring +// buffer. The results of such comparisons need to be buffered and automatically +// removed when one of the two corresponding items that have been compared is +// removed from the ring buffer. It is assumed that the comparison is symmetric +// and that comparing an item with itself is not needed. +template +class SymmetricMatrixBuffer { + static_assert(S > 2, ""); + + public: + SymmetricMatrixBuffer() = default; + SymmetricMatrixBuffer(const SymmetricMatrixBuffer&) = delete; + SymmetricMatrixBuffer& operator=(const SymmetricMatrixBuffer&) = delete; + ~SymmetricMatrixBuffer() = default; + // Sets the buffer values to zero. + void Reset() { + static_assert(std::is_arithmetic::value, + "Integral or floating point required."); + buf_.fill(0); + } + // Pushes the results from the comparison between the most recent item and + // those that are still in the ring buffer. The first element in `values` must + // correspond to the comparison between the most recent item and the second + // most recent one in the ring buffer, whereas the last element in `values` + // must correspond to the comparison between the most recent item and the + // oldest one in the ring buffer. + void Push(ArrayView values) { + // Move the lower-right sub-matrix of size (S-2) x (S-2) one row up and one + // column left. + std::memmove(buf_.data(), buf_.data() + S, (buf_.size() - S) * sizeof(T)); + // Copy new values in the last column in the right order. + for (int i = 0; SafeLt(i, values.size()); ++i) { + const int index = (S - 1 - i) * (S - 1) - 1; + RTC_DCHECK_GE(index, 0); + RTC_DCHECK_LT(index, buf_.size()); + buf_[index] = values[i]; + } + } + // Reads the value that corresponds to comparison of two items in the ring + // buffer having delay `delay1` and `delay2`. The two arguments must not be + // equal and both must be in {0, ..., S - 1}. + T GetValue(int delay1, int delay2) const { + int row = S - 1 - delay1; + int col = S - 1 - delay2; + RTC_DCHECK_NE(row, col) << "The diagonal cannot be accessed."; + if (row > col) + std::swap(row, col); // Swap to access the upper-right triangular part. + RTC_DCHECK_LE(0, row); + RTC_DCHECK_LT(row, S - 1) << "Not enforcing row < col and row != col."; + RTC_DCHECK_LE(1, col) << "Not enforcing row < col and row != col."; + RTC_DCHECK_LT(col, S); + const int index = row * (S - 1) + (col - 1); + RTC_DCHECK_LE(0, index); + RTC_DCHECK_LT(index, buf_.size()); + return buf_[index]; + } + + private: + // Encode an upper-right triangular matrix (excluding its diagonal) using a + // square matrix. This allows to move the data in Push() with one single + // operation. + std::array buf_{}; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SYMMETRIC_MATRIX_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.h new file mode 100644 index 00000000..60417826 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_TEST_UTILS_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_TEST_UTILS_H_ + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_compare.h" + +namespace webrtc { +namespace rnn_vad { + +constexpr float kFloatMin = std::numeric_limits::min(); + +// Fails for every pair from two equally sized webrtc::ArrayView views +// such that the values in the pair do not match. +void ExpectEqualFloatArray(ArrayView expected, + ArrayView computed); + +// Fails for every pair from two equally sized webrtc::ArrayView views +// such that their absolute error is above a given threshold. +void ExpectNearAbsolute(ArrayView expected, + ArrayView computed, + float tolerance); + +// File reader interface. +class FileReader { + public: + virtual ~FileReader() = default; + // Number of values in the file. + virtual int size() const = 0; + // Reads `dst.size()` float values into `dst`, advances the internal file + // position according to the number of read bytes and returns true if the + // values are correctly read. If the number of remaining bytes in the file is + // not sufficient to read `dst.size()` float values, `dst` is partially + // modified and false is returned. + virtual bool ReadChunk(ArrayView dst) = 0; + // Reads a single float value, advances the internal file position according + // to the number of read bytes and returns true if the value is correctly + // read. If the number of remaining bytes in the file is not sufficient to + // read one float, `dst` is not modified and false is returned. + virtual bool ReadValue(float& dst) = 0; + // Advances the internal file position by `hop` float values. + virtual void SeekForward(int hop) = 0; + // Resets the internal file position to BOF. + virtual void SeekBeginning() = 0; +}; + +// File reader for files that contain `num_chunks` chunks with size equal to +// `chunk_size`. +struct ChunksFileReader { + const int chunk_size; + const int num_chunks; + std::unique_ptr reader; +}; + +// Creates a reader for the PCM S16 samples file. +std::unique_ptr CreatePcmSamplesReader(); + +// Creates a reader for the 24 kHz pitch buffer test data. +ChunksFileReader CreatePitchBuffer24kHzReader(); + +// Creates a reader for the LP residual and pitch information test data. +ChunksFileReader CreateLpResidualAndPitchInfoReader(); + +// Creates a reader for the sequence of GRU input vectors. +std::unique_ptr CreateGruInputReader(); + +// Creates a reader for the VAD probabilities test data. +std::unique_ptr CreateVadProbsReader(); + +// Class to retrieve a test pitch buffer content and the expected output for the +// analysis steps. +class PitchTestData { + public: + PitchTestData(); + ~PitchTestData(); + ArrayView PitchBuffer24kHzView() const { + return pitch_buffer_24k_; + } + ArrayView SquareEnergies24kHzView() const { + return square_energies_24k_; + } + ArrayView AutoCorrelation12kHzView() const { + return auto_correlation_12k_; + } + + private: + std::array pitch_buffer_24k_; + std::array square_energies_24k_; + std::array auto_correlation_12k_; +}; + +// Writer for binary files. +class FileWriter { + public: + explicit FileWriter(absl::string_view file_path) + : os_(std::string(file_path), std::ios::binary) {} + FileWriter(const FileWriter&) = delete; + FileWriter& operator=(const FileWriter&) = delete; + ~FileWriter() = default; + void WriteChunk(ArrayView value) { + const std::streamsize bytes_to_write = value.size() * sizeof(float); + os_.write(reinterpret_cast(value.data()), bytes_to_write); + } + + private: + std::ofstream os_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_TEST_UTILS_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/vector_math.h b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/vector_math.h new file mode 100644 index 00000000..0cf4e34e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/vector_math.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_VECTOR_MATH_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_VECTOR_MATH_H_ + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_HAS_NEON) +#include +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/cpu_features.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace rnn_vad { + +// Provides optimizations for mathematical operations having vectors as +// operand(s). +class VectorMath { + public: + explicit VectorMath(AvailableCpuFeatures cpu_features) + : cpu_features_(cpu_features) {} + + // Computes the dot product between two equally sized vectors. + float DotProduct(ArrayView x, ArrayView y) const { + RTC_DCHECK_EQ(x.size(), y.size()); +#if defined(WEBRTC_ARCH_X86_FAMILY) + if (cpu_features_.avx2) { + return DotProductAvx2(x, y); + } else if (cpu_features_.sse2) { + __m128 accumulator = _mm_setzero_ps(); + constexpr int kBlockSizeLog2 = 2; + constexpr int kBlockSize = 1 << kBlockSizeLog2; + const int incomplete_block_index = (x.size() >> kBlockSizeLog2) + << kBlockSizeLog2; + for (int i = 0; i < incomplete_block_index; i += kBlockSize) { + RTC_DCHECK_LE(i + kBlockSize, x.size()); + const __m128 x_i = _mm_loadu_ps(&x[i]); + const __m128 y_i = _mm_loadu_ps(&y[i]); + // Multiply-add. + const __m128 z_j = _mm_mul_ps(x_i, y_i); + accumulator = _mm_add_ps(accumulator, z_j); + } + // Reduce `accumulator` by addition. + __m128 high = _mm_movehl_ps(accumulator, accumulator); + accumulator = _mm_add_ps(accumulator, high); + high = _mm_shuffle_ps(accumulator, accumulator, 1); + accumulator = _mm_add_ps(accumulator, high); + float dot_product = _mm_cvtss_f32(accumulator); + // Add the result for the last block if incomplete. + for (int i = incomplete_block_index; i < dchecked_cast(x.size()); + ++i) { + dot_product += x[i] * y[i]; + } + return dot_product; + } +#elif defined(WEBRTC_HAS_NEON) && defined(WEBRTC_ARCH_ARM64) + if (cpu_features_.neon) { + float32x4_t accumulator = vdupq_n_f32(0.f); + constexpr int kBlockSizeLog2 = 2; + constexpr int kBlockSize = 1 << kBlockSizeLog2; + const int incomplete_block_index = (x.size() >> kBlockSizeLog2) + << kBlockSizeLog2; + for (int i = 0; i < incomplete_block_index; i += kBlockSize) { + RTC_DCHECK_LE(i + kBlockSize, x.size()); + const float32x4_t x_i = vld1q_f32(&x[i]); + const float32x4_t y_i = vld1q_f32(&y[i]); + accumulator = vfmaq_f32(accumulator, x_i, y_i); + } + // Reduce `accumulator` by addition. + const float32x2_t tmp = + vpadd_f32(vget_low_f32(accumulator), vget_high_f32(accumulator)); + float dot_product = vget_lane_f32(vpadd_f32(tmp, vrev64_f32(tmp)), 0); + // Add the result for the last block if incomplete. + for (int i = incomplete_block_index; + i < webrtc::dchecked_cast(x.size()); ++i) { + dot_product += x[i] * y[i]; + } + return dot_product; + } +#endif + return std::inner_product(x.begin(), x.end(), y.begin(), 0.f); + } + + private: + float DotProductAvx2(ArrayView x, + ArrayView y) const; + + const AvailableCpuFeatures cpu_features_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_VECTOR_MATH_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector.cc new file mode 100644 index 00000000..fa350456 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector.cc @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/saturation_protector.h" + +#include + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/agc2/saturation_protector_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +constexpr int kPeakEnveloperSuperFrameLengthMs = 400; +constexpr float kMinMarginDb = 12.0f; +constexpr float kMaxMarginDb = 25.0f; +constexpr float kAttack = 0.9988493699365052f; +constexpr float kDecay = 0.9997697679981565f; + +// Saturation protector state. Defined outside of `SaturationProtectorImpl` to +// implement check-point and restore ops. +struct SaturationProtectorState { + bool operator==(const SaturationProtectorState& s) const { + return headroom_db == s.headroom_db && + peak_delay_buffer == s.peak_delay_buffer && + max_peaks_dbfs == s.max_peaks_dbfs && + time_since_push_ms == s.time_since_push_ms; + } + inline bool operator!=(const SaturationProtectorState& s) const { + return !(*this == s); + } + + float headroom_db; + SaturationProtectorBuffer peak_delay_buffer; + float max_peaks_dbfs; + int time_since_push_ms; // Time since the last ring buffer push operation. +}; + +// Resets the saturation protector state. +void ResetSaturationProtectorState(float initial_headroom_db, + SaturationProtectorState& state) { + state.headroom_db = initial_headroom_db; + state.peak_delay_buffer.Reset(); + state.max_peaks_dbfs = kMinLevelDbfs; + state.time_since_push_ms = 0; +} + +// Updates `state` by analyzing the estimated speech level `speech_level_dbfs` +// and the peak level `peak_dbfs` for an observed frame. `state` must not be +// modified without calling this function. +void UpdateSaturationProtectorState(float peak_dbfs, + float speech_level_dbfs, + SaturationProtectorState& state) { + // Get the max peak over `kPeakEnveloperSuperFrameLengthMs` ms. + state.max_peaks_dbfs = std::max(state.max_peaks_dbfs, peak_dbfs); + state.time_since_push_ms += kFrameDurationMs; + if (SafeGt(state.time_since_push_ms, kPeakEnveloperSuperFrameLengthMs)) { + // Push `max_peaks_dbfs` back into the ring buffer. + state.peak_delay_buffer.PushBack(state.max_peaks_dbfs); + // Reset. + state.max_peaks_dbfs = kMinLevelDbfs; + state.time_since_push_ms = 0; + } + + // Update the headroom by comparing the estimated speech level and the delayed + // max speech peak. + const float delayed_peak_dbfs = + state.peak_delay_buffer.Front().value_or(state.max_peaks_dbfs); + const float difference_db = delayed_peak_dbfs - speech_level_dbfs; + if (difference_db > state.headroom_db) { + // Attack. + state.headroom_db = + state.headroom_db * kAttack + difference_db * (1.0f - kAttack); + } else { + // Decay. + state.headroom_db = + state.headroom_db * kDecay + difference_db * (1.0f - kDecay); + } + + state.headroom_db = + SafeClamp(state.headroom_db, kMinMarginDb, kMaxMarginDb); +} + +// Saturation protector which recommends a headroom based on the recent peaks. +class SaturationProtectorImpl : public SaturationProtector { + public: + explicit SaturationProtectorImpl(float initial_headroom_db, + int adjacent_speech_frames_threshold, + ApmDataDumper* apm_data_dumper) + : apm_data_dumper_(apm_data_dumper), + initial_headroom_db_(initial_headroom_db), + adjacent_speech_frames_threshold_(adjacent_speech_frames_threshold) { + Reset(); + } + SaturationProtectorImpl(const SaturationProtectorImpl&) = delete; + SaturationProtectorImpl& operator=(const SaturationProtectorImpl&) = delete; + ~SaturationProtectorImpl() = default; + + float HeadroomDb() override { return headroom_db_; } + + void Analyze(float speech_probability, + float peak_dbfs, + float speech_level_dbfs) override { + if (speech_probability < kVadConfidenceThreshold) { + // Not a speech frame. + if (adjacent_speech_frames_threshold_ > 1) { + // When two or more adjacent speech frames are required in order to + // update the state, we need to decide whether to discard or confirm the + // updates based on the speech sequence length. + if (num_adjacent_speech_frames_ >= adjacent_speech_frames_threshold_) { + // First non-speech frame after a long enough sequence of speech + // frames. Update the reliable state. + reliable_state_ = preliminary_state_; + } else if (num_adjacent_speech_frames_ > 0) { + // First non-speech frame after a too short sequence of speech frames. + // Reset to the last reliable state. + preliminary_state_ = reliable_state_; + } + } + num_adjacent_speech_frames_ = 0; + } else { + // Speech frame observed. + num_adjacent_speech_frames_++; + + // Update preliminary level estimate. + UpdateSaturationProtectorState(peak_dbfs, speech_level_dbfs, + preliminary_state_); + + if (num_adjacent_speech_frames_ >= adjacent_speech_frames_threshold_) { + // `preliminary_state_` is now reliable. Update the headroom. + headroom_db_ = preliminary_state_.headroom_db; + } + } + DumpDebugData(); + } + + void Reset() override { + num_adjacent_speech_frames_ = 0; + headroom_db_ = initial_headroom_db_; + ResetSaturationProtectorState(initial_headroom_db_, preliminary_state_); + ResetSaturationProtectorState(initial_headroom_db_, reliable_state_); + } + + private: + void DumpDebugData() { + apm_data_dumper_->DumpRaw( + "agc2_saturation_protector_preliminary_max_peak_dbfs", + preliminary_state_.max_peaks_dbfs); + apm_data_dumper_->DumpRaw( + "agc2_saturation_protector_reliable_max_peak_dbfs", + reliable_state_.max_peaks_dbfs); + } + + ApmDataDumper* const apm_data_dumper_; + const float initial_headroom_db_; + const int adjacent_speech_frames_threshold_; + int num_adjacent_speech_frames_; + float headroom_db_; + SaturationProtectorState preliminary_state_; + SaturationProtectorState reliable_state_; +}; + +} // namespace + +std::unique_ptr CreateSaturationProtector( + float initial_headroom_db, + int adjacent_speech_frames_threshold, + ApmDataDumper* apm_data_dumper) { + return std::make_unique( + initial_headroom_db, adjacent_speech_frames_threshold, apm_data_dumper); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector.h b/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector.h new file mode 100644 index 00000000..ef22145d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_SATURATION_PROTECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_SATURATION_PROTECTOR_H_ + +#include + +namespace webrtc { +class ApmDataDumper; + +// Saturation protector. Analyzes peak levels and recommends a headroom to +// reduce the chances of clipping. +class SaturationProtector { + public: + virtual ~SaturationProtector() = default; + + // Returns the recommended headroom in dB. + virtual float HeadroomDb() = 0; + + // Analyzes the peak level of a 10 ms frame along with its speech probability + // and the current speech level estimate to update the recommended headroom. + virtual void Analyze(float speech_probability, + float peak_dbfs, + float speech_level_dbfs) = 0; + + // Resets the internal state. + virtual void Reset() = 0; +}; + +// Creates a saturation protector that starts at `initial_headroom_db`. +std::unique_ptr CreateSaturationProtector( + float initial_headroom_db, + int adjacent_speech_frames_threshold, + ApmDataDumper* apm_data_dumper); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_SATURATION_PROTECTOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector_buffer.cc new file mode 100644 index 00000000..be0c121f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector_buffer.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/saturation_protector_buffer.h" + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_compare.h" + +namespace webrtc { + +SaturationProtectorBuffer::SaturationProtectorBuffer() = default; + +SaturationProtectorBuffer::~SaturationProtectorBuffer() = default; + +bool SaturationProtectorBuffer::operator==( + const SaturationProtectorBuffer& b) const { + RTC_DCHECK_LE(size_, buffer_.size()); + RTC_DCHECK_LE(b.size_, b.buffer_.size()); + if (size_ != b.size_) { + return false; + } + for (int i = 0, i0 = FrontIndex(), i1 = b.FrontIndex(); i < size_; + ++i, ++i0, ++i1) { + if (buffer_[i0 % buffer_.size()] != b.buffer_[i1 % b.buffer_.size()]) { + return false; + } + } + return true; +} + +int SaturationProtectorBuffer::Capacity() const { + return buffer_.size(); +} + +int SaturationProtectorBuffer::Size() const { + return size_; +} + +void SaturationProtectorBuffer::Reset() { + next_ = 0; + size_ = 0; +} + +void SaturationProtectorBuffer::PushBack(float v) { + RTC_DCHECK_GE(next_, 0); + RTC_DCHECK_GE(size_, 0); + RTC_DCHECK_LT(next_, buffer_.size()); + RTC_DCHECK_LE(size_, buffer_.size()); + buffer_[next_++] = v; + if (SafeEq(next_, buffer_.size())) { + next_ = 0; + } + if (SafeLt(size_, buffer_.size())) { + size_++; + } +} + +std::optional SaturationProtectorBuffer::Front() const { + if (size_ == 0) { + return std::nullopt; + } + RTC_DCHECK_LT(FrontIndex(), buffer_.size()); + return buffer_[FrontIndex()]; +} + +int SaturationProtectorBuffer::FrontIndex() const { + return SafeEq(size_, buffer_.size()) ? next_ : 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector_buffer.h b/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector_buffer.h new file mode 100644 index 00000000..3965e937 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/saturation_protector_buffer.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_SATURATION_PROTECTOR_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_SATURATION_PROTECTOR_BUFFER_H_ + +#include +#include + +#include "modules/audio_processing/agc2/agc2_common.h" + +namespace webrtc { + +// Ring buffer for the saturation protector which only supports (i) push back +// and (ii) read oldest item. +class SaturationProtectorBuffer { + public: + SaturationProtectorBuffer(); + ~SaturationProtectorBuffer(); + + bool operator==(const SaturationProtectorBuffer& b) const; + inline bool operator!=(const SaturationProtectorBuffer& b) const { + return !(*this == b); + } + + // Maximum number of values that the buffer can contain. + int Capacity() const; + + // Number of values in the buffer. + int Size() const; + + void Reset(); + + // Pushes back `v`. If the buffer is full, the oldest value is replaced. + void PushBack(float v); + + // Returns the oldest item in the buffer. Returns an empty value if the + // buffer is empty. + std::optional Front() const; + + private: + int FrontIndex() const; + // `buffer_` has `size_` elements (up to the size of `buffer_`) and `next_` is + // the position where the next new value is written in `buffer_`. + std::array buffer_; + int next_ = 0; + int size_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_SATURATION_PROTECTOR_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/speech_level_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/speech_level_estimator.cc new file mode 100644 index 00000000..f9354d16 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/speech_level_estimator.cc @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/speech_level_estimator.h" + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +float ClampLevelEstimateDbfs(float level_estimate_dbfs) { + return SafeClamp(level_estimate_dbfs, -90.0f, 30.0f); +} + +// Returns the initial speech level estimate needed to apply the initial gain. +float GetInitialSpeechLevelEstimateDbfs( + const AudioProcessing::Config::GainController2::AdaptiveDigital& config) { + return ClampLevelEstimateDbfs(-kSaturationProtectorInitialHeadroomDb - + config.initial_gain_db - config.headroom_db); +} + +} // namespace + +bool SpeechLevelEstimator::LevelEstimatorState::operator==( + const SpeechLevelEstimator::LevelEstimatorState& b) const { + return time_to_confidence_ms == b.time_to_confidence_ms && + level_dbfs.numerator == b.level_dbfs.numerator && + level_dbfs.denominator == b.level_dbfs.denominator; +} + +float SpeechLevelEstimator::LevelEstimatorState::Ratio::GetRatio() const { + RTC_DCHECK_NE(denominator, 0.f); + return numerator / denominator; +} + +SpeechLevelEstimator::SpeechLevelEstimator( + ApmDataDumper* apm_data_dumper, + const AudioProcessing::Config::GainController2::AdaptiveDigital& config, + int adjacent_speech_frames_threshold) + : apm_data_dumper_(apm_data_dumper), + initial_speech_level_dbfs_(GetInitialSpeechLevelEstimateDbfs(config)), + adjacent_speech_frames_threshold_(adjacent_speech_frames_threshold), + level_dbfs_(initial_speech_level_dbfs_), + // TODO(bugs.webrtc.org/7494): Remove init below when AGC2 input volume + // controller temporal dependency removed. + is_confident_(false) { + RTC_DCHECK(apm_data_dumper_); + RTC_DCHECK_GE(adjacent_speech_frames_threshold_, 1); + Reset(); +} + +void SpeechLevelEstimator::Update(float rms_dbfs, + float peak_dbfs, + float speech_probability) { + RTC_DCHECK_GT(rms_dbfs, -150.0f); + RTC_DCHECK_LT(rms_dbfs, 50.0f); + RTC_DCHECK_GT(peak_dbfs, -150.0f); + RTC_DCHECK_LT(peak_dbfs, 50.0f); + RTC_DCHECK_GE(speech_probability, 0.0f); + RTC_DCHECK_LE(speech_probability, 1.0f); + if (speech_probability < kVadConfidenceThreshold) { + // Not a speech frame. + if (adjacent_speech_frames_threshold_ > 1) { + // When two or more adjacent speech frames are required in order to update + // the state, we need to decide whether to discard or confirm the updates + // based on the speech sequence length. + if (num_adjacent_speech_frames_ >= adjacent_speech_frames_threshold_) { + // First non-speech frame after a long enough sequence of speech frames. + // Update the reliable state. + reliable_state_ = preliminary_state_; + } else if (num_adjacent_speech_frames_ > 0) { + // First non-speech frame after a too short sequence of speech frames. + // Reset to the last reliable state. + preliminary_state_ = reliable_state_; + } + } + num_adjacent_speech_frames_ = 0; + } else { + // Speech frame observed. + num_adjacent_speech_frames_++; + + // Update preliminary level estimate. + RTC_DCHECK_GE(preliminary_state_.time_to_confidence_ms, 0); + const bool buffer_is_full = preliminary_state_.time_to_confidence_ms == 0; + if (!buffer_is_full) { + preliminary_state_.time_to_confidence_ms -= kFrameDurationMs; + } + // Weighted average of levels with speech probability as weight. + RTC_DCHECK_GT(speech_probability, 0.0f); + const float leak_factor = buffer_is_full ? kLevelEstimatorLeakFactor : 1.0f; + preliminary_state_.level_dbfs.numerator = + preliminary_state_.level_dbfs.numerator * leak_factor + + rms_dbfs * speech_probability; + preliminary_state_.level_dbfs.denominator = + preliminary_state_.level_dbfs.denominator * leak_factor + + speech_probability; + + const float level_dbfs = preliminary_state_.level_dbfs.GetRatio(); + + if (num_adjacent_speech_frames_ >= adjacent_speech_frames_threshold_) { + // `preliminary_state_` is now reliable. Update the last level estimation. + level_dbfs_ = ClampLevelEstimateDbfs(level_dbfs); + } + } + UpdateIsConfident(); + DumpDebugData(); +} + +void SpeechLevelEstimator::UpdateIsConfident() { + if (adjacent_speech_frames_threshold_ == 1) { + // Ignore `reliable_state_` when a single frame is enough to update the + // level estimate (because it is not used). + is_confident_ = preliminary_state_.time_to_confidence_ms == 0; + return; + } + // Once confident, it remains confident. + RTC_DCHECK(reliable_state_.time_to_confidence_ms != 0 || + preliminary_state_.time_to_confidence_ms == 0); + // During the first long enough speech sequence, `reliable_state_` must be + // ignored since `preliminary_state_` is used. + is_confident_ = + reliable_state_.time_to_confidence_ms == 0 || + (num_adjacent_speech_frames_ >= adjacent_speech_frames_threshold_ && + preliminary_state_.time_to_confidence_ms == 0); +} + +void SpeechLevelEstimator::Reset() { + ResetLevelEstimatorState(preliminary_state_); + ResetLevelEstimatorState(reliable_state_); + level_dbfs_ = initial_speech_level_dbfs_; + num_adjacent_speech_frames_ = 0; +} + +void SpeechLevelEstimator::ResetLevelEstimatorState( + LevelEstimatorState& state) const { + state.time_to_confidence_ms = kLevelEstimatorTimeToConfidenceMs; + state.level_dbfs.numerator = initial_speech_level_dbfs_; + state.level_dbfs.denominator = 1.0f; +} + +void SpeechLevelEstimator::DumpDebugData() const { + if (!apm_data_dumper_) + return; + apm_data_dumper_->DumpRaw("agc2_speech_level_dbfs", level_dbfs_); + apm_data_dumper_->DumpRaw("agc2_speech_level_is_confident", is_confident_); + apm_data_dumper_->DumpRaw( + "agc2_adaptive_level_estimator_num_adjacent_speech_frames", + num_adjacent_speech_frames_); + apm_data_dumper_->DumpRaw( + "agc2_adaptive_level_estimator_preliminary_level_estimate_num", + preliminary_state_.level_dbfs.numerator); + apm_data_dumper_->DumpRaw( + "agc2_adaptive_level_estimator_preliminary_level_estimate_den", + preliminary_state_.level_dbfs.denominator); + apm_data_dumper_->DumpRaw( + "agc2_adaptive_level_estimator_preliminary_time_to_confidence_ms", + preliminary_state_.time_to_confidence_ms); + apm_data_dumper_->DumpRaw( + "agc2_adaptive_level_estimator_reliable_time_to_confidence_ms", + reliable_state_.time_to_confidence_ms); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/speech_level_estimator.h b/pkg/apm/webrtc/modules/audio_processing/agc2/speech_level_estimator.h new file mode 100644 index 00000000..adbf84c6 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/speech_level_estimator.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_SPEECH_LEVEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_SPEECH_LEVEL_ESTIMATOR_H_ + +#include + +#include + +#include "api/audio/audio_processing.h" +#include "modules/audio_processing/agc2/agc2_common.h" + +namespace webrtc { +class ApmDataDumper; + +// Active speech level estimator based on the analysis of the following +// framewise properties: RMS level (dBFS), peak level (dBFS), speech +// probability. +class SpeechLevelEstimator { + public: + SpeechLevelEstimator( + ApmDataDumper* apm_data_dumper, + const AudioProcessing::Config::GainController2::AdaptiveDigital& config, + int adjacent_speech_frames_threshold); + SpeechLevelEstimator(const SpeechLevelEstimator&) = delete; + SpeechLevelEstimator& operator=(const SpeechLevelEstimator&) = delete; + + // Updates the level estimation. + void Update(float rms_dbfs, float peak_dbfs, float speech_probability); + // Returns the estimated speech plus noise level. + float level_dbfs() const { return level_dbfs_; } + // Returns true if the estimator is confident on its current estimate. + bool is_confident() const { return is_confident_; } + + void Reset(); + + private: + // Part of the level estimator state used for check-pointing and restore ops. + struct LevelEstimatorState { + bool operator==(const LevelEstimatorState& s) const; + inline bool operator!=(const LevelEstimatorState& s) const { + return !(*this == s); + } + // TODO(bugs.webrtc.org/7494): Remove `time_to_confidence_ms` if redundant. + int time_to_confidence_ms; + struct Ratio { + float numerator; + float denominator; + float GetRatio() const; + } level_dbfs; + }; + static_assert(std::is_trivially_copyable::value, ""); + + void UpdateIsConfident(); + + void ResetLevelEstimatorState(LevelEstimatorState& state) const; + + void DumpDebugData() const; + + ApmDataDumper* const apm_data_dumper_; + + const float initial_speech_level_dbfs_; + const int adjacent_speech_frames_threshold_; + LevelEstimatorState preliminary_state_; + LevelEstimatorState reliable_state_; + float level_dbfs_; + bool is_confident_; + int num_adjacent_speech_frames_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_SPEECH_LEVEL_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/speech_probability_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/speech_probability_buffer.cc new file mode 100644 index 00000000..7746f6c0 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/speech_probability_buffer.cc @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/speech_probability_buffer.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr float kActivityThreshold = 0.9f; +constexpr int kNumAnalysisFrames = 100; +// We use 12 in AGC2 adaptive digital, but with a slightly different logic. +constexpr int kTransientWidthThreshold = 7; + +} // namespace + +SpeechProbabilityBuffer::SpeechProbabilityBuffer( + float low_probability_threshold) + : low_probability_threshold_(low_probability_threshold), + probabilities_(kNumAnalysisFrames) { + RTC_DCHECK_GE(low_probability_threshold, 0.0f); + RTC_DCHECK_LE(low_probability_threshold, 1.0f); + RTC_DCHECK(!probabilities_.empty()); +} + +void SpeechProbabilityBuffer::Update(float probability) { + // Remove the oldest entry if the circular buffer is full. + if (buffer_is_full_) { + const float oldest_probability = probabilities_[buffer_index_]; + sum_probabilities_ -= oldest_probability; + } + + // Check for transients. + if (probability <= low_probability_threshold_) { + // Set a probability lower than the threshold to zero. + probability = 0.0f; + + // Check if this has been a transient. + if (num_high_probability_observations_ <= kTransientWidthThreshold) { + RemoveTransient(); + } + num_high_probability_observations_ = 0; + } else if (num_high_probability_observations_ <= kTransientWidthThreshold) { + ++num_high_probability_observations_; + } + + // Update the circular buffer and the current sum. + probabilities_[buffer_index_] = probability; + sum_probabilities_ += probability; + + // Increment the buffer index and check for wrap-around. + if (++buffer_index_ >= kNumAnalysisFrames) { + buffer_index_ = 0; + buffer_is_full_ = true; + } +} + +void SpeechProbabilityBuffer::RemoveTransient() { + // Don't expect to be here if high-activity region is longer than + // `kTransientWidthThreshold` or there has not been any transient. + RTC_DCHECK_LE(num_high_probability_observations_, kTransientWidthThreshold); + + // Replace previously added probabilities with zero. + int index = + (buffer_index_ > 0) ? (buffer_index_ - 1) : (kNumAnalysisFrames - 1); + + while (num_high_probability_observations_-- > 0) { + sum_probabilities_ -= probabilities_[index]; + probabilities_[index] = 0.0f; + + // Update the circular buffer index. + index = (index > 0) ? (index - 1) : (kNumAnalysisFrames - 1); + } +} + +bool SpeechProbabilityBuffer::IsActiveSegment() const { + if (!buffer_is_full_) { + return false; + } + if (sum_probabilities_ < kActivityThreshold * kNumAnalysisFrames) { + return false; + } + return true; +} + +void SpeechProbabilityBuffer::Reset() { + sum_probabilities_ = 0.0f; + + // Empty the circular buffer. + buffer_index_ = 0; + buffer_is_full_ = false; + num_high_probability_observations_ = 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/speech_probability_buffer.h b/pkg/apm/webrtc/modules/audio_processing/agc2/speech_probability_buffer.h new file mode 100644 index 00000000..3056a3ee --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/speech_probability_buffer.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_SPEECH_PROBABILITY_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_SPEECH_PROBABILITY_BUFFER_H_ + +#include + +#include "rtc_base/gtest_prod_util.h" + +namespace webrtc { + +// This class implements a circular buffer that stores speech probabilities +// for a speech segment and estimates speech activity for that segment. +class SpeechProbabilityBuffer { + public: + // Ctor. The value of `low_probability_threshold` is required to be on the + // range [0.0f, 1.0f]. + explicit SpeechProbabilityBuffer(float low_probability_threshold); + ~SpeechProbabilityBuffer() {} + SpeechProbabilityBuffer(const SpeechProbabilityBuffer&) = delete; + SpeechProbabilityBuffer& operator=(const SpeechProbabilityBuffer&) = delete; + + // Adds `probability` in the buffer and computes an updatds sum of the buffer + // probabilities. Value of `probability` is required to be on the range + // [0.0f, 1.0f]. + void Update(float probability); + + // Resets the histogram, forgets the past. + void Reset(); + + // Returns true if the segment is active (a long enough segment with an + // average speech probability above `low_probability_threshold`). + bool IsActiveSegment() const; + + private: + void RemoveTransient(); + + // Use only for testing. + float GetSumProbabilities() const { return sum_probabilities_; } + + FRIEND_TEST_ALL_PREFIXES(SpeechProbabilityBufferTest, + CheckSumAfterInitialization); + FRIEND_TEST_ALL_PREFIXES(SpeechProbabilityBufferTest, CheckSumAfterUpdate); + FRIEND_TEST_ALL_PREFIXES(SpeechProbabilityBufferTest, CheckSumAfterReset); + FRIEND_TEST_ALL_PREFIXES(SpeechProbabilityBufferTest, + CheckSumAfterTransientNotRemoved); + FRIEND_TEST_ALL_PREFIXES(SpeechProbabilityBufferTest, + CheckSumAfterTransientRemoved); + + const float low_probability_threshold_; + + // Sum of probabilities stored in `probabilities_`. Must be updated if + // `probabilities_` is updated. + float sum_probabilities_ = 0.0f; + + // Circular buffer for probabilities. + std::vector probabilities_; + + // Current index of the circular buffer, where the newest data will be written + // to, therefore, pointing to the oldest data if buffer is full. + int buffer_index_ = 0; + + // Indicates if the buffer is full and adding a new value removes the oldest + // value. + int buffer_is_full_ = false; + + int num_high_probability_observations_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_SPEECH_PROBABILITY_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/vad_wrapper.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/vad_wrapper.cc new file mode 100644 index 00000000..238c51e4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/vad_wrapper.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/vad_wrapper.h" + +#include +#include + +#include "common_audio/resampler/include/push_resampler.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/features_extraction.h" +#include "modules/audio_processing/agc2/rnn_vad/rnn.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr int kNumFramesPerSecond = 100; + +class MonoVadImpl : public VoiceActivityDetectorWrapper::MonoVad { + public: + explicit MonoVadImpl(const AvailableCpuFeatures& cpu_features) + : features_extractor_(cpu_features), rnn_vad_(cpu_features) {} + MonoVadImpl(const MonoVadImpl&) = delete; + MonoVadImpl& operator=(const MonoVadImpl&) = delete; + ~MonoVadImpl() = default; + + int SampleRateHz() const override { return rnn_vad::kSampleRate24kHz; } + void Reset() override { rnn_vad_.Reset(); } + float Analyze(MonoView frame) override { + RTC_DCHECK_EQ(frame.size(), rnn_vad::kFrameSize10ms24kHz); + std::array feature_vector; + const bool is_silence = features_extractor_.CheckSilenceComputeFeatures( + /*samples=*/{frame.data(), rnn_vad::kFrameSize10ms24kHz}, + feature_vector); + return rnn_vad_.ComputeVadProbability(feature_vector, is_silence); + } + + private: + rnn_vad::FeaturesExtractor features_extractor_; + rnn_vad::RnnVad rnn_vad_; +}; + +} // namespace + +VoiceActivityDetectorWrapper::VoiceActivityDetectorWrapper( + const AvailableCpuFeatures& cpu_features, + int sample_rate_hz) + : VoiceActivityDetectorWrapper(kVadResetPeriodMs, + cpu_features, + sample_rate_hz) {} + +VoiceActivityDetectorWrapper::VoiceActivityDetectorWrapper( + int vad_reset_period_ms, + const AvailableCpuFeatures& cpu_features, + int sample_rate_hz) + : VoiceActivityDetectorWrapper(vad_reset_period_ms, + std::make_unique(cpu_features), + sample_rate_hz) {} + +VoiceActivityDetectorWrapper::VoiceActivityDetectorWrapper( + int vad_reset_period_ms, + std::unique_ptr vad, + int sample_rate_hz) + : vad_reset_period_frames_( + CheckedDivExact(vad_reset_period_ms, kFrameDurationMs)), + frame_size_(CheckedDivExact(sample_rate_hz, kNumFramesPerSecond)), + time_to_vad_reset_(vad_reset_period_frames_), + vad_(std::move(vad)), + resampled_buffer_( + CheckedDivExact(vad_->SampleRateHz(), kNumFramesPerSecond)), + resampler_(frame_size_, + resampled_buffer_.size(), + /*num_channels=*/1) { + RTC_DCHECK_GT(vad_reset_period_frames_, 1); + vad_->Reset(); +} + +VoiceActivityDetectorWrapper::~VoiceActivityDetectorWrapper() = default; + +float VoiceActivityDetectorWrapper::Analyze( + DeinterleavedView frame) { + // Periodically reset the VAD. + time_to_vad_reset_--; + if (time_to_vad_reset_ <= 0) { + vad_->Reset(); + time_to_vad_reset_ = vad_reset_period_frames_; + } + + // Resample the first channel of `frame`. + RTC_DCHECK_EQ(frame.samples_per_channel(), frame_size_); + MonoView dst(resampled_buffer_.data(), resampled_buffer_.size()); + resampler_.Resample(frame[0], dst); + + return vad_->Analyze(resampled_buffer_); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/vad_wrapper.h b/pkg/apm/webrtc/modules/audio_processing/agc2/vad_wrapper.h new file mode 100644 index 00000000..025a48ef --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/vad_wrapper.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_VAD_WRAPPER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_VAD_WRAPPER_H_ + +#include +#include + +#include "api/audio/audio_view.h" +#include "common_audio/resampler/include/push_resampler.h" +#include "modules/audio_processing/agc2/cpu_features.h" + +namespace webrtc { + +// Wraps a single-channel Voice Activity Detector (VAD) which is used to analyze +// the first channel of the input audio frames. Takes care of resampling the +// input frames to match the sample rate of the wrapped VAD and periodically +// resets the VAD. +class VoiceActivityDetectorWrapper { + public: + // Single channel VAD interface. + class MonoVad { + public: + virtual ~MonoVad() = default; + // Returns the sample rate (Hz) required for the input frames analyzed by + // `ComputeProbability`. + virtual int SampleRateHz() const = 0; + // Resets the internal state. + virtual void Reset() = 0; + // Analyzes an audio frame and returns the speech probability. + virtual float Analyze(MonoView frame) = 0; + }; + + // Ctor. Uses `cpu_features` to instantiate the default VAD. + VoiceActivityDetectorWrapper(const AvailableCpuFeatures& cpu_features, + int sample_rate_hz); + + // Ctor. `vad_reset_period_ms` indicates the period in milliseconds to call + // `MonoVad::Reset()`; it must be equal to or greater than the duration of two + // frames. Uses `cpu_features` to instantiate the default VAD. + VoiceActivityDetectorWrapper(int vad_reset_period_ms, + const AvailableCpuFeatures& cpu_features, + int sample_rate_hz); + // Ctor. Uses a custom `vad`. + VoiceActivityDetectorWrapper(int vad_reset_period_ms, + std::unique_ptr vad, + int sample_rate_hz); + + VoiceActivityDetectorWrapper(const VoiceActivityDetectorWrapper&) = delete; + VoiceActivityDetectorWrapper& operator=(const VoiceActivityDetectorWrapper&) = + delete; + ~VoiceActivityDetectorWrapper(); + + // Analyzes the first channel of `frame` and returns the speech probability. + // `frame` must be a 10 ms frame with the sample rate specified in the last + // `Initialize()` call. + float Analyze(DeinterleavedView frame); + + private: + const int vad_reset_period_frames_; + const int frame_size_; + int time_to_vad_reset_; + std::unique_ptr vad_; + std::vector resampled_buffer_; + PushResampler resampler_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_VAD_WRAPPER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/vector_float_frame.cc b/pkg/apm/webrtc/modules/audio_processing/agc2/vector_float_frame.cc new file mode 100644 index 00000000..85dd7feb --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/vector_float_frame.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/agc2/vector_float_frame.h" + +namespace webrtc { + +VectorFloatFrame::VectorFloatFrame(int num_channels, + int samples_per_channel, + float start_value) + : channels_(num_channels * samples_per_channel, start_value), + view_(channels_.data(), samples_per_channel, num_channels) {} + +VectorFloatFrame::~VectorFloatFrame() = default; + +AudioFrameView VectorFloatFrame::float_frame_view() { + return AudioFrameView(view_); +} + +AudioFrameView VectorFloatFrame::float_frame_view() const { + return AudioFrameView(view_); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/agc2/vector_float_frame.h b/pkg/apm/webrtc/modules/audio_processing/agc2/vector_float_frame.h new file mode 100644 index 00000000..e2a32113 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/agc2/vector_float_frame.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_VECTOR_FLOAT_FRAME_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_VECTOR_FLOAT_FRAME_H_ + +#include + +#include "api/audio/audio_view.h" +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { + +// A construct consisting of a multi-channel audio frame, and a FloatFrame view +// of it. +class VectorFloatFrame { + public: + VectorFloatFrame(int num_channels, + int samples_per_channel, + float start_value); + ~VectorFloatFrame(); + + AudioFrameView float_frame_view(); + AudioFrameView float_frame_view() const; + + DeinterleavedView view() { return view_; } + DeinterleavedView view() const { return view_; } + + private: + std::vector channels_; + DeinterleavedView view_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_VECTOR_FLOAT_FRAME_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/apm.go b/pkg/apm/webrtc/modules/audio_processing/apm.go new file mode 100644 index 00000000..e8400c88 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/apm.go @@ -0,0 +1,24 @@ +//go:build console + +package audio_processing + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" + +import ( + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/aec3" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/aecm" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/agc" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/agc2" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/echo_detector" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/include" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/logging" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/ns" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/utility" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/vad" +) diff --git a/pkg/apm/webrtc/modules/audio_processing/apm_amd64.go b/pkg/apm/webrtc/modules/audio_processing/apm_amd64.go new file mode 100644 index 00000000..51895b8e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/apm_amd64.go @@ -0,0 +1,8 @@ +//go:build console && amd64 + +package audio_processing + +import ( + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/aec3/avx2" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/modules/audio_processing/agc2/rnn_vad/avx2" +) diff --git a/pkg/apm/webrtc/modules/audio_processing/audio_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/audio_buffer.cc new file mode 100644 index 00000000..a1b55fe1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/audio_buffer.cc @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/audio_buffer.h" + +#include + +#include + +#include "common_audio/channel_buffer.h" +#include "common_audio/resampler/push_sinc_resampler.h" +#include "modules/audio_processing/splitting_filter.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr size_t kSamplesPer32kHzChannel = 320; +constexpr size_t kSamplesPer48kHzChannel = 480; + +size_t NumBandsFromFramesPerChannel(size_t num_frames) { + if (num_frames == kSamplesPer32kHzChannel) { + return 2; + } + if (num_frames == kSamplesPer48kHzChannel) { + return 3; + } + return 1; +} + +} // namespace + +AudioBuffer::AudioBuffer(size_t input_rate, + size_t input_num_channels, + size_t buffer_rate, + size_t buffer_num_channels, + size_t output_rate, + size_t /* output_num_channels */) + : input_num_frames_(static_cast(input_rate) / 100), + input_num_channels_(input_num_channels), + buffer_num_frames_(static_cast(buffer_rate) / 100), + buffer_num_channels_(buffer_num_channels), + output_num_frames_(static_cast(output_rate) / 100), + output_num_channels_(0), + num_channels_(buffer_num_channels), + num_bands_(NumBandsFromFramesPerChannel(buffer_num_frames_)), + num_split_frames_(CheckedDivExact(buffer_num_frames_, num_bands_)), + data_( + new ChannelBuffer(buffer_num_frames_, buffer_num_channels_)) { + RTC_DCHECK_GT(input_num_frames_, 0); + RTC_DCHECK_GT(buffer_num_frames_, 0); + RTC_DCHECK_GT(output_num_frames_, 0); + RTC_DCHECK_GT(input_num_channels_, 0); + RTC_DCHECK_GT(buffer_num_channels_, 0); + RTC_DCHECK_LE(buffer_num_channels_, input_num_channels_); + + const bool input_resampling_needed = input_num_frames_ != buffer_num_frames_; + const bool output_resampling_needed = + output_num_frames_ != buffer_num_frames_; + if (input_resampling_needed) { + for (size_t i = 0; i < buffer_num_channels_; ++i) { + input_resamplers_.push_back(std::unique_ptr( + new PushSincResampler(input_num_frames_, buffer_num_frames_))); + } + } + + if (output_resampling_needed) { + for (size_t i = 0; i < buffer_num_channels_; ++i) { + output_resamplers_.push_back(std::unique_ptr( + new PushSincResampler(buffer_num_frames_, output_num_frames_))); + } + } + + if (num_bands_ > 1) { + split_data_.reset(new ChannelBuffer( + buffer_num_frames_, buffer_num_channels_, num_bands_)); + splitting_filter_.reset(new SplittingFilter( + buffer_num_channels_, num_bands_, buffer_num_frames_)); + } +} + +AudioBuffer::~AudioBuffer() {} + +void AudioBuffer::set_downmixing_to_specific_channel(size_t channel) { + downmix_by_averaging_ = false; + RTC_DCHECK_GT(input_num_channels_, channel); + channel_for_downmixing_ = std::min(channel, input_num_channels_ - 1); +} + +void AudioBuffer::set_downmixing_by_averaging() { + downmix_by_averaging_ = true; +} + +void AudioBuffer::CopyFrom(const float* const* stacked_data, + const StreamConfig& stream_config) { + RTC_DCHECK_EQ(stream_config.num_frames(), input_num_frames_); + RTC_DCHECK_EQ(stream_config.num_channels(), input_num_channels_); + RestoreNumChannels(); + const bool downmix_needed = input_num_channels_ > 1 && num_channels_ == 1; + + const bool resampling_needed = input_num_frames_ != buffer_num_frames_; + + if (downmix_needed) { + RTC_DCHECK_GE(kMaxSamplesPerChannel10ms, input_num_frames_); + + std::array downmix; + if (downmix_by_averaging_) { + const float kOneByNumChannels = 1.f / input_num_channels_; + for (size_t i = 0; i < input_num_frames_; ++i) { + float value = stacked_data[0][i]; + for (size_t j = 1; j < input_num_channels_; ++j) { + value += stacked_data[j][i]; + } + downmix[i] = value * kOneByNumChannels; + } + } + const float* downmixed_data = downmix_by_averaging_ + ? downmix.data() + : stacked_data[channel_for_downmixing_]; + + if (resampling_needed) { + input_resamplers_[0]->Resample(downmixed_data, input_num_frames_, + data_->channels()[0], buffer_num_frames_); + } + const float* data_to_convert = + resampling_needed ? data_->channels()[0] : downmixed_data; + FloatToFloatS16(data_to_convert, buffer_num_frames_, data_->channels()[0]); + } else { + if (resampling_needed) { + for (size_t i = 0; i < num_channels_; ++i) { + input_resamplers_[i]->Resample(stacked_data[i], input_num_frames_, + data_->channels()[i], + buffer_num_frames_); + FloatToFloatS16(data_->channels()[i], buffer_num_frames_, + data_->channels()[i]); + } + } else { + for (size_t i = 0; i < num_channels_; ++i) { + FloatToFloatS16(stacked_data[i], buffer_num_frames_, + data_->channels()[i]); + } + } + } +} + +void AudioBuffer::CopyTo(const StreamConfig& stream_config, + float* const* stacked_data) { + RTC_DCHECK_EQ(stream_config.num_frames(), output_num_frames_); + + const bool resampling_needed = output_num_frames_ != buffer_num_frames_; + if (resampling_needed) { + for (size_t i = 0; i < num_channels_; ++i) { + FloatS16ToFloat(data_->channels()[i], buffer_num_frames_, + data_->channels()[i]); + output_resamplers_[i]->Resample(data_->channels()[i], buffer_num_frames_, + stacked_data[i], output_num_frames_); + } + } else { + for (size_t i = 0; i < num_channels_; ++i) { + FloatS16ToFloat(data_->channels()[i], buffer_num_frames_, + stacked_data[i]); + } + } + + for (size_t i = num_channels_; i < stream_config.num_channels(); ++i) { + memcpy(stacked_data[i], stacked_data[0], + output_num_frames_ * sizeof(**stacked_data)); + } +} + +void AudioBuffer::CopyTo(AudioBuffer* buffer) const { + RTC_DCHECK_EQ(buffer->num_frames(), output_num_frames_); + + const bool resampling_needed = output_num_frames_ != buffer_num_frames_; + if (resampling_needed) { + for (size_t i = 0; i < num_channels_; ++i) { + output_resamplers_[i]->Resample(data_->channels()[i], buffer_num_frames_, + buffer->channels()[i], + buffer->num_frames()); + } + } else { + for (size_t i = 0; i < num_channels_; ++i) { + memcpy(buffer->channels()[i], data_->channels()[i], + buffer_num_frames_ * sizeof(**buffer->channels())); + } + } + + for (size_t i = num_channels_; i < buffer->num_channels(); ++i) { + memcpy(buffer->channels()[i], buffer->channels()[0], + output_num_frames_ * sizeof(**buffer->channels())); + } +} + +void AudioBuffer::RestoreNumChannels() { + num_channels_ = buffer_num_channels_; + data_->set_num_channels(buffer_num_channels_); + if (split_data_.get()) { + split_data_->set_num_channels(buffer_num_channels_); + } +} + +void AudioBuffer::set_num_channels(size_t num_channels) { + RTC_DCHECK_GE(buffer_num_channels_, num_channels); + num_channels_ = num_channels; + data_->set_num_channels(num_channels); + if (split_data_.get()) { + split_data_->set_num_channels(num_channels); + } +} + +// The resampler is only for supporting 48kHz to 16kHz in the reverse stream. +void AudioBuffer::CopyFrom(const int16_t* const interleaved_data, + const StreamConfig& stream_config) { + RTC_DCHECK_EQ(stream_config.num_channels(), input_num_channels_); + RTC_DCHECK_EQ(stream_config.num_frames(), input_num_frames_); + RestoreNumChannels(); + + const bool resampling_required = input_num_frames_ != buffer_num_frames_; + + const int16_t* interleaved = interleaved_data; + if (num_channels_ == 1) { + if (input_num_channels_ == 1) { + if (resampling_required) { + std::array float_buffer; + S16ToFloatS16(interleaved, input_num_frames_, float_buffer.data()); + input_resamplers_[0]->Resample(float_buffer.data(), input_num_frames_, + data_->channels()[0], + buffer_num_frames_); + } else { + S16ToFloatS16(interleaved, input_num_frames_, data_->channels()[0]); + } + } else { + std::array float_buffer; + float* downmixed_data = + resampling_required ? float_buffer.data() : data_->channels()[0]; + if (downmix_by_averaging_) { + for (size_t j = 0, k = 0; j < input_num_frames_; ++j) { + int32_t sum = 0; + for (size_t i = 0; i < input_num_channels_; ++i, ++k) { + sum += interleaved[k]; + } + downmixed_data[j] = sum / static_cast(input_num_channels_); + } + } else { + for (size_t j = 0, k = channel_for_downmixing_; j < input_num_frames_; + ++j, k += input_num_channels_) { + downmixed_data[j] = interleaved[k]; + } + } + + if (resampling_required) { + input_resamplers_[0]->Resample(downmixed_data, input_num_frames_, + data_->channels()[0], + buffer_num_frames_); + } + } + } else { + auto deinterleave_channel = [](size_t channel, size_t num_channels, + size_t samples_per_channel, const int16_t* x, + float* y) { + for (size_t j = 0, k = channel; j < samples_per_channel; + ++j, k += num_channels) { + y[j] = x[k]; + } + }; + + if (resampling_required) { + std::array float_buffer; + for (size_t i = 0; i < num_channels_; ++i) { + deinterleave_channel(i, num_channels_, input_num_frames_, interleaved, + float_buffer.data()); + input_resamplers_[i]->Resample(float_buffer.data(), input_num_frames_, + data_->channels()[i], + buffer_num_frames_); + } + } else { + for (size_t i = 0; i < num_channels_; ++i) { + deinterleave_channel(i, num_channels_, input_num_frames_, interleaved, + data_->channels()[i]); + } + } + } +} + +void AudioBuffer::CopyTo(const StreamConfig& stream_config, + int16_t* const interleaved_data) { + const size_t config_num_channels = stream_config.num_channels(); + + RTC_DCHECK(config_num_channels == num_channels_ || num_channels_ == 1); + RTC_DCHECK_EQ(stream_config.num_frames(), output_num_frames_); + + const bool resampling_required = buffer_num_frames_ != output_num_frames_; + + int16_t* interleaved = interleaved_data; + if (num_channels_ == 1) { + std::array float_buffer; + + if (resampling_required) { + output_resamplers_[0]->Resample(data_->channels()[0], buffer_num_frames_, + float_buffer.data(), output_num_frames_); + } + const float* deinterleaved = + resampling_required ? float_buffer.data() : data_->channels()[0]; + + if (config_num_channels == 1) { + for (size_t j = 0; j < output_num_frames_; ++j) { + interleaved[j] = FloatS16ToS16(deinterleaved[j]); + } + } else { + for (size_t i = 0, k = 0; i < output_num_frames_; ++i) { + float tmp = FloatS16ToS16(deinterleaved[i]); + for (size_t j = 0; j < config_num_channels; ++j, ++k) { + interleaved[k] = tmp; + } + } + } + } else { + auto interleave_channel = [](size_t channel, size_t num_channels, + size_t samples_per_channel, const float* x, + int16_t* y) { + for (size_t k = 0, j = channel; k < samples_per_channel; + ++k, j += num_channels) { + y[j] = FloatS16ToS16(x[k]); + } + }; + + if (resampling_required) { + for (size_t i = 0; i < num_channels_; ++i) { + std::array float_buffer; + output_resamplers_[i]->Resample(data_->channels()[i], + buffer_num_frames_, float_buffer.data(), + output_num_frames_); + interleave_channel(i, config_num_channels, output_num_frames_, + float_buffer.data(), interleaved); + } + } else { + for (size_t i = 0; i < num_channels_; ++i) { + interleave_channel(i, config_num_channels, output_num_frames_, + data_->channels()[i], interleaved); + } + } + + for (size_t i = num_channels_; i < config_num_channels; ++i) { + for (size_t j = 0, k = i, n = num_channels_; j < output_num_frames_; + ++j, k += config_num_channels, n += config_num_channels) { + interleaved[k] = interleaved[n]; + } + } + } +} + +void AudioBuffer::SplitIntoFrequencyBands() { + splitting_filter_->Analysis(data_.get(), split_data_.get()); +} + +void AudioBuffer::MergeFrequencyBands() { + splitting_filter_->Synthesis(split_data_.get(), data_.get()); +} + +void AudioBuffer::ExportSplitChannelData( + size_t channel, + int16_t* const* split_band_data) const { + for (size_t k = 0; k < num_bands(); ++k) { + const float* band_data = split_bands_const(channel)[k]; + + RTC_DCHECK(split_band_data[k]); + RTC_DCHECK(band_data); + for (size_t i = 0; i < num_frames_per_band(); ++i) { + split_band_data[k][i] = FloatS16ToS16(band_data[i]); + } + } +} + +void AudioBuffer::ImportSplitChannelData( + size_t channel, + const int16_t* const* split_band_data) { + for (size_t k = 0; k < num_bands(); ++k) { + float* band_data = split_bands(channel)[k]; + RTC_DCHECK(split_band_data[k]); + RTC_DCHECK(band_data); + for (size_t i = 0; i < num_frames_per_band(); ++i) { + band_data[i] = split_band_data[k][i]; + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/audio_buffer.h b/pkg/apm/webrtc/modules/audio_processing/audio_buffer.h new file mode 100644 index 00000000..9369572a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/audio_buffer.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ + +#include +#include + +#include +#include + +#include "api/audio/audio_processing.h" +#include "api/audio/audio_view.h" +#include "common_audio/channel_buffer.h" +#include "common_audio/include/audio_util.h" + +namespace webrtc { + +class PushSincResampler; +class SplittingFilter; + +enum Band { kBand0To8kHz = 0, kBand8To16kHz = 1, kBand16To24kHz = 2 }; + +// Stores any audio data in a way that allows the audio processing module to +// operate on it in a controlled manner. +class AudioBuffer { + public: + static const int kSplitBandSize = 160; + // TODO(tommi): Remove this (`AudioBuffer::kMaxSampleRate`) constant. + static const int kMaxSampleRate = webrtc::kMaxSampleRateHz; + AudioBuffer(size_t input_rate, + size_t input_num_channels, + size_t buffer_rate, + size_t buffer_num_channels, + size_t output_rate, + size_t output_num_channels); + + virtual ~AudioBuffer(); + + AudioBuffer(const AudioBuffer&) = delete; + AudioBuffer& operator=(const AudioBuffer&) = delete; + + // Specify that downmixing should be done by selecting a single channel. + void set_downmixing_to_specific_channel(size_t channel); + + // Specify that downmixing should be done by averaging all channels,. + void set_downmixing_by_averaging(); + + // Set the number of channels in the buffer. The specified number of channels + // cannot be larger than the specified buffer_num_channels. The number is also + // reset at each call to CopyFrom or InterleaveFrom. + void set_num_channels(size_t num_channels); + + // Returns a DeinterleavedView<> over the channel data. + DeinterleavedView view() { + return DeinterleavedView( + num_channels_ && buffer_num_frames_ ? channels()[0] : nullptr, + buffer_num_frames_, num_channels_); + } + + size_t num_channels() const { return num_channels_; } + size_t num_frames() const { return buffer_num_frames_; } + size_t num_frames_per_band() const { return num_split_frames_; } + size_t num_bands() const { return num_bands_; } + + // Returns pointer arrays to the full-band channels. + // Usage: + // channels()[channel][sample]. + // Where: + // 0 <= channel < `buffer_num_channels_` + // 0 <= sample < `buffer_num_frames_` + float* const* channels() { return data_->channels(); } + const float* const* channels_const() const { return data_->channels(); } + + // Returns pointer arrays to the bands for a specific channel. + // Usage: + // split_bands(channel)[band][sample]. + // Where: + // 0 <= channel < `buffer_num_channels_` + // 0 <= band < `num_bands_` + // 0 <= sample < `num_split_frames_` + const float* const* split_bands_const(size_t channel) const { + return split_data_.get() ? split_data_->bands(channel) + : data_->bands(channel); + } + float* const* split_bands(size_t channel) { + return split_data_.get() ? split_data_->bands(channel) + : data_->bands(channel); + } + + // Returns a pointer array to the channels for a specific band. + // Usage: + // split_channels(band)[channel][sample]. + // Where: + // 0 <= band < `num_bands_` + // 0 <= channel < `buffer_num_channels_` + // 0 <= sample < `num_split_frames_` + const float* const* split_channels_const(Band band) const { + if (split_data_.get()) { + return split_data_->channels(band); + } else { + return band == kBand0To8kHz ? data_->channels() : nullptr; + } + } + + // Copies data into the buffer. + void CopyFrom(const int16_t* const interleaved_data, + const StreamConfig& stream_config); + void CopyFrom(const float* const* stacked_data, + const StreamConfig& stream_config); + + // Copies data from the buffer. + void CopyTo(const StreamConfig& stream_config, + int16_t* const interleaved_data); + void CopyTo(const StreamConfig& stream_config, float* const* stacked_data); + void CopyTo(AudioBuffer* buffer) const; + + // Splits the buffer data into frequency bands. + void SplitIntoFrequencyBands(); + + // Recombines the frequency bands into a full-band signal. + void MergeFrequencyBands(); + + // Copies the split bands data into the integer two-dimensional array. + void ExportSplitChannelData(size_t channel, + int16_t* const* split_band_data) const; + + // Copies the data in the integer two-dimensional array into the split_bands + // data. + void ImportSplitChannelData(size_t channel, + const int16_t* const* split_band_data); + + static const size_t kMaxSplitFrameLength = 160; + static const size_t kMaxNumBands = 3; + + // Deprecated methods, will be removed soon. + float* const* channels_f() { return channels(); } + const float* const* channels_const_f() const { return channels_const(); } + const float* const* split_bands_const_f(size_t channel) const { + return split_bands_const(channel); + } + float* const* split_bands_f(size_t channel) { return split_bands(channel); } + const float* const* split_channels_const_f(Band band) const { + return split_channels_const(band); + } + + private: + FRIEND_TEST_ALL_PREFIXES(AudioBufferTest, + SetNumChannelsSetsChannelBuffersNumChannels); + void RestoreNumChannels(); + + const size_t input_num_frames_; + const size_t input_num_channels_; + const size_t buffer_num_frames_; + const size_t buffer_num_channels_; + const size_t output_num_frames_; + const size_t output_num_channels_; + + size_t num_channels_; + size_t num_bands_; + size_t num_split_frames_; + + std::unique_ptr> data_; + std::unique_ptr> split_data_; + std::unique_ptr splitting_filter_; + std::vector> input_resamplers_; + std::vector> output_resamplers_; + bool downmix_by_averaging_ = true; + size_t channel_for_downmixing_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/audio_processing_impl.cc b/pkg/apm/webrtc/modules/audio_processing/audio_processing_impl.cc new file mode 100644 index 00000000..99a351e9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/audio_processing_impl.cc @@ -0,0 +1,2303 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/audio_processing_impl.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/nullability.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/audio/audio_frame.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_base.h" +#include "common_audio/audio_converter.h" +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/aec_dump/aec_dump_factory.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/include/audio_frame_view.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/time_utils.h" +#include "rtc_base/trace_event.h" +#include "system_wrappers/include/denormal_disabler.h" +#include "system_wrappers/include/metrics.h" + +#define RETURN_ON_ERR(expr) \ + do { \ + int err = (expr); \ + if (err != kNoError) { \ + return err; \ + } \ + } while (0) + +namespace webrtc { + +namespace { + +bool SampleRateSupportsMultiBand(int sample_rate_hz) { + return sample_rate_hz == AudioProcessing::kSampleRate32kHz || + sample_rate_hz == AudioProcessing::kSampleRate48kHz; +} + +// Checks whether the high-pass filter should be done in the full-band. +bool EnforceSplitBandHpf(const FieldTrialsView& field_trials) { + return field_trials.IsEnabled("WebRTC-FullBandHpfKillSwitch"); +} + +// Checks whether AEC3 should be allowed to decide what the default +// configuration should be based on the render and capture channel configuration +// at hand. +bool UseSetupSpecificDefaultAec3Congfig(const FieldTrialsView& field_trials) { + return !field_trials.IsEnabled( + "WebRTC-Aec3SetupSpecificDefaultConfigDefaultsKillSwitch"); +} + +// Identify the native processing rate that best handles a sample rate. +int SuitableProcessRate(int minimum_rate, + int max_splitting_rate, + bool band_splitting_required) { + const int uppermost_native_rate = + band_splitting_required ? max_splitting_rate : 48000; + for (auto rate : {16000, 32000, 48000}) { + if (rate >= uppermost_native_rate) { + return uppermost_native_rate; + } + if (rate >= minimum_rate) { + return rate; + } + } + RTC_DCHECK_NOTREACHED(); + return uppermost_native_rate; +} + +GainControl::Mode Agc1ConfigModeToInterfaceMode( + AudioProcessing::Config::GainController1::Mode mode) { + using Agc1Config = AudioProcessing::Config::GainController1; + switch (mode) { + case Agc1Config::kAdaptiveAnalog: + return GainControl::kAdaptiveAnalog; + case Agc1Config::kAdaptiveDigital: + return GainControl::kAdaptiveDigital; + case Agc1Config::kFixedDigital: + return GainControl::kFixedDigital; + } + RTC_CHECK_NOTREACHED(); +} + +bool MinimizeProcessingForUnusedOutput(const FieldTrialsView& field_trials) { + return !field_trials.IsEnabled("WebRTC-MutedStateKillSwitch"); +} + +// Maximum lengths that frame of samples being passed from the render side to +// the capture side can have (does not apply to AEC3). +static const size_t kMaxAllowedValuesOfSamplesPerBand = 160; +static const size_t kMaxAllowedValuesOfSamplesPerFrame = 480; + +// Maximum number of frames to buffer in the render queue. +// TODO(peah): Decrease this once we properly handle hugely unbalanced +// reverse and forward call numbers. +static const size_t kMaxNumFramesToBuffer = 100; + +void PackRenderAudioBufferForEchoDetector(const AudioBuffer& audio, + std::vector& packed_buffer) { + packed_buffer.clear(); + packed_buffer.insert(packed_buffer.end(), audio.channels_const()[0], + audio.channels_const()[0] + audio.num_frames()); +} + +// Options for gracefully handling processing errors. +enum class FormatErrorOutputOption { + kOutputExactCopyOfInput, + kOutputBroadcastCopyOfFirstInputChannel, + kOutputSilence, + kDoNothing +}; + +enum class AudioFormatValidity { + // Format is supported by APM. + kValidAndSupported, + // Format has a reasonable interpretation but is not supported. + kValidButUnsupportedSampleRate, + // The remaining enums values signal that the audio does not have a reasonable + // interpretation and cannot be used. + kInvalidSampleRate, + kInvalidChannelCount +}; + +AudioFormatValidity ValidateAudioFormat(const StreamConfig& config) { + if (config.sample_rate_hz() < 0) + return AudioFormatValidity::kInvalidSampleRate; + if (config.num_channels() == 0) + return AudioFormatValidity::kInvalidChannelCount; + + // Format has a reasonable interpretation, but may still be unsupported. + if (config.sample_rate_hz() < 8000 || + config.sample_rate_hz() > AudioBuffer::kMaxSampleRate) + return AudioFormatValidity::kValidButUnsupportedSampleRate; + + // Format is fully supported. + return AudioFormatValidity::kValidAndSupported; +} + +int AudioFormatValidityToErrorCode(AudioFormatValidity validity) { + switch (validity) { + case AudioFormatValidity::kValidAndSupported: + return AudioProcessing::kNoError; + case AudioFormatValidity::kValidButUnsupportedSampleRate: // fall-through + case AudioFormatValidity::kInvalidSampleRate: + return AudioProcessing::kBadSampleRateError; + case AudioFormatValidity::kInvalidChannelCount: + return AudioProcessing::kBadNumberChannelsError; + } + RTC_DCHECK(false); +} + +// Returns an AudioProcessing::Error together with the best possible option for +// output audio content. +std::pair ChooseErrorOutputOption( + const StreamConfig& input_config, + const StreamConfig& output_config) { + AudioFormatValidity input_validity = ValidateAudioFormat(input_config); + AudioFormatValidity output_validity = ValidateAudioFormat(output_config); + + if (input_validity == AudioFormatValidity::kValidAndSupported && + output_validity == AudioFormatValidity::kValidAndSupported && + (output_config.num_channels() == 1 || + output_config.num_channels() == input_config.num_channels())) { + return {AudioProcessing::kNoError, FormatErrorOutputOption::kDoNothing}; + } + + int error_code = AudioFormatValidityToErrorCode(input_validity); + if (error_code == AudioProcessing::kNoError) { + error_code = AudioFormatValidityToErrorCode(output_validity); + } + if (error_code == AudioProcessing::kNoError) { + // The individual formats are valid but there is some error - must be + // channel mismatch. + error_code = AudioProcessing::kBadNumberChannelsError; + } + + FormatErrorOutputOption output_option; + if (output_validity != AudioFormatValidity::kValidAndSupported && + output_validity != AudioFormatValidity::kValidButUnsupportedSampleRate) { + // The output format is uninterpretable: cannot do anything. + output_option = FormatErrorOutputOption::kDoNothing; + } else if (input_validity != AudioFormatValidity::kValidAndSupported && + input_validity != + AudioFormatValidity::kValidButUnsupportedSampleRate) { + // The input format is uninterpretable: cannot use it, must output silence. + output_option = FormatErrorOutputOption::kOutputSilence; + } else if (input_config.sample_rate_hz() != output_config.sample_rate_hz()) { + // Sample rates do not match: Cannot copy input into output, output silence. + // Note: If the sample rates are in a supported range, we could resample. + // However, that would significantly increase complexity of this error + // handling code. + output_option = FormatErrorOutputOption::kOutputSilence; + } else if (input_config.num_channels() != output_config.num_channels()) { + // Channel counts do not match: We cannot easily map input channels to + // output channels. + output_option = + FormatErrorOutputOption::kOutputBroadcastCopyOfFirstInputChannel; + } else { + // The formats match exactly. + RTC_DCHECK(input_config == output_config); + output_option = FormatErrorOutputOption::kOutputExactCopyOfInput; + } + return std::make_pair(error_code, output_option); +} + +// Checks if the audio format is supported. If not, the output is populated in a +// best-effort manner and an APM error code is returned. +int HandleUnsupportedAudioFormats(const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest) { + RTC_DCHECK(src); + RTC_DCHECK(dest); + + auto [error_code, output_option] = + ChooseErrorOutputOption(input_config, output_config); + if (error_code == AudioProcessing::kNoError) + return AudioProcessing::kNoError; + + const size_t num_output_channels = output_config.num_channels(); + switch (output_option) { + case FormatErrorOutputOption::kOutputSilence: + memset(dest, 0, output_config.num_samples() * sizeof(int16_t)); + break; + case FormatErrorOutputOption::kOutputBroadcastCopyOfFirstInputChannel: + for (size_t i = 0; i < output_config.num_frames(); ++i) { + int16_t sample = src[input_config.num_channels() * i]; + for (size_t ch = 0; ch < num_output_channels; ++ch) { + dest[ch + num_output_channels * i] = sample; + } + } + break; + case FormatErrorOutputOption::kOutputExactCopyOfInput: + memcpy(dest, src, output_config.num_samples() * sizeof(int16_t)); + break; + case FormatErrorOutputOption::kDoNothing: + break; + } + return error_code; +} + +// Checks if the audio format is supported. If not, the output is populated in a +// best-effort manner and an APM error code is returned. +int HandleUnsupportedAudioFormats(const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config, + float* const* dest) { + RTC_DCHECK(src); + RTC_DCHECK(dest); + for (size_t i = 0; i < input_config.num_channels(); ++i) { + RTC_DCHECK(src[i]); + } + for (size_t i = 0; i < output_config.num_channels(); ++i) { + RTC_DCHECK(dest[i]); + } + + auto [error_code, output_option] = + ChooseErrorOutputOption(input_config, output_config); + if (error_code == AudioProcessing::kNoError) + return AudioProcessing::kNoError; + + const size_t num_output_channels = output_config.num_channels(); + switch (output_option) { + case FormatErrorOutputOption::kOutputSilence: + for (size_t ch = 0; ch < num_output_channels; ++ch) { + memset(dest[ch], 0, output_config.num_frames() * sizeof(float)); + } + break; + case FormatErrorOutputOption::kOutputBroadcastCopyOfFirstInputChannel: + for (size_t ch = 0; ch < num_output_channels; ++ch) { + memcpy(dest[ch], src[0], output_config.num_frames() * sizeof(float)); + } + break; + case FormatErrorOutputOption::kOutputExactCopyOfInput: + for (size_t ch = 0; ch < num_output_channels; ++ch) { + memcpy(dest[ch], src[ch], output_config.num_frames() * sizeof(float)); + } + break; + case FormatErrorOutputOption::kDoNothing: + break; + } + return error_code; +} + +using DownmixMethod = AudioProcessing::Config::Pipeline::DownmixMethod; + +void SetDownmixMethod(AudioBuffer& buffer, DownmixMethod method) { + switch (method) { + case DownmixMethod::kAverageChannels: + buffer.set_downmixing_by_averaging(); + break; + case DownmixMethod::kUseFirstChannel: + buffer.set_downmixing_to_specific_channel(/*channel=*/0); + break; + } +} + +constexpr int kUnspecifiedDataDumpInputVolume = -100; + +} // namespace + +// Throughout webrtc, it's assumed that success is represented by zero. +static_assert(AudioProcessing::kNoError == 0, "kNoError must be zero"); + +AudioProcessingImpl::SubmoduleStates::SubmoduleStates( + bool capture_post_processor_enabled, + bool render_pre_processor_enabled, + bool capture_analyzer_enabled) + : capture_post_processor_enabled_(capture_post_processor_enabled), + render_pre_processor_enabled_(render_pre_processor_enabled), + capture_analyzer_enabled_(capture_analyzer_enabled) {} + +bool AudioProcessingImpl::SubmoduleStates::Update( + bool high_pass_filter_enabled, + bool mobile_echo_controller_enabled, + bool noise_suppressor_enabled, + bool adaptive_gain_controller_enabled, + bool gain_controller2_enabled, + bool gain_adjustment_enabled, + bool echo_controller_enabled) { + bool changed = false; + changed |= (high_pass_filter_enabled != high_pass_filter_enabled_); + changed |= + (mobile_echo_controller_enabled != mobile_echo_controller_enabled_); + changed |= (noise_suppressor_enabled != noise_suppressor_enabled_); + changed |= + (adaptive_gain_controller_enabled != adaptive_gain_controller_enabled_); + changed |= (gain_controller2_enabled != gain_controller2_enabled_); + changed |= (gain_adjustment_enabled != gain_adjustment_enabled_); + changed |= (echo_controller_enabled != echo_controller_enabled_); + if (changed) { + high_pass_filter_enabled_ = high_pass_filter_enabled; + mobile_echo_controller_enabled_ = mobile_echo_controller_enabled; + noise_suppressor_enabled_ = noise_suppressor_enabled; + adaptive_gain_controller_enabled_ = adaptive_gain_controller_enabled; + gain_controller2_enabled_ = gain_controller2_enabled; + gain_adjustment_enabled_ = gain_adjustment_enabled; + echo_controller_enabled_ = echo_controller_enabled; + } + + changed |= first_update_; + first_update_ = false; + return changed; +} + +bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandSubModulesActive() + const { + return CaptureMultiBandProcessingPresent(); +} + +bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandProcessingPresent() + const { + // If echo controller is present, assume it performs active processing. + return CaptureMultiBandProcessingActive(/*ec_processing_active=*/true); +} + +bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandProcessingActive( + bool ec_processing_active) const { + return high_pass_filter_enabled_ || mobile_echo_controller_enabled_ || + noise_suppressor_enabled_ || adaptive_gain_controller_enabled_ || + (echo_controller_enabled_ && ec_processing_active); +} + +bool AudioProcessingImpl::SubmoduleStates::CaptureFullBandProcessingActive() + const { + return gain_controller2_enabled_ || capture_post_processor_enabled_ || + gain_adjustment_enabled_; +} + +bool AudioProcessingImpl::SubmoduleStates::CaptureAnalyzerActive() const { + return capture_analyzer_enabled_; +} + +bool AudioProcessingImpl::SubmoduleStates::RenderMultiBandSubModulesActive() + const { + return RenderMultiBandProcessingActive() || mobile_echo_controller_enabled_ || + adaptive_gain_controller_enabled_ || echo_controller_enabled_; +} + +bool AudioProcessingImpl::SubmoduleStates::RenderFullBandProcessingActive() + const { + return render_pre_processor_enabled_; +} + +bool AudioProcessingImpl::SubmoduleStates::RenderMultiBandProcessingActive() + const { + return false; +} + +bool AudioProcessingImpl::SubmoduleStates::HighPassFilteringRequired() const { + return high_pass_filter_enabled_ || mobile_echo_controller_enabled_ || + noise_suppressor_enabled_; +} + +AudioProcessingImpl::AudioProcessingImpl(const Environment& env) + : AudioProcessingImpl(env, + /*config=*/{}, + /*capture_post_processor=*/nullptr, + /*render_pre_processor=*/nullptr, + /*echo_control_factory=*/nullptr, + /*echo_detector=*/nullptr, + /*capture_analyzer=*/nullptr) {} + +std::atomic AudioProcessingImpl::instance_count_(0); + +AudioProcessingImpl::AudioProcessingImpl( + const Environment& env, + const AudioProcessing::Config& config, + std::unique_ptr capture_post_processor, + std::unique_ptr render_pre_processor, + std::unique_ptr echo_control_factory, + scoped_refptr echo_detector, + std::unique_ptr capture_analyzer) + : env_(env), + data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), + use_setup_specific_default_aec3_config_( + UseSetupSpecificDefaultAec3Congfig(env.field_trials())), + capture_runtime_settings_(RuntimeSettingQueueSize()), + render_runtime_settings_(RuntimeSettingQueueSize()), + capture_runtime_settings_enqueuer_(&capture_runtime_settings_), + render_runtime_settings_enqueuer_(&render_runtime_settings_), + echo_control_factory_(std::move(echo_control_factory)), + config_(config), + submodule_states_(!!capture_post_processor, + !!render_pre_processor, + !!capture_analyzer), + submodules_(std::move(capture_post_processor), + std::move(render_pre_processor), + std::move(echo_detector), + std::move(capture_analyzer)), + constants_(!env.field_trials().IsEnabled( + "WebRTC-ApmExperimentalMultiChannelRenderKillSwitch"), + !env.field_trials().IsEnabled( + "WebRTC-ApmExperimentalMultiChannelCaptureKillSwitch"), + EnforceSplitBandHpf(env.field_trials()), + MinimizeProcessingForUnusedOutput(env.field_trials())), + capture_(), + capture_nonlocked_(), + applied_input_volume_stats_reporter_( + InputVolumeStatsReporter::InputVolumeType::kApplied), + recommended_input_volume_stats_reporter_( + InputVolumeStatsReporter::InputVolumeType::kRecommended) { + RTC_LOG(LS_INFO) << "Injected APM submodules:" + "\nEcho control factory: " + << !!echo_control_factory_ + << "\nEcho detector: " << !!submodules_.echo_detector + << "\nCapture analyzer: " << !!submodules_.capture_analyzer + << "\nCapture post processor: " + << !!submodules_.capture_post_processor + << "\nRender pre processor: " + << !!submodules_.render_pre_processor; + if (!DenormalDisabler::IsSupported()) { + RTC_LOG(LS_INFO) << "Denormal disabler unsupported"; + } + + RTC_LOG(LS_INFO) << "AudioProcessing: " << config_.ToString(); + + // Mark Echo Controller enabled if a factory is injected. + capture_nonlocked_.echo_controller_enabled = + static_cast(echo_control_factory_); + + Initialize(); +} + +AudioProcessingImpl::~AudioProcessingImpl() = default; + +int AudioProcessingImpl::Initialize() { + // Run in a single-threaded manner during initialization. + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + InitializeLocked(); + return kNoError; +} + +int AudioProcessingImpl::Initialize(const ProcessingConfig& processing_config) { + // Run in a single-threaded manner during initialization. + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + InitializeLocked(processing_config); + return kNoError; +} + +void AudioProcessingImpl::MaybeInitializeRender( + const StreamConfig& input_config, + const StreamConfig& output_config) { + ProcessingConfig processing_config = formats_.api_format; + processing_config.reverse_input_stream() = input_config; + processing_config.reverse_output_stream() = output_config; + + if (processing_config == formats_.api_format) { + return; + } + + MutexLock lock_capture(&mutex_capture_); + InitializeLocked(processing_config); +} + +void AudioProcessingImpl::InitializeLocked() { + UpdateActiveSubmoduleStates(); + + const int render_audiobuffer_sample_rate_hz = + formats_.api_format.reverse_output_stream().num_frames() == 0 + ? formats_.render_processing_format.sample_rate_hz() + : formats_.api_format.reverse_output_stream().sample_rate_hz(); + if (formats_.api_format.reverse_input_stream().num_channels() > 0) { + render_.render_audio.reset(new AudioBuffer( + formats_.api_format.reverse_input_stream().sample_rate_hz(), + formats_.api_format.reverse_input_stream().num_channels(), + formats_.render_processing_format.sample_rate_hz(), + formats_.render_processing_format.num_channels(), + render_audiobuffer_sample_rate_hz, + formats_.render_processing_format.num_channels())); + if (formats_.api_format.reverse_input_stream() != + formats_.api_format.reverse_output_stream()) { + render_.render_converter = AudioConverter::Create( + formats_.api_format.reverse_input_stream().num_channels(), + formats_.api_format.reverse_input_stream().num_frames(), + formats_.api_format.reverse_output_stream().num_channels(), + formats_.api_format.reverse_output_stream().num_frames()); + } else { + render_.render_converter.reset(nullptr); + } + } else { + render_.render_audio.reset(nullptr); + render_.render_converter.reset(nullptr); + } + + capture_.capture_audio.reset(new AudioBuffer( + formats_.api_format.input_stream().sample_rate_hz(), + formats_.api_format.input_stream().num_channels(), + capture_nonlocked_.capture_processing_format.sample_rate_hz(), + formats_.api_format.output_stream().num_channels(), + formats_.api_format.output_stream().sample_rate_hz(), + formats_.api_format.output_stream().num_channels())); + SetDownmixMethod(*capture_.capture_audio, + config_.pipeline.capture_downmix_method); + + if (capture_nonlocked_.capture_processing_format.sample_rate_hz() < + formats_.api_format.output_stream().sample_rate_hz() && + formats_.api_format.output_stream().sample_rate_hz() == 48000) { + capture_.capture_fullband_audio.reset( + new AudioBuffer(formats_.api_format.input_stream().sample_rate_hz(), + formats_.api_format.input_stream().num_channels(), + formats_.api_format.output_stream().sample_rate_hz(), + formats_.api_format.output_stream().num_channels(), + formats_.api_format.output_stream().sample_rate_hz(), + formats_.api_format.output_stream().num_channels())); + SetDownmixMethod(*capture_.capture_fullband_audio, + config_.pipeline.capture_downmix_method); + } else { + capture_.capture_fullband_audio.reset(); + } + + AllocateRenderQueue(); + + InitializeGainController1(); + InitializeHighPassFilter(true); + InitializeResidualEchoDetector(); + InitializeEchoController(); + InitializeGainController2(); + InitializeNoiseSuppressor(); + InitializeAnalyzer(); + InitializePostProcessor(); + InitializePreProcessor(); + InitializeCaptureLevelsAdjuster(); + + if (aec_dump_) { + aec_dump_->WriteInitMessage(formats_.api_format, TimeUTCMillis()); + } +} + +void AudioProcessingImpl::InitializeLocked(const ProcessingConfig& config) { + UpdateActiveSubmoduleStates(); + + formats_.api_format = config; + + RTC_LOG(LS_INFO) << "AudioProcessing::InitializeLocked input sampleRate:" + << formats_.api_format.input_stream().sample_rate_hz() + << " output sampleRate:" + << formats_.api_format.output_stream().sample_rate_hz(); + + // Choose maximum rate to use for the split filtering. + RTC_DCHECK(config_.pipeline.maximum_internal_processing_rate == 48000 || + config_.pipeline.maximum_internal_processing_rate == 32000); + int max_splitting_rate = 48000; + if (config_.pipeline.maximum_internal_processing_rate == 32000) { + max_splitting_rate = config_.pipeline.maximum_internal_processing_rate; + } + + int capture_processing_rate = SuitableProcessRate( + std::min(formats_.api_format.input_stream().sample_rate_hz(), + formats_.api_format.output_stream().sample_rate_hz()), + max_splitting_rate, + submodule_states_.CaptureMultiBandSubModulesActive() || + submodule_states_.RenderMultiBandSubModulesActive()); + RTC_DCHECK_NE(8000, capture_processing_rate); + + capture_nonlocked_.capture_processing_format = + StreamConfig(capture_processing_rate); + + int render_processing_rate; + if (!capture_nonlocked_.echo_controller_enabled) { + render_processing_rate = SuitableProcessRate( + std::min(formats_.api_format.reverse_input_stream().sample_rate_hz(), + formats_.api_format.reverse_output_stream().sample_rate_hz()), + max_splitting_rate, + submodule_states_.CaptureMultiBandSubModulesActive() || + submodule_states_.RenderMultiBandSubModulesActive()); + } else { + render_processing_rate = capture_processing_rate; + } + + // If the forward sample rate is 8 kHz, the render stream is also processed + // at this rate. + if (capture_nonlocked_.capture_processing_format.sample_rate_hz() == + kSampleRate8kHz) { + render_processing_rate = kSampleRate8kHz; + } else { + render_processing_rate = + std::max(render_processing_rate, static_cast(kSampleRate16kHz)); + } + + RTC_DCHECK_NE(8000, render_processing_rate); + + if (submodule_states_.RenderMultiBandSubModulesActive()) { + // By default, downmix the render stream to mono for analysis. This has been + // demonstrated to work well for AEC in most practical scenarios. + const bool multi_channel_render = config_.pipeline.multi_channel_render && + constants_.multi_channel_render_support; + int render_processing_num_channels = + multi_channel_render + ? formats_.api_format.reverse_input_stream().num_channels() + : 1; + formats_.render_processing_format = + StreamConfig(render_processing_rate, render_processing_num_channels); + } else { + formats_.render_processing_format = StreamConfig( + formats_.api_format.reverse_input_stream().sample_rate_hz(), + formats_.api_format.reverse_input_stream().num_channels()); + } + + if (capture_nonlocked_.capture_processing_format.sample_rate_hz() == + kSampleRate32kHz || + capture_nonlocked_.capture_processing_format.sample_rate_hz() == + kSampleRate48kHz) { + capture_nonlocked_.split_rate = kSampleRate16kHz; + } else { + capture_nonlocked_.split_rate = + capture_nonlocked_.capture_processing_format.sample_rate_hz(); + } + + InitializeLocked(); +} + +void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) { + // Run in a single-threaded manner when applying the settings. + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + + RTC_LOG(LS_INFO) << "AudioProcessing::ApplyConfig: " << config.ToString(); + + const bool pipeline_config_changed = + config_.pipeline.multi_channel_render != + config.pipeline.multi_channel_render || + config_.pipeline.multi_channel_capture != + config.pipeline.multi_channel_capture || + config_.pipeline.maximum_internal_processing_rate != + config.pipeline.maximum_internal_processing_rate || + config_.pipeline.capture_downmix_method != + config.pipeline.capture_downmix_method; + + const bool aec_config_changed = + config_.echo_canceller.enabled != config.echo_canceller.enabled || + config_.echo_canceller.mobile_mode != config.echo_canceller.mobile_mode; + + const bool agc1_config_changed = + config_.gain_controller1 != config.gain_controller1; + + const bool agc2_config_changed = + config_.gain_controller2 != config.gain_controller2; + + const bool ns_config_changed = + config_.noise_suppression.enabled != config.noise_suppression.enabled || + config_.noise_suppression.level != config.noise_suppression.level; + + const bool pre_amplifier_config_changed = + config_.pre_amplifier.enabled != config.pre_amplifier.enabled || + config_.pre_amplifier.fixed_gain_factor != + config.pre_amplifier.fixed_gain_factor; + + const bool gain_adjustment_config_changed = + config_.capture_level_adjustment != config.capture_level_adjustment; + + config_ = config; + + if (aec_config_changed) { + InitializeEchoController(); + } + + if (ns_config_changed) { + InitializeNoiseSuppressor(); + } + + InitializeHighPassFilter(false); + + if (agc1_config_changed) { + InitializeGainController1(); + } + + const bool config_ok = GainController2::Validate(config_.gain_controller2); + if (!config_ok) { + RTC_LOG(LS_ERROR) + << "Invalid Gain Controller 2 config; using the default config."; + config_.gain_controller2 = AudioProcessing::Config::GainController2(); + } + + if (agc2_config_changed) { + InitializeGainController2(); + } + + if (pre_amplifier_config_changed || gain_adjustment_config_changed) { + InitializeCaptureLevelsAdjuster(); + } + + // Reinitialization must happen after all submodule configuration to avoid + // additional reinitializations on the next capture / render processing call. + if (pipeline_config_changed) { + InitializeLocked(formats_.api_format); + } +} + +int AudioProcessingImpl::proc_sample_rate_hz() const { + // Used as callback from submodules, hence locking is not allowed. + return capture_nonlocked_.capture_processing_format.sample_rate_hz(); +} + +int AudioProcessingImpl::proc_fullband_sample_rate_hz() const { + return capture_.capture_fullband_audio + ? capture_.capture_fullband_audio->num_frames() * 100 + : capture_nonlocked_.capture_processing_format.sample_rate_hz(); +} + +int AudioProcessingImpl::proc_split_sample_rate_hz() const { + // Used as callback from submodules, hence locking is not allowed. + return capture_nonlocked_.split_rate; +} + +size_t AudioProcessingImpl::num_reverse_channels() const { + // Used as callback from submodules, hence locking is not allowed. + return formats_.render_processing_format.num_channels(); +} + +size_t AudioProcessingImpl::num_input_channels() const { + // Used as callback from submodules, hence locking is not allowed. + return formats_.api_format.input_stream().num_channels(); +} + +size_t AudioProcessingImpl::num_proc_channels() const { + // Used as callback from submodules, hence locking is not allowed. + const bool multi_channel_capture = config_.pipeline.multi_channel_capture && + constants_.multi_channel_capture_support; + if (capture_nonlocked_.echo_controller_enabled && !multi_channel_capture) { + return 1; + } + return num_output_channels(); +} + +size_t AudioProcessingImpl::num_output_channels() const { + // Used as callback from submodules, hence locking is not allowed. + return formats_.api_format.output_stream().num_channels(); +} + +bool AudioProcessingImpl::get_output_will_be_muted() { + MutexLock lock(&mutex_capture_); + return !capture_.capture_output_used; +} + +void AudioProcessingImpl::set_output_will_be_muted(bool muted) { + MutexLock lock(&mutex_capture_); + HandleCaptureOutputUsedSetting(!muted); +} + +void AudioProcessingImpl::HandleCaptureOutputUsedSetting( + bool capture_output_used) { + capture_.capture_output_used = + capture_output_used || !constants_.minimize_processing_for_unused_output; + + if (submodules_.agc_manager.get()) { + submodules_.agc_manager->HandleCaptureOutputUsedChange( + capture_.capture_output_used); + } + if (submodules_.echo_controller) { + submodules_.echo_controller->SetCaptureOutputUsage( + capture_.capture_output_used); + } + if (submodules_.noise_suppressor) { + submodules_.noise_suppressor->SetCaptureOutputUsage( + capture_.capture_output_used); + } + if (submodules_.gain_controller2) { + submodules_.gain_controller2->SetCaptureOutputUsed( + capture_.capture_output_used); + } +} + +void AudioProcessingImpl::SetRuntimeSetting(RuntimeSetting setting) { + PostRuntimeSetting(setting); +} + +bool AudioProcessingImpl::PostRuntimeSetting(RuntimeSetting setting) { + switch (setting.type()) { + case RuntimeSetting::Type::kCustomRenderProcessingRuntimeSetting: + case RuntimeSetting::Type::kPlayoutAudioDeviceChange: + return render_runtime_settings_enqueuer_.Enqueue(setting); + case RuntimeSetting::Type::kCapturePreGain: + case RuntimeSetting::Type::kCapturePostGain: + case RuntimeSetting::Type::kCaptureCompressionGain: + case RuntimeSetting::Type::kCaptureFixedPostGain: + case RuntimeSetting::Type::kCaptureOutputUsed: + return capture_runtime_settings_enqueuer_.Enqueue(setting); + case RuntimeSetting::Type::kPlayoutVolumeChange: { + bool enqueueing_successful; + enqueueing_successful = + capture_runtime_settings_enqueuer_.Enqueue(setting); + enqueueing_successful = + render_runtime_settings_enqueuer_.Enqueue(setting) && + enqueueing_successful; + return enqueueing_successful; + } + case RuntimeSetting::Type::kNotSpecified: + RTC_DCHECK_NOTREACHED(); + return true; + } + // The language allows the enum to have a non-enumerator + // value. Check that this doesn't happen. + RTC_DCHECK_NOTREACHED(); + return true; +} + +AudioProcessingImpl::RuntimeSettingEnqueuer::RuntimeSettingEnqueuer( + SwapQueue* runtime_settings) + : runtime_settings_(*runtime_settings) { + RTC_DCHECK(runtime_settings); +} + +AudioProcessingImpl::RuntimeSettingEnqueuer::~RuntimeSettingEnqueuer() = + default; + +bool AudioProcessingImpl::RuntimeSettingEnqueuer::Enqueue( + RuntimeSetting setting) { + const bool successful_insert = runtime_settings_.Insert(&setting); + + if (!successful_insert) { + RTC_LOG(LS_ERROR) << "Cannot enqueue a new runtime setting."; + } + return successful_insert; +} + +void AudioProcessingImpl::MaybeInitializeCapture( + const StreamConfig& input_config, + const StreamConfig& output_config) { + ProcessingConfig processing_config; + bool reinitialization_required = false; + { + // Acquire the capture lock in order to access api_format. The lock is + // released immediately, as we may need to acquire the render lock as part + // of the conditional reinitialization. + MutexLock lock_capture(&mutex_capture_); + processing_config = formats_.api_format; + reinitialization_required = UpdateActiveSubmoduleStates(); + } + + if (processing_config.input_stream() != input_config) { + reinitialization_required = true; + } + + if (processing_config.output_stream() != output_config) { + reinitialization_required = true; + } + + if (reinitialization_required) { + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + // Reread the API format since the render format may have changed. + processing_config = formats_.api_format; + processing_config.input_stream() = input_config; + processing_config.output_stream() = output_config; + InitializeLocked(processing_config); + } +} + +int AudioProcessingImpl::ProcessStream(const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config, + float* const* dest) { + TRACE_EVENT0("webrtc", "AudioProcessing::ProcessStream_StreamConfig"); + DenormalDisabler denormal_disabler; + RETURN_ON_ERR( + HandleUnsupportedAudioFormats(src, input_config, output_config, dest)); + MaybeInitializeCapture(input_config, output_config); + + MutexLock lock_capture(&mutex_capture_); + + if (aec_dump_) { + RecordUnprocessedCaptureStream(src); + } + + capture_.capture_audio->CopyFrom(src, formats_.api_format.input_stream()); + if (capture_.capture_fullband_audio) { + capture_.capture_fullband_audio->CopyFrom( + src, formats_.api_format.input_stream()); + } + RETURN_ON_ERR(ProcessCaptureStreamLocked()); + if (capture_.capture_fullband_audio) { + capture_.capture_fullband_audio->CopyTo(formats_.api_format.output_stream(), + dest); + } else { + capture_.capture_audio->CopyTo(formats_.api_format.output_stream(), dest); + } + + if (aec_dump_) { + RecordProcessedCaptureStream(dest); + } + return kNoError; +} + +void AudioProcessingImpl::HandleCaptureRuntimeSettings() { + RuntimeSetting setting; + int num_settings_processed = 0; + while (capture_runtime_settings_.Remove(&setting)) { + if (aec_dump_) { + aec_dump_->WriteRuntimeSetting(setting); + } + switch (setting.type()) { + case RuntimeSetting::Type::kCapturePreGain: + if (config_.pre_amplifier.enabled || + config_.capture_level_adjustment.enabled) { + float value; + setting.GetFloat(&value); + // If the pre-amplifier is used, apply the new gain to the + // pre-amplifier regardless if the capture level adjustment is + // activated. This approach allows both functionalities to coexist + // until they have been properly merged. + if (config_.pre_amplifier.enabled) { + config_.pre_amplifier.fixed_gain_factor = value; + } else { + config_.capture_level_adjustment.pre_gain_factor = value; + } + + // Use both the pre-amplifier and the capture level adjustment gains + // as pre-gains. + float gain = 1.f; + if (config_.pre_amplifier.enabled) { + gain *= config_.pre_amplifier.fixed_gain_factor; + } + if (config_.capture_level_adjustment.enabled) { + gain *= config_.capture_level_adjustment.pre_gain_factor; + } + + submodules_.capture_levels_adjuster->SetPreGain(gain); + } + // TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump. + break; + case RuntimeSetting::Type::kCapturePostGain: + if (config_.capture_level_adjustment.enabled) { + float value; + setting.GetFloat(&value); + config_.capture_level_adjustment.post_gain_factor = value; + submodules_.capture_levels_adjuster->SetPostGain( + config_.capture_level_adjustment.post_gain_factor); + } + // TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump. + break; + case RuntimeSetting::Type::kCaptureCompressionGain: { + if (!submodules_.agc_manager && + !(submodules_.gain_controller2 && + config_.gain_controller2.input_volume_controller.enabled)) { + float value; + setting.GetFloat(&value); + int int_value = static_cast(value + .5f); + config_.gain_controller1.compression_gain_db = int_value; + if (submodules_.gain_control) { + int error = + submodules_.gain_control->set_compression_gain_db(int_value); + RTC_DCHECK_EQ(kNoError, error); + } + } + break; + } + case RuntimeSetting::Type::kCaptureFixedPostGain: { + if (submodules_.gain_controller2) { + float value; + setting.GetFloat(&value); + config_.gain_controller2.fixed_digital.gain_db = value; + submodules_.gain_controller2->SetFixedGainDb(value); + } + break; + } + case RuntimeSetting::Type::kPlayoutVolumeChange: { + int value; + setting.GetInt(&value); + capture_.playout_volume = value; + break; + } + case RuntimeSetting::Type::kPlayoutAudioDeviceChange: + RTC_DCHECK_NOTREACHED(); + break; + case RuntimeSetting::Type::kCustomRenderProcessingRuntimeSetting: + RTC_DCHECK_NOTREACHED(); + break; + case RuntimeSetting::Type::kNotSpecified: + RTC_DCHECK_NOTREACHED(); + break; + case RuntimeSetting::Type::kCaptureOutputUsed: + bool value; + setting.GetBool(&value); + HandleCaptureOutputUsedSetting(value); + break; + } + ++num_settings_processed; + } + + if (num_settings_processed >= RuntimeSettingQueueSize()) { + // Handle overrun of the runtime settings queue, which likely will has + // caused settings to be discarded. + HandleOverrunInCaptureRuntimeSettingsQueue(); + } +} + +void AudioProcessingImpl::HandleOverrunInCaptureRuntimeSettingsQueue() { + // Fall back to a safe state for the case when a setting for capture output + // usage setting has been missed. + HandleCaptureOutputUsedSetting(/*capture_output_used=*/true); +} + +void AudioProcessingImpl::HandleRenderRuntimeSettings() { + RuntimeSetting setting; + while (render_runtime_settings_.Remove(&setting)) { + if (aec_dump_) { + aec_dump_->WriteRuntimeSetting(setting); + } + switch (setting.type()) { + case RuntimeSetting::Type::kPlayoutAudioDeviceChange: // fall-through + case RuntimeSetting::Type::kPlayoutVolumeChange: // fall-through + case RuntimeSetting::Type::kCustomRenderProcessingRuntimeSetting: + if (submodules_.render_pre_processor) { + submodules_.render_pre_processor->SetRuntimeSetting(setting); + } + break; + case RuntimeSetting::Type::kCapturePreGain: // fall-through + case RuntimeSetting::Type::kCapturePostGain: // fall-through + case RuntimeSetting::Type::kCaptureCompressionGain: // fall-through + case RuntimeSetting::Type::kCaptureFixedPostGain: // fall-through + case RuntimeSetting::Type::kCaptureOutputUsed: // fall-through + case RuntimeSetting::Type::kNotSpecified: + RTC_DCHECK_NOTREACHED(); + break; + } + } +} + +void AudioProcessingImpl::QueueBandedRenderAudio(AudioBuffer* audio) { + RTC_DCHECK_GE(160, audio->num_frames_per_band()); + + if (submodules_.echo_control_mobile) { + EchoControlMobileImpl::PackRenderAudioBuffer(audio, num_output_channels(), + num_reverse_channels(), + &aecm_render_queue_buffer_); + RTC_DCHECK(aecm_render_signal_queue_); + // Insert the samples into the queue. + if (!aecm_render_signal_queue_->Insert(&aecm_render_queue_buffer_)) { + // The data queue is full and needs to be emptied. + EmptyQueuedRenderAudio(); + + // Retry the insert (should always work). + bool result = + aecm_render_signal_queue_->Insert(&aecm_render_queue_buffer_); + RTC_DCHECK(result); + } + } + + if (!submodules_.agc_manager && submodules_.gain_control) { + GainControlImpl::PackRenderAudioBuffer(*audio, &agc_render_queue_buffer_); + // Insert the samples into the queue. + if (!agc_render_signal_queue_->Insert(&agc_render_queue_buffer_)) { + // The data queue is full and needs to be emptied. + EmptyQueuedRenderAudio(); + + // Retry the insert (should always work). + bool result = agc_render_signal_queue_->Insert(&agc_render_queue_buffer_); + RTC_DCHECK(result); + } + } +} + +void AudioProcessingImpl::QueueNonbandedRenderAudio(AudioBuffer* audio) { + if (submodules_.echo_detector) { + PackRenderAudioBufferForEchoDetector(*audio, red_render_queue_buffer_); + RTC_DCHECK(red_render_signal_queue_); + // Insert the samples into the queue. + if (!red_render_signal_queue_->Insert(&red_render_queue_buffer_)) { + // The data queue is full and needs to be emptied. + EmptyQueuedRenderAudio(); + + // Retry the insert (should always work). + bool result = red_render_signal_queue_->Insert(&red_render_queue_buffer_); + RTC_DCHECK(result); + } + } +} + +void AudioProcessingImpl::AllocateRenderQueue() { + const size_t new_agc_render_queue_element_max_size = + std::max(static_cast(1), kMaxAllowedValuesOfSamplesPerBand); + + const size_t new_red_render_queue_element_max_size = + std::max(static_cast(1), kMaxAllowedValuesOfSamplesPerFrame); + + // Reallocate the queues if the queue item sizes are too small to fit the + // data to put in the queues. + + if (agc_render_queue_element_max_size_ < + new_agc_render_queue_element_max_size) { + agc_render_queue_element_max_size_ = new_agc_render_queue_element_max_size; + + std::vector template_queue_element( + agc_render_queue_element_max_size_); + + agc_render_signal_queue_.reset( + new SwapQueue, RenderQueueItemVerifier>( + kMaxNumFramesToBuffer, template_queue_element, + RenderQueueItemVerifier( + agc_render_queue_element_max_size_))); + + agc_render_queue_buffer_.resize(agc_render_queue_element_max_size_); + agc_capture_queue_buffer_.resize(agc_render_queue_element_max_size_); + } else { + agc_render_signal_queue_->Clear(); + } + + if (submodules_.echo_detector) { + if (red_render_queue_element_max_size_ < + new_red_render_queue_element_max_size) { + red_render_queue_element_max_size_ = + new_red_render_queue_element_max_size; + + std::vector template_queue_element( + red_render_queue_element_max_size_); + + red_render_signal_queue_.reset( + new SwapQueue, RenderQueueItemVerifier>( + kMaxNumFramesToBuffer, template_queue_element, + RenderQueueItemVerifier( + red_render_queue_element_max_size_))); + + red_render_queue_buffer_.resize(red_render_queue_element_max_size_); + red_capture_queue_buffer_.resize(red_render_queue_element_max_size_); + } else { + red_render_signal_queue_->Clear(); + } + } +} + +void AudioProcessingImpl::EmptyQueuedRenderAudio() { + MutexLock lock_capture(&mutex_capture_); + EmptyQueuedRenderAudioLocked(); +} + +void AudioProcessingImpl::EmptyQueuedRenderAudioLocked() { + if (submodules_.echo_control_mobile) { + RTC_DCHECK(aecm_render_signal_queue_); + while (aecm_render_signal_queue_->Remove(&aecm_capture_queue_buffer_)) { + submodules_.echo_control_mobile->ProcessRenderAudio( + aecm_capture_queue_buffer_); + } + } + + if (submodules_.gain_control) { + while (agc_render_signal_queue_->Remove(&agc_capture_queue_buffer_)) { + submodules_.gain_control->ProcessRenderAudio(agc_capture_queue_buffer_); + } + } + + if (submodules_.echo_detector) { + while (red_render_signal_queue_->Remove(&red_capture_queue_buffer_)) { + submodules_.echo_detector->AnalyzeRenderAudio(red_capture_queue_buffer_); + } + } +} + +int AudioProcessingImpl::ProcessStream(const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest) { + TRACE_EVENT0("webrtc", "AudioProcessing::ProcessStream_AudioFrame"); + + RETURN_ON_ERR( + HandleUnsupportedAudioFormats(src, input_config, output_config, dest)); + MaybeInitializeCapture(input_config, output_config); + + MutexLock lock_capture(&mutex_capture_); + DenormalDisabler denormal_disabler; + + if (aec_dump_) { + RecordUnprocessedCaptureStream(src, input_config); + } + + capture_.capture_audio->CopyFrom(src, input_config); + if (capture_.capture_fullband_audio) { + capture_.capture_fullband_audio->CopyFrom(src, input_config); + } + RETURN_ON_ERR(ProcessCaptureStreamLocked()); + if (submodule_states_.CaptureMultiBandProcessingPresent() || + submodule_states_.CaptureFullBandProcessingActive()) { + if (capture_.capture_fullband_audio) { + capture_.capture_fullband_audio->CopyTo(output_config, dest); + } else { + capture_.capture_audio->CopyTo(output_config, dest); + } + } + + if (aec_dump_) { + RecordProcessedCaptureStream(dest, output_config); + } + return kNoError; +} + +int AudioProcessingImpl::ProcessCaptureStreamLocked() { + EmptyQueuedRenderAudioLocked(); + HandleCaptureRuntimeSettings(); + DenormalDisabler denormal_disabler; + + // Ensure that not both the AEC and AECM are active at the same time. + // TODO(peah): Simplify once the public API Enable functions for these + // are moved to APM. + RTC_DCHECK_LE( + !!submodules_.echo_controller + !!submodules_.echo_control_mobile, 1); + + data_dumper_->DumpRaw( + "applied_input_volume", + capture_.applied_input_volume.value_or(kUnspecifiedDataDumpInputVolume)); + + AudioBuffer* capture_buffer = capture_.capture_audio.get(); // For brevity. + AudioBuffer* linear_aec_buffer = capture_.linear_aec_output.get(); + + if (submodules_.high_pass_filter && + config_.high_pass_filter.apply_in_full_band && + !constants_.enforce_split_band_hpf) { + submodules_.high_pass_filter->Process(capture_buffer, + /*use_split_band_data=*/false); + } + + if (submodules_.capture_levels_adjuster) { + if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) { + // When the input volume is emulated, retrieve the volume applied to the + // input audio and notify that to APM so that the volume is passed to the + // active AGC. + set_stream_analog_level_locked( + submodules_.capture_levels_adjuster->GetAnalogMicGainLevel()); + } + submodules_.capture_levels_adjuster->ApplyPreLevelAdjustment( + *capture_buffer); + } + + capture_input_rms_.Analyze(ArrayView( + capture_buffer->channels_const()[0], + capture_nonlocked_.capture_processing_format.num_frames())); + const bool log_rms = ++capture_rms_interval_counter_ >= 1000; + if (log_rms) { + capture_rms_interval_counter_ = 0; + RmsLevel::Levels levels = capture_input_rms_.AverageAndPeak(); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.ApmCaptureInputLevelAverageRms", + levels.average, 1, RmsLevel::kMinLevelDb, 64); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.ApmCaptureInputLevelPeakRms", + levels.peak, 1, RmsLevel::kMinLevelDb, 64); + } + + if (capture_.applied_input_volume.has_value()) { + applied_input_volume_stats_reporter_.UpdateStatistics( + *capture_.applied_input_volume); + } + + if (submodules_.echo_controller) { + // Determine if the echo path gain has changed by checking all the gains + // applied before AEC. + capture_.echo_path_gain_change = capture_.applied_input_volume_changed; + + // Detect and flag any change in the capture level adjustment pre-gain. + if (submodules_.capture_levels_adjuster) { + float pre_adjustment_gain = + submodules_.capture_levels_adjuster->GetPreAdjustmentGain(); + capture_.echo_path_gain_change = + capture_.echo_path_gain_change || + (capture_.prev_pre_adjustment_gain != pre_adjustment_gain && + capture_.prev_pre_adjustment_gain >= 0.0f); + capture_.prev_pre_adjustment_gain = pre_adjustment_gain; + } + + // Detect volume change. + capture_.echo_path_gain_change = + capture_.echo_path_gain_change || + (capture_.prev_playout_volume != capture_.playout_volume && + capture_.prev_playout_volume >= 0); + capture_.prev_playout_volume = capture_.playout_volume; + + submodules_.echo_controller->AnalyzeCapture(capture_buffer); + } + + if (submodules_.agc_manager) { + submodules_.agc_manager->AnalyzePreProcess(*capture_buffer); + } + + if (submodules_.gain_controller2 && + config_.gain_controller2.input_volume_controller.enabled) { + // Expect the volume to be available if the input controller is enabled. + RTC_DCHECK(capture_.applied_input_volume.has_value()); + if (capture_.applied_input_volume.has_value()) { + submodules_.gain_controller2->Analyze(*capture_.applied_input_volume, + *capture_buffer); + } + } + + if (submodule_states_.CaptureMultiBandSubModulesActive() && + SampleRateSupportsMultiBand( + capture_nonlocked_.capture_processing_format.sample_rate_hz())) { + capture_buffer->SplitIntoFrequencyBands(); + } + + const bool multi_channel_capture = config_.pipeline.multi_channel_capture && + constants_.multi_channel_capture_support; + if (submodules_.echo_controller && !multi_channel_capture) { + // Force down-mixing of the number of channels after the detection of + // capture signal saturation. + // TODO(peah): Look into ensuring that this kind of tampering with the + // AudioBuffer functionality should not be needed. + capture_buffer->set_num_channels(1); + } + + if (submodules_.high_pass_filter && + (!config_.high_pass_filter.apply_in_full_band || + constants_.enforce_split_band_hpf)) { + submodules_.high_pass_filter->Process(capture_buffer, + /*use_split_band_data=*/true); + } + + if (submodules_.gain_control) { + RETURN_ON_ERR( + submodules_.gain_control->AnalyzeCaptureAudio(*capture_buffer)); + } + + if ((!config_.noise_suppression.analyze_linear_aec_output_when_available || + !linear_aec_buffer || submodules_.echo_control_mobile) && + submodules_.noise_suppressor) { + submodules_.noise_suppressor->Analyze(*capture_buffer); + } + + if (submodules_.echo_control_mobile) { + // Ensure that the stream delay was set before the call to the + // AECM ProcessCaptureAudio function. + if (!capture_.was_stream_delay_set) { + return AudioProcessing::kStreamParameterNotSetError; + } + + if (submodules_.noise_suppressor) { + submodules_.noise_suppressor->Process(capture_buffer); + } + + RETURN_ON_ERR(submodules_.echo_control_mobile->ProcessCaptureAudio( + capture_buffer, stream_delay_ms())); + } else { + if (submodules_.echo_controller) { + data_dumper_->DumpRaw("stream_delay", stream_delay_ms()); + + if (capture_.was_stream_delay_set) { + submodules_.echo_controller->SetAudioBufferDelay(stream_delay_ms()); + } + + submodules_.echo_controller->ProcessCapture( + capture_buffer, linear_aec_buffer, capture_.echo_path_gain_change); + } + + if (config_.noise_suppression.analyze_linear_aec_output_when_available && + linear_aec_buffer && submodules_.noise_suppressor) { + submodules_.noise_suppressor->Analyze(*linear_aec_buffer); + } + + if (submodules_.noise_suppressor) { + submodules_.noise_suppressor->Process(capture_buffer); + } + } + + if (submodules_.agc_manager) { + submodules_.agc_manager->Process(*capture_buffer); + + std::optional new_digital_gain = + submodules_.agc_manager->GetDigitalComressionGain(); + if (new_digital_gain && submodules_.gain_control) { + submodules_.gain_control->set_compression_gain_db(*new_digital_gain); + } + } + + if (submodules_.gain_control) { + // TODO(peah): Add reporting from AEC3 whether there is echo. + RETURN_ON_ERR(submodules_.gain_control->ProcessCaptureAudio( + capture_buffer, /*stream_has_echo*/ false)); + } + + if (submodule_states_.CaptureMultiBandProcessingPresent() && + SampleRateSupportsMultiBand( + capture_nonlocked_.capture_processing_format.sample_rate_hz())) { + capture_buffer->MergeFrequencyBands(); + } + + if (capture_.capture_output_used) { + if (capture_.capture_fullband_audio) { + const auto& ec = submodules_.echo_controller; + bool ec_active = ec ? ec->ActiveProcessing() : false; + // Only update the fullband buffer if the multiband processing has changed + // the signal. Keep the original signal otherwise. + if (submodule_states_.CaptureMultiBandProcessingActive(ec_active)) { + capture_buffer->CopyTo(capture_.capture_fullband_audio.get()); + } + capture_buffer = capture_.capture_fullband_audio.get(); + } + + if (submodules_.echo_detector) { + submodules_.echo_detector->AnalyzeCaptureAudio(ArrayView( + capture_buffer->channels()[0], capture_buffer->num_frames())); + } + + // Experimental APM sub-module that analyzes `capture_buffer`. + if (submodules_.capture_analyzer) { + submodules_.capture_analyzer->Analyze(capture_buffer); + } + + if (submodules_.gain_controller2) { + // TODO(bugs.webrtc.org/7494): Let AGC2 detect applied input volume + // changes. + submodules_.gain_controller2->Process( + /*speech_probability=*/std::nullopt, + capture_.applied_input_volume_changed, capture_buffer); + } + + if (submodules_.capture_post_processor) { + submodules_.capture_post_processor->Process(capture_buffer); + } + + capture_output_rms_.Analyze(ArrayView( + capture_buffer->channels_const()[0], + capture_nonlocked_.capture_processing_format.num_frames())); + if (log_rms) { + RmsLevel::Levels levels = capture_output_rms_.AverageAndPeak(); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.ApmCaptureOutputLevelAverageRms", levels.average, 1, + RmsLevel::kMinLevelDb, 64); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.ApmCaptureOutputLevelPeakRms", + levels.peak, 1, RmsLevel::kMinLevelDb, 64); + } + + // Compute echo-detector stats. + if (submodules_.echo_detector) { + auto ed_metrics = submodules_.echo_detector->GetMetrics(); + capture_.stats.residual_echo_likelihood = ed_metrics.echo_likelihood; + capture_.stats.residual_echo_likelihood_recent_max = + ed_metrics.echo_likelihood_recent_max; + } + } + + // Compute echo-controller stats. + if (submodules_.echo_controller) { + auto ec_metrics = submodules_.echo_controller->GetMetrics(); + capture_.stats.echo_return_loss = ec_metrics.echo_return_loss; + capture_.stats.echo_return_loss_enhancement = + ec_metrics.echo_return_loss_enhancement; + capture_.stats.delay_ms = ec_metrics.delay_ms; + } + + // Pass stats for reporting. + stats_reporter_.UpdateStatistics(capture_.stats); + + UpdateRecommendedInputVolumeLocked(); + if (capture_.recommended_input_volume.has_value()) { + recommended_input_volume_stats_reporter_.UpdateStatistics( + *capture_.recommended_input_volume); + } + + if (submodules_.capture_levels_adjuster) { + submodules_.capture_levels_adjuster->ApplyPostLevelAdjustment( + *capture_buffer); + + if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) { + // If the input volume emulation is used, retrieve the recommended input + // volume and set that to emulate the input volume on the next processed + // audio frame. + RTC_DCHECK(capture_.recommended_input_volume.has_value()); + submodules_.capture_levels_adjuster->SetAnalogMicGainLevel( + *capture_.recommended_input_volume); + } + } + + // Temporarily set the output to zero after the stream has been unmuted + // (capture output is again used). The purpose of this is to avoid clicks and + // artefacts in the audio that results when the processing again is + // reactivated after unmuting. + if (!capture_.capture_output_used_last_frame && + capture_.capture_output_used) { + for (size_t ch = 0; ch < capture_buffer->num_channels(); ++ch) { + ArrayView channel_view(capture_buffer->channels()[ch], + capture_buffer->num_frames()); + std::fill(channel_view.begin(), channel_view.end(), 0.f); + } + } + capture_.capture_output_used_last_frame = capture_.capture_output_used; + + capture_.was_stream_delay_set = false; + + data_dumper_->DumpRaw("recommended_input_volume", + capture_.recommended_input_volume.value_or( + kUnspecifiedDataDumpInputVolume)); + + return kNoError; +} + +int AudioProcessingImpl::AnalyzeReverseStream( + const float* const* data, + const StreamConfig& reverse_config) { + TRACE_EVENT0("webrtc", "AudioProcessing::AnalyzeReverseStream_StreamConfig"); + MutexLock lock(&mutex_render_); + DenormalDisabler denormal_disabler; + RTC_DCHECK(data); + for (size_t i = 0; i < reverse_config.num_channels(); ++i) { + RTC_DCHECK(data[i]); + } + RETURN_ON_ERR( + AudioFormatValidityToErrorCode(ValidateAudioFormat(reverse_config))); + + MaybeInitializeRender(reverse_config, reverse_config); + return AnalyzeReverseStreamLocked(data, reverse_config, reverse_config); +} + +int AudioProcessingImpl::ProcessReverseStream(const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config, + float* const* dest) { + TRACE_EVENT0("webrtc", "AudioProcessing::ProcessReverseStream_StreamConfig"); + MutexLock lock(&mutex_render_); + DenormalDisabler denormal_disabler; + RETURN_ON_ERR( + HandleUnsupportedAudioFormats(src, input_config, output_config, dest)); + + MaybeInitializeRender(input_config, output_config); + + RETURN_ON_ERR(AnalyzeReverseStreamLocked(src, input_config, output_config)); + + if (submodule_states_.RenderMultiBandProcessingActive() || + submodule_states_.RenderFullBandProcessingActive()) { + render_.render_audio->CopyTo(formats_.api_format.reverse_output_stream(), + dest); + } else if (formats_.api_format.reverse_input_stream() != + formats_.api_format.reverse_output_stream()) { + render_.render_converter->Convert(src, input_config.num_samples(), dest, + output_config.num_samples()); + } else { + CopyAudioIfNeeded(src, input_config.num_frames(), + input_config.num_channels(), dest); + } + + return kNoError; +} + +int AudioProcessingImpl::AnalyzeReverseStreamLocked( + const float* const* src, + const StreamConfig& /* input_config */, + const StreamConfig& /* output_config */) { + if (aec_dump_) { + const size_t channel_size = + formats_.api_format.reverse_input_stream().num_frames(); + const size_t num_channels = + formats_.api_format.reverse_input_stream().num_channels(); + aec_dump_->WriteRenderStreamMessage( + AudioFrameView(src, num_channels, channel_size)); + } + render_.render_audio->CopyFrom(src, + formats_.api_format.reverse_input_stream()); + return ProcessRenderStreamLocked(); +} + +int AudioProcessingImpl::ProcessReverseStream(const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest) { + TRACE_EVENT0("webrtc", "AudioProcessing::ProcessReverseStream_AudioFrame"); + + MutexLock lock(&mutex_render_); + DenormalDisabler denormal_disabler; + + RETURN_ON_ERR( + HandleUnsupportedAudioFormats(src, input_config, output_config, dest)); + MaybeInitializeRender(input_config, output_config); + + if (aec_dump_) { + aec_dump_->WriteRenderStreamMessage(src, input_config.num_frames(), + input_config.num_channels()); + } + + render_.render_audio->CopyFrom(src, input_config); + RETURN_ON_ERR(ProcessRenderStreamLocked()); + if (submodule_states_.RenderMultiBandProcessingActive() || + submodule_states_.RenderFullBandProcessingActive()) { + render_.render_audio->CopyTo(output_config, dest); + } + return kNoError; +} + +int AudioProcessingImpl::ProcessRenderStreamLocked() { + AudioBuffer* render_buffer = render_.render_audio.get(); // For brevity. + + HandleRenderRuntimeSettings(); + DenormalDisabler denormal_disabler; + + if (submodules_.render_pre_processor) { + submodules_.render_pre_processor->Process(render_buffer); + } + + QueueNonbandedRenderAudio(render_buffer); + + if (submodule_states_.RenderMultiBandSubModulesActive() && + SampleRateSupportsMultiBand( + formats_.render_processing_format.sample_rate_hz())) { + render_buffer->SplitIntoFrequencyBands(); + } + + if (submodule_states_.RenderMultiBandSubModulesActive()) { + QueueBandedRenderAudio(render_buffer); + } + + // TODO(peah): Perform the queuing inside QueueRenderAudiuo(). + if (submodules_.echo_controller) { + submodules_.echo_controller->AnalyzeRender(render_buffer); + } + + if (submodule_states_.RenderMultiBandProcessingActive() && + SampleRateSupportsMultiBand( + formats_.render_processing_format.sample_rate_hz())) { + render_buffer->MergeFrequencyBands(); + } + + return kNoError; +} + +int AudioProcessingImpl::set_stream_delay_ms(int delay) { + MutexLock lock(&mutex_capture_); + Error retval = kNoError; + capture_.was_stream_delay_set = true; + + if (delay < 0) { + delay = 0; + retval = kBadStreamParameterWarning; + } + + // TODO(ajm): the max is rather arbitrarily chosen; investigate. + if (delay > 500) { + delay = 500; + retval = kBadStreamParameterWarning; + } + + capture_nonlocked_.stream_delay_ms = delay; + return retval; +} + +bool AudioProcessingImpl::GetLinearAecOutput( + ArrayView> linear_output) const { + MutexLock lock(&mutex_capture_); + AudioBuffer* linear_aec_buffer = capture_.linear_aec_output.get(); + + RTC_DCHECK(linear_aec_buffer); + if (linear_aec_buffer) { + RTC_DCHECK_EQ(1, linear_aec_buffer->num_bands()); + RTC_DCHECK_EQ(linear_output.size(), linear_aec_buffer->num_channels()); + + for (size_t ch = 0; ch < linear_aec_buffer->num_channels(); ++ch) { + RTC_DCHECK_EQ(linear_output[ch].size(), linear_aec_buffer->num_frames()); + ArrayView channel_view = + ArrayView(linear_aec_buffer->channels_const()[ch], + linear_aec_buffer->num_frames()); + FloatS16ToFloat(channel_view.data(), channel_view.size(), + linear_output[ch].data()); + } + return true; + } + RTC_LOG(LS_ERROR) << "No linear AEC output available"; + RTC_DCHECK_NOTREACHED(); + return false; +} + +int AudioProcessingImpl::stream_delay_ms() const { + // Used as callback from submodules, hence locking is not allowed. + return capture_nonlocked_.stream_delay_ms; +} + +void AudioProcessingImpl::set_stream_key_pressed(bool key_pressed) { + MutexLock lock(&mutex_capture_); + capture_.key_pressed = key_pressed; +} + +void AudioProcessingImpl::set_stream_analog_level(int level) { + MutexLock lock_capture(&mutex_capture_); + set_stream_analog_level_locked(level); +} + +void AudioProcessingImpl::set_stream_analog_level_locked(int level) { + capture_.applied_input_volume_changed = + capture_.applied_input_volume.has_value() && + *capture_.applied_input_volume != level; + capture_.applied_input_volume = level; + + // Invalidate any previously recommended input volume which will be updated by + // `ProcessStream()`. + capture_.recommended_input_volume = std::nullopt; + + if (submodules_.agc_manager) { + submodules_.agc_manager->set_stream_analog_level(level); + return; + } + + if (submodules_.gain_control) { + int error = submodules_.gain_control->set_stream_analog_level(level); + RTC_DCHECK_EQ(kNoError, error); + return; + } +} + +int AudioProcessingImpl::recommended_stream_analog_level() const { + MutexLock lock_capture(&mutex_capture_); + if (!capture_.applied_input_volume.has_value()) { + RTC_LOG(LS_ERROR) << "set_stream_analog_level has not been called"; + } + // Input volume to recommend when `set_stream_analog_level()` is not called. + constexpr int kFallBackInputVolume = 255; + // When APM has no input volume to recommend, return the latest applied input + // volume that has been observed in order to possibly produce no input volume + // change. If no applied input volume has been observed, return a fall-back + // value. + return capture_.recommended_input_volume.value_or( + capture_.applied_input_volume.value_or(kFallBackInputVolume)); +} + +void AudioProcessingImpl::UpdateRecommendedInputVolumeLocked() { + if (!capture_.applied_input_volume.has_value()) { + // When `set_stream_analog_level()` is not called, no input level can be + // recommended. + capture_.recommended_input_volume = std::nullopt; + return; + } + + if (submodules_.agc_manager) { + capture_.recommended_input_volume = + submodules_.agc_manager->recommended_analog_level(); + return; + } + + if (submodules_.gain_control) { + capture_.recommended_input_volume = + submodules_.gain_control->stream_analog_level(); + return; + } + + if (submodules_.gain_controller2 && + config_.gain_controller2.input_volume_controller.enabled) { + capture_.recommended_input_volume = + submodules_.gain_controller2->recommended_input_volume(); + return; + } + + capture_.recommended_input_volume = capture_.applied_input_volume; +} + +bool AudioProcessingImpl::CreateAndAttachAecDump(absl::string_view file_name, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull + worker_queue) { + std::unique_ptr aec_dump = + AecDumpFactory::Create(file_name, max_log_size_bytes, worker_queue); + if (!aec_dump) { + return false; + } + + AttachAecDump(std::move(aec_dump)); + return true; +} + +bool AudioProcessingImpl::CreateAndAttachAecDump(FILE* handle, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull + worker_queue) { + std::unique_ptr aec_dump = + AecDumpFactory::Create(handle, max_log_size_bytes, worker_queue); + if (!aec_dump) { + return false; + } + + AttachAecDump(std::move(aec_dump)); + return true; +} + +void AudioProcessingImpl::AttachAecDump(std::unique_ptr aec_dump) { + RTC_DCHECK(aec_dump); + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + + // The previously attached AecDump will be destroyed with the + // 'aec_dump' parameter, which is after locks are released. + aec_dump_.swap(aec_dump); + WriteAecDumpConfigMessage(true); + aec_dump_->WriteInitMessage(formats_.api_format, TimeUTCMillis()); +} + +void AudioProcessingImpl::DetachAecDump() { + // The d-tor of a task-queue based AecDump blocks until all pending + // tasks are done. This construction avoids blocking while holding + // the render and capture locks. + std::unique_ptr aec_dump = nullptr; + { + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + aec_dump = std::move(aec_dump_); + } +} + +AudioProcessing::Config AudioProcessingImpl::GetConfig() const { + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + return config_; +} + +bool AudioProcessingImpl::UpdateActiveSubmoduleStates() { + return submodule_states_.Update( + config_.high_pass_filter.enabled, !!submodules_.echo_control_mobile, + !!submodules_.noise_suppressor, !!submodules_.gain_control, + !!submodules_.gain_controller2, + config_.pre_amplifier.enabled || config_.capture_level_adjustment.enabled, + capture_nonlocked_.echo_controller_enabled); +} + +void AudioProcessingImpl::InitializeHighPassFilter(bool forced_reset) { + bool high_pass_filter_needed_by_aec = + config_.echo_canceller.enabled && + config_.echo_canceller.enforce_high_pass_filtering && + !config_.echo_canceller.mobile_mode; + if (submodule_states_.HighPassFilteringRequired() || + high_pass_filter_needed_by_aec) { + bool use_full_band = config_.high_pass_filter.apply_in_full_band && + !constants_.enforce_split_band_hpf; + int rate = use_full_band ? proc_fullband_sample_rate_hz() + : proc_split_sample_rate_hz(); + size_t num_channels = + use_full_band ? num_output_channels() : num_proc_channels(); + + if (!submodules_.high_pass_filter || + rate != submodules_.high_pass_filter->sample_rate_hz() || + forced_reset || + num_channels != submodules_.high_pass_filter->num_channels()) { + submodules_.high_pass_filter.reset( + new HighPassFilter(rate, num_channels)); + } + } else { + submodules_.high_pass_filter.reset(); + } +} + +void AudioProcessingImpl::InitializeEchoController() { + bool use_echo_controller = + echo_control_factory_ || + (config_.echo_canceller.enabled && !config_.echo_canceller.mobile_mode); + + if (use_echo_controller) { + // Create and activate the echo controller. + if (echo_control_factory_) { + submodules_.echo_controller = echo_control_factory_->Create( + env_, proc_sample_rate_hz(), num_reverse_channels(), + num_proc_channels()); + RTC_DCHECK(submodules_.echo_controller); + } else { + EchoCanceller3Config config; + std::optional multichannel_config; + if (use_setup_specific_default_aec3_config_) { + multichannel_config = + EchoCanceller3Config::CreateDefaultMultichannelConfig(); + } + submodules_.echo_controller = std::make_unique( + env_, config, multichannel_config, proc_sample_rate_hz(), + num_reverse_channels(), num_proc_channels()); + } + + // Setup the storage for returning the linear AEC output. + if (config_.echo_canceller.export_linear_aec_output) { + constexpr int kLinearOutputRateHz = 16000; + capture_.linear_aec_output = std::make_unique( + kLinearOutputRateHz, num_proc_channels(), kLinearOutputRateHz, + num_proc_channels(), kLinearOutputRateHz, num_proc_channels()); + } else { + capture_.linear_aec_output.reset(); + } + + capture_nonlocked_.echo_controller_enabled = true; + + submodules_.echo_control_mobile.reset(); + aecm_render_signal_queue_.reset(); + return; + } + + submodules_.echo_controller.reset(); + capture_nonlocked_.echo_controller_enabled = false; + capture_.linear_aec_output.reset(); + + if (!config_.echo_canceller.enabled) { + submodules_.echo_control_mobile.reset(); + aecm_render_signal_queue_.reset(); + return; + } + + if (config_.echo_canceller.mobile_mode) { + // Create and activate AECM. + size_t max_element_size = + std::max(static_cast(1), + kMaxAllowedValuesOfSamplesPerBand * + EchoControlMobileImpl::NumCancellersRequired( + num_output_channels(), num_reverse_channels())); + + std::vector template_queue_element(max_element_size); + + aecm_render_signal_queue_.reset( + new SwapQueue, RenderQueueItemVerifier>( + kMaxNumFramesToBuffer, template_queue_element, + RenderQueueItemVerifier(max_element_size))); + + aecm_render_queue_buffer_.resize(max_element_size); + aecm_capture_queue_buffer_.resize(max_element_size); + + submodules_.echo_control_mobile.reset(new EchoControlMobileImpl()); + + submodules_.echo_control_mobile->Initialize(proc_split_sample_rate_hz(), + num_reverse_channels(), + num_output_channels()); + return; + } + + submodules_.echo_control_mobile.reset(); + aecm_render_signal_queue_.reset(); +} + +void AudioProcessingImpl::InitializeGainController1() { + if (config_.gain_controller2.enabled && + config_.gain_controller2.input_volume_controller.enabled && + config_.gain_controller1.enabled && + (config_.gain_controller1.mode == + AudioProcessing::Config::GainController1::kAdaptiveAnalog || + config_.gain_controller1.analog_gain_controller.enabled)) { + RTC_LOG(LS_ERROR) << "APM configuration not valid: " + << "Multiple input volume controllers enabled."; + } + + if (!config_.gain_controller1.enabled) { + submodules_.agc_manager.reset(); + submodules_.gain_control.reset(); + return; + } + + RTC_HISTOGRAM_BOOLEAN( + "WebRTC.Audio.GainController.Analog.Enabled", + config_.gain_controller1.analog_gain_controller.enabled); + + if (!submodules_.gain_control) { + submodules_.gain_control.reset(new GainControlImpl()); + } + + submodules_.gain_control->Initialize(num_proc_channels(), + proc_sample_rate_hz()); + if (!config_.gain_controller1.analog_gain_controller.enabled) { + int error = submodules_.gain_control->set_mode( + Agc1ConfigModeToInterfaceMode(config_.gain_controller1.mode)); + RTC_DCHECK_EQ(kNoError, error); + error = submodules_.gain_control->set_target_level_dbfs( + config_.gain_controller1.target_level_dbfs); + RTC_DCHECK_EQ(kNoError, error); + error = submodules_.gain_control->set_compression_gain_db( + config_.gain_controller1.compression_gain_db); + RTC_DCHECK_EQ(kNoError, error); + error = submodules_.gain_control->enable_limiter( + config_.gain_controller1.enable_limiter); + RTC_DCHECK_EQ(kNoError, error); + constexpr int kAnalogLevelMinimum = 0; + constexpr int kAnalogLevelMaximum = 255; + error = submodules_.gain_control->set_analog_level_limits( + kAnalogLevelMinimum, kAnalogLevelMaximum); + RTC_DCHECK_EQ(kNoError, error); + + submodules_.agc_manager.reset(); + return; + } + + if (!submodules_.agc_manager.get() || + submodules_.agc_manager->num_channels() != + static_cast(num_proc_channels())) { + int stream_analog_level = -1; + const bool re_creation = !!submodules_.agc_manager; + if (re_creation) { + stream_analog_level = submodules_.agc_manager->recommended_analog_level(); + } + submodules_.agc_manager = std::make_unique( + env_, num_proc_channels(), + config_.gain_controller1.analog_gain_controller); + if (re_creation) { + submodules_.agc_manager->set_stream_analog_level(stream_analog_level); + } + } + submodules_.agc_manager->Initialize(); + submodules_.agc_manager->SetupDigitalGainControl(*submodules_.gain_control); + submodules_.agc_manager->HandleCaptureOutputUsedChange( + capture_.capture_output_used); +} + +void AudioProcessingImpl::InitializeGainController2() { + if (!config_.gain_controller2.enabled) { + submodules_.gain_controller2.reset(); + return; + } + // Input volume controller configuration if the AGC2 is running + // and its parameters require to fully switch the gain control to + // AGC2. + const InputVolumeController::Config input_volume_controller_config = + InputVolumeController::Config{}; + submodules_.gain_controller2 = std::make_unique( + env_, config_.gain_controller2, input_volume_controller_config, + proc_fullband_sample_rate_hz(), num_output_channels(), + /*use_internal_vad=*/true); + submodules_.gain_controller2->SetCaptureOutputUsed( + capture_.capture_output_used); +} + +void AudioProcessingImpl::InitializeNoiseSuppressor() { + submodules_.noise_suppressor.reset(); + + if (config_.noise_suppression.enabled) { + auto map_level = + [](AudioProcessing::Config::NoiseSuppression::Level level) { + using NoiseSuppresionConfig = + AudioProcessing::Config::NoiseSuppression; + switch (level) { + case NoiseSuppresionConfig::kLow: + return NsConfig::SuppressionLevel::k6dB; + case NoiseSuppresionConfig::kModerate: + return NsConfig::SuppressionLevel::k12dB; + case NoiseSuppresionConfig::kHigh: + return NsConfig::SuppressionLevel::k18dB; + case NoiseSuppresionConfig::kVeryHigh: + return NsConfig::SuppressionLevel::k21dB; + } + RTC_CHECK_NOTREACHED(); + }; + + NsConfig cfg; + cfg.target_level = map_level(config_.noise_suppression.level); + submodules_.noise_suppressor = std::make_unique( + cfg, proc_sample_rate_hz(), num_proc_channels()); + } +} + +void AudioProcessingImpl::InitializeCaptureLevelsAdjuster() { + if (config_.pre_amplifier.enabled || + config_.capture_level_adjustment.enabled) { + // Use both the pre-amplifier and the capture level adjustment gains as + // pre-gains. + float pre_gain = 1.f; + if (config_.pre_amplifier.enabled) { + pre_gain *= config_.pre_amplifier.fixed_gain_factor; + } + if (config_.capture_level_adjustment.enabled) { + pre_gain *= config_.capture_level_adjustment.pre_gain_factor; + } + + submodules_.capture_levels_adjuster = + std::make_unique( + config_.capture_level_adjustment.analog_mic_gain_emulation.enabled, + config_.capture_level_adjustment.analog_mic_gain_emulation + .initial_level, + pre_gain, config_.capture_level_adjustment.post_gain_factor); + } else { + submodules_.capture_levels_adjuster.reset(); + } +} + +void AudioProcessingImpl::InitializeResidualEchoDetector() { + if (submodules_.echo_detector) { + submodules_.echo_detector->Initialize( + proc_fullband_sample_rate_hz(), 1, + formats_.render_processing_format.sample_rate_hz(), 1); + } +} + +void AudioProcessingImpl::InitializeAnalyzer() { + if (submodules_.capture_analyzer) { + submodules_.capture_analyzer->Initialize(proc_fullband_sample_rate_hz(), + num_proc_channels()); + } +} + +void AudioProcessingImpl::InitializePostProcessor() { + if (submodules_.capture_post_processor) { + submodules_.capture_post_processor->Initialize( + proc_fullband_sample_rate_hz(), num_proc_channels()); + } +} + +void AudioProcessingImpl::InitializePreProcessor() { + if (submodules_.render_pre_processor) { + submodules_.render_pre_processor->Initialize( + formats_.render_processing_format.sample_rate_hz(), + formats_.render_processing_format.num_channels()); + } +} + +void AudioProcessingImpl::WriteAecDumpConfigMessage(bool forced) { + if (!aec_dump_) { + return; + } + + std::string experiments_description = ""; + // TODO(peah): Add semicolon-separated concatenations of experiment + // descriptions for other submodules. + if (!!submodules_.capture_post_processor) { + experiments_description += "CapturePostProcessor;"; + } + if (!!submodules_.render_pre_processor) { + experiments_description += "RenderPreProcessor;"; + } + if (capture_nonlocked_.echo_controller_enabled) { + experiments_description += "EchoController;"; + } + if (config_.gain_controller2.enabled) { + experiments_description += "GainController2;"; + } + + InternalAPMConfig apm_config; + + apm_config.aec_enabled = config_.echo_canceller.enabled; + apm_config.aec_delay_agnostic_enabled = false; + apm_config.aec_extended_filter_enabled = false; + apm_config.aec_suppression_level = 0; + + apm_config.aecm_enabled = !!submodules_.echo_control_mobile; + apm_config.aecm_comfort_noise_enabled = + submodules_.echo_control_mobile && + submodules_.echo_control_mobile->is_comfort_noise_enabled(); + apm_config.aecm_routing_mode = + submodules_.echo_control_mobile + ? static_cast(submodules_.echo_control_mobile->routing_mode()) + : 0; + + apm_config.agc_enabled = !!submodules_.gain_control; + + apm_config.agc_mode = submodules_.gain_control + ? static_cast(submodules_.gain_control->mode()) + : GainControl::kAdaptiveAnalog; + apm_config.agc_limiter_enabled = + submodules_.gain_control ? submodules_.gain_control->is_limiter_enabled() + : false; + apm_config.noise_robust_agc_enabled = !!submodules_.agc_manager; + + apm_config.hpf_enabled = config_.high_pass_filter.enabled; + + apm_config.ns_enabled = config_.noise_suppression.enabled; + apm_config.ns_level = static_cast(config_.noise_suppression.level); + + apm_config.experiments_description = experiments_description; + apm_config.pre_amplifier_enabled = config_.pre_amplifier.enabled; + apm_config.pre_amplifier_fixed_gain_factor = + config_.pre_amplifier.fixed_gain_factor; + + if (!forced && apm_config == apm_config_for_aec_dump_) { + return; + } + aec_dump_->WriteConfig(apm_config); + apm_config_for_aec_dump_ = apm_config; +} + +void AudioProcessingImpl::RecordUnprocessedCaptureStream( + const float* const* src) { + RTC_DCHECK(aec_dump_); + WriteAecDumpConfigMessage(false); + + const size_t channel_size = formats_.api_format.input_stream().num_frames(); + const size_t num_channels = formats_.api_format.input_stream().num_channels(); + aec_dump_->AddCaptureStreamInput( + AudioFrameView(src, num_channels, channel_size)); + RecordAudioProcessingState(); +} + +void AudioProcessingImpl::RecordUnprocessedCaptureStream( + const int16_t* const data, + const StreamConfig& config) { + RTC_DCHECK(aec_dump_); + WriteAecDumpConfigMessage(false); + + aec_dump_->AddCaptureStreamInput(data, config.num_channels(), + config.num_frames()); + RecordAudioProcessingState(); +} + +void AudioProcessingImpl::RecordProcessedCaptureStream( + const float* const* processed_capture_stream) { + RTC_DCHECK(aec_dump_); + + const size_t channel_size = formats_.api_format.output_stream().num_frames(); + const size_t num_channels = + formats_.api_format.output_stream().num_channels(); + aec_dump_->AddCaptureStreamOutput(AudioFrameView( + processed_capture_stream, num_channels, channel_size)); + aec_dump_->WriteCaptureStreamMessage(); +} + +void AudioProcessingImpl::RecordProcessedCaptureStream( + const int16_t* const data, + const StreamConfig& config) { + RTC_DCHECK(aec_dump_); + + aec_dump_->AddCaptureStreamOutput(data, config.num_channels(), + config.num_frames()); + aec_dump_->WriteCaptureStreamMessage(); +} + +void AudioProcessingImpl::RecordAudioProcessingState() { + RTC_DCHECK(aec_dump_); + AecDump::AudioProcessingState audio_proc_state; + audio_proc_state.delay = capture_nonlocked_.stream_delay_ms; + audio_proc_state.drift = 0; + audio_proc_state.applied_input_volume = capture_.applied_input_volume; + audio_proc_state.keypress = capture_.key_pressed; + aec_dump_->AddAudioProcessingState(audio_proc_state); +} + +AudioProcessingImpl::ApmCaptureState::ApmCaptureState() + : was_stream_delay_set(false), + capture_output_used(true), + capture_output_used_last_frame(true), + key_pressed(false), + capture_processing_format(kSampleRate16kHz), + split_rate(kSampleRate16kHz), + echo_path_gain_change(false), + prev_pre_adjustment_gain(-1.0f), + playout_volume(-1), + prev_playout_volume(-1), + applied_input_volume_changed(false) {} + +AudioProcessingImpl::ApmCaptureState::~ApmCaptureState() = default; + +AudioProcessingImpl::ApmRenderState::ApmRenderState() = default; + +AudioProcessingImpl::ApmRenderState::~ApmRenderState() = default; + +AudioProcessingImpl::ApmStatsReporter::ApmStatsReporter() + : stats_message_queue_(1) {} + +AudioProcessingImpl::ApmStatsReporter::~ApmStatsReporter() = default; + +AudioProcessingStats AudioProcessingImpl::ApmStatsReporter::GetStatistics() { + MutexLock lock_stats(&mutex_stats_); + bool new_stats_available = stats_message_queue_.Remove(&cached_stats_); + // If the message queue is full, return the cached stats. + static_cast(new_stats_available); + + return cached_stats_; +} + +void AudioProcessingImpl::ApmStatsReporter::UpdateStatistics( + const AudioProcessingStats& new_stats) { + AudioProcessingStats stats_to_queue = new_stats; + bool stats_message_passed = stats_message_queue_.Insert(&stats_to_queue); + // If the message queue is full, discard the new stats. + static_cast(stats_message_passed); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/audio_processing_impl.h b/pkg/apm/webrtc/modules/audio_processing/audio_processing_impl.h new file mode 100644 index 00000000..fe26ea16 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/audio_processing_impl.h @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AUDIO_PROCESSING_IMPL_H_ +#define MODULES_AUDIO_PROCESSING_AUDIO_PROCESSING_IMPL_H_ + +#include + +#include +#include +#include +#include +#include +#include + +#include "absl/base/nullability.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/audio/audio_processing.h" +#include "api/audio/audio_processing_statistics.h" +#include "api/environment/environment.h" +#include "api/function_view.h" +#include "api/task_queue/task_queue_base.h" +#include "modules/audio_processing/aec3/echo_canceller3.h" +#include "modules/audio_processing/agc/agc_manager_direct.h" +#include "modules/audio_processing/agc/gain_control.h" +#include "modules/audio_processing/agc2/input_volume_stats_reporter.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h" +#include "modules/audio_processing/echo_control_mobile_impl.h" +#include "modules/audio_processing/gain_control_impl.h" +#include "modules/audio_processing/gain_controller2.h" +#include "modules/audio_processing/high_pass_filter.h" +#include "modules/audio_processing/include/aec_dump.h" +#include "modules/audio_processing/include/audio_frame_proxies.h" +#include "modules/audio_processing/ns/noise_suppressor.h" +#include "modules/audio_processing/render_queue_item_verifier.h" +#include "modules/audio_processing/rms_level.h" +#include "rtc_base/gtest_prod_util.h" +#include "rtc_base/swap_queue.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class ApmDataDumper; +class AudioConverter; + +constexpr int RuntimeSettingQueueSize() { + return 100; +} + +class AudioProcessingImpl : public AudioProcessing { + public: + // Methods forcing APM to run in a single-threaded manner. + // Acquires both the render and capture locks. + explicit AudioProcessingImpl(const Environment& env); + AudioProcessingImpl(const Environment& env, + const AudioProcessing::Config& config, + std::unique_ptr capture_post_processor, + std::unique_ptr render_pre_processor, + std::unique_ptr echo_control_factory, + scoped_refptr echo_detector, + std::unique_ptr capture_analyzer); + ~AudioProcessingImpl() override; + int Initialize() override; + int Initialize(const ProcessingConfig& processing_config) override; + void ApplyConfig(const AudioProcessing::Config& config) override; + bool CreateAndAttachAecDump(absl::string_view file_name, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull + worker_queue) override; + bool CreateAndAttachAecDump(FILE* handle, + int64_t max_log_size_bytes, + TaskQueueBase* absl_nonnull + worker_queue) override; + // TODO(webrtc:5298) Deprecated variant. + void AttachAecDump(std::unique_ptr aec_dump) override; + void DetachAecDump() override; + void SetRuntimeSetting(RuntimeSetting setting) override; + bool PostRuntimeSetting(RuntimeSetting setting) override; + + // Capture-side exclusive methods possibly running APM in a + // multi-threaded manner. Acquire the capture lock. + int ProcessStream(const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest) override; + int ProcessStream(const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config, + float* const* dest) override; + bool GetLinearAecOutput( + ArrayView> linear_output) const override; + void set_output_will_be_muted(bool muted) override; + bool get_output_will_be_muted() override; + void HandleCaptureOutputUsedSetting(bool capture_output_used) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + int set_stream_delay_ms(int delay) override; + void set_stream_key_pressed(bool key_pressed) override; + void set_stream_analog_level(int level) override; + int recommended_stream_analog_level() const + RTC_LOCKS_EXCLUDED(mutex_capture_) override; + + // Render-side exclusive methods possibly running APM in a + // multi-threaded manner. Acquire the render lock. + int ProcessReverseStream(const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest) override; + int AnalyzeReverseStream(const float* const* data, + const StreamConfig& reverse_config) override; + int ProcessReverseStream(const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config, + float* const* dest) override; + + // Methods only accessed from APM submodules or + // from AudioProcessing tests in a single-threaded manner. + // Hence there is no need for locks in these. + int proc_sample_rate_hz() const override; + int proc_split_sample_rate_hz() const override; + size_t num_input_channels() const override; + size_t num_proc_channels() const override; + size_t num_output_channels() const override; + size_t num_reverse_channels() const override; + int stream_delay_ms() const override; + + AudioProcessingStats GetStatistics(bool /* has_remote_tracks */) override { + return GetStatistics(); + } + AudioProcessingStats GetStatistics() override { + return stats_reporter_.GetStatistics(); + } + + AudioProcessing::Config GetConfig() const override; + + protected: + // Overridden in a mock. + virtual void InitializeLocked() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_, mutex_capture_); + void AssertLockedForTest() + RTC_ASSERT_EXCLUSIVE_LOCK(mutex_render_, mutex_capture_) { + mutex_render_.AssertHeld(); + mutex_capture_.AssertHeld(); + } + + private: + // TODO(peah): These friend classes should be removed as soon as the new + // parameter setting scheme allows. + FRIEND_TEST_ALL_PREFIXES(ApmConfiguration, DefaultBehavior); + FRIEND_TEST_ALL_PREFIXES(ApmConfiguration, ValidConfigBehavior); + FRIEND_TEST_ALL_PREFIXES(ApmConfiguration, InValidConfigBehavior); + + void set_stream_analog_level_locked(int level) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void UpdateRecommendedInputVolumeLocked() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Class providing thread-safe message pipe functionality for + // `runtime_settings_`. + class RuntimeSettingEnqueuer { + public: + explicit RuntimeSettingEnqueuer( + SwapQueue* runtime_settings); + ~RuntimeSettingEnqueuer(); + + // Enqueue setting and return whether the setting was successfully enqueued. + bool Enqueue(RuntimeSetting setting); + + private: + SwapQueue& runtime_settings_; + }; + + const Environment env_; + const std::unique_ptr data_dumper_; + static std::atomic instance_count_; + const bool use_setup_specific_default_aec3_config_; + + SwapQueue capture_runtime_settings_; + SwapQueue render_runtime_settings_; + + RuntimeSettingEnqueuer capture_runtime_settings_enqueuer_; + RuntimeSettingEnqueuer render_runtime_settings_enqueuer_; + + // EchoControl factory. + const std::unique_ptr echo_control_factory_; + + class SubmoduleStates { + public: + SubmoduleStates(bool capture_post_processor_enabled, + bool render_pre_processor_enabled, + bool capture_analyzer_enabled); + // Updates the submodule state and returns true if it has changed. + bool Update(bool high_pass_filter_enabled, + bool mobile_echo_controller_enabled, + bool noise_suppressor_enabled, + bool adaptive_gain_controller_enabled, + bool gain_controller2_enabled, + bool gain_adjustment_enabled, + bool echo_controller_enabled); + bool CaptureMultiBandSubModulesActive() const; + bool CaptureMultiBandProcessingPresent() const; + bool CaptureMultiBandProcessingActive(bool ec_processing_active) const; + bool CaptureFullBandProcessingActive() const; + bool CaptureAnalyzerActive() const; + bool RenderMultiBandSubModulesActive() const; + bool RenderFullBandProcessingActive() const; + bool RenderMultiBandProcessingActive() const; + bool HighPassFilteringRequired() const; + + private: + const bool capture_post_processor_enabled_ = false; + const bool render_pre_processor_enabled_ = false; + const bool capture_analyzer_enabled_ = false; + bool high_pass_filter_enabled_ = false; + bool mobile_echo_controller_enabled_ = false; + bool noise_suppressor_enabled_ = false; + bool adaptive_gain_controller_enabled_ = false; + bool gain_controller2_enabled_ = false; + bool gain_adjustment_enabled_ = false; + bool echo_controller_enabled_ = false; + bool first_update_ = true; + }; + + // Methods for modifying the formats struct that is used by both + // the render and capture threads. The check for whether modifications are + // needed is done while holding a single lock only, thereby avoiding that the + // capture thread blocks the render thread. + // Called by render: Holds the render lock when reading the format struct and + // acquires both locks if reinitialization is required. + void MaybeInitializeRender(const StreamConfig& input_config, + const StreamConfig& output_config) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + // Called by capture: Acquires and releases the capture lock to read the + // format struct and acquires both locks if reinitialization is needed. + void MaybeInitializeCapture(const StreamConfig& input_config, + const StreamConfig& output_config); + + // Method for updating the state keeping track of the active submodules. + // Returns a bool indicating whether the state has changed. + bool UpdateActiveSubmoduleStates() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Methods requiring APM running in a single-threaded manner, requiring both + // the render and capture lock to be acquired. + void InitializeLocked(const ProcessingConfig& config) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_, mutex_capture_); + void InitializeResidualEchoDetector() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_, mutex_capture_); + void InitializeEchoController() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_, mutex_capture_); + + // Initializations of capture-only sub-modules, requiring the capture lock + // already acquired. + void InitializeHighPassFilter(bool forced_reset) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializeGainController1() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + // Initializes the `GainController2` sub-module. If the sub-module is enabled, + // recreates it. + void InitializeGainController2() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializeNoiseSuppressor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializeCaptureLevelsAdjuster() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializePostProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializeAnalyzer() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Initializations of render-only submodules, requiring the render lock + // already acquired. + void InitializePreProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + + // Sample rate used for the fullband processing. + int proc_fullband_sample_rate_hz() const + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Empties and handles the respective RuntimeSetting queues. + void HandleCaptureRuntimeSettings() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void HandleRenderRuntimeSettings() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + + void EmptyQueuedRenderAudio() RTC_LOCKS_EXCLUDED(mutex_capture_); + void EmptyQueuedRenderAudioLocked() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void AllocateRenderQueue() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_, mutex_capture_); + void QueueBandedRenderAudio(AudioBuffer* audio) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + void QueueNonbandedRenderAudio(AudioBuffer* audio) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + + // Capture-side exclusive methods possibly running APM in a multi-threaded + // manner that are called with the render lock already acquired. + int ProcessCaptureStreamLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Render-side exclusive methods possibly running APM in a multi-threaded + // manner that are called with the render lock already acquired. + int AnalyzeReverseStreamLocked(const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + int ProcessRenderStreamLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + + // Collects configuration settings from public and private + // submodules to be saved as an audioproc::Config message on the + // AecDump if it is attached. If not `forced`, only writes the current + // config if it is different from the last saved one; if `forced`, + // writes the config regardless of the last saved. + void WriteAecDumpConfigMessage(bool forced) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Notifies attached AecDump of current configuration and capture data. + void RecordUnprocessedCaptureStream(const float* const* capture_stream) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + void RecordUnprocessedCaptureStream(const int16_t* const data, + const StreamConfig& config) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Notifies attached AecDump of current configuration and + // processed capture data and issues a capture stream recording + // request. + void RecordProcessedCaptureStream( + const float* const* processed_capture_stream) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + void RecordProcessedCaptureStream(const int16_t* const data, + const StreamConfig& config) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Notifies attached AecDump about current state (delay, drift, etc). + void RecordAudioProcessingState() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Ensures that overruns in the capture runtime settings queue is properly + // handled by the code, providing safe-fallbacks to mitigate the implications + // of any settings being missed. + void HandleOverrunInCaptureRuntimeSettingsQueue() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // AecDump instance used for optionally logging APM config, input + // and output to file in the AEC-dump format defined in debug.proto. + std::unique_ptr aec_dump_; + + // Hold the last config written with AecDump for avoiding writing + // the same config twice. + InternalAPMConfig apm_config_for_aec_dump_ RTC_GUARDED_BY(mutex_capture_); + + // Critical sections. + mutable Mutex mutex_render_ RTC_ACQUIRED_BEFORE(mutex_capture_); + mutable Mutex mutex_capture_; + + // Struct containing the Config specifying the behavior of APM. + AudioProcessing::Config config_; + + // Class containing information about what submodules are active. + SubmoduleStates submodule_states_; + + // Struct containing the pointers to the submodules. + struct Submodules { + Submodules(std::unique_ptr capture_post_processor, + std::unique_ptr render_pre_processor, + scoped_refptr echo_detector, + std::unique_ptr capture_analyzer) + : echo_detector(std::move(echo_detector)), + capture_post_processor(std::move(capture_post_processor)), + render_pre_processor(std::move(render_pre_processor)), + capture_analyzer(std::move(capture_analyzer)) {} + // Accessed internally from capture or during initialization. + const scoped_refptr echo_detector; + const std::unique_ptr capture_post_processor; + const std::unique_ptr render_pre_processor; + const std::unique_ptr capture_analyzer; + std::unique_ptr agc_manager; + std::unique_ptr gain_control; + std::unique_ptr gain_controller2; + std::unique_ptr high_pass_filter; + std::unique_ptr echo_controller; + std::unique_ptr echo_control_mobile; + std::unique_ptr noise_suppressor; + std::unique_ptr capture_levels_adjuster; + } submodules_; + + // State that is written to while holding both the render and capture locks + // but can be read without any lock being held. + // As this is only accessed internally of APM, and all internal methods in APM + // either are holding the render or capture locks, this construct is safe as + // it is not possible to read the variables while writing them. + struct ApmFormatState { + ApmFormatState() + : // Format of processing streams at input/output call sites. + api_format({{{kSampleRate16kHz, 1}, + {kSampleRate16kHz, 1}, + {kSampleRate16kHz, 1}, + {kSampleRate16kHz, 1}}}), + render_processing_format(kSampleRate16kHz, 1) {} + ProcessingConfig api_format; + StreamConfig render_processing_format; + } formats_; + + // APM constants. + const struct ApmConstants { + ApmConstants(bool multi_channel_render_support, + bool multi_channel_capture_support, + bool enforce_split_band_hpf, + bool minimize_processing_for_unused_output) + : multi_channel_render_support(multi_channel_render_support), + multi_channel_capture_support(multi_channel_capture_support), + enforce_split_band_hpf(enforce_split_band_hpf), + minimize_processing_for_unused_output( + minimize_processing_for_unused_output) {} + bool multi_channel_render_support; + bool multi_channel_capture_support; + bool enforce_split_band_hpf; + bool minimize_processing_for_unused_output; + } constants_; + + struct ApmCaptureState { + ApmCaptureState(); + ~ApmCaptureState(); + bool was_stream_delay_set; + bool capture_output_used; + bool capture_output_used_last_frame; + bool key_pressed; + std::unique_ptr capture_audio; + std::unique_ptr capture_fullband_audio; + std::unique_ptr linear_aec_output; + // Only the rate and samples fields of capture_processing_format_ are used + // because the capture processing number of channels is mutable and is + // tracked by the capture_audio_. + StreamConfig capture_processing_format; + int split_rate; + bool echo_path_gain_change; + float prev_pre_adjustment_gain; + int playout_volume; + int prev_playout_volume; + AudioProcessingStats stats; + // Input volume applied on the audio input device when the audio is + // acquired. Unspecified when unknown. + std::optional applied_input_volume; + bool applied_input_volume_changed; + // Recommended input volume to apply on the audio input device the next time + // that audio is acquired. Unspecified when no input volume can be + // recommended. + std::optional recommended_input_volume; + } capture_ RTC_GUARDED_BY(mutex_capture_); + + struct ApmCaptureNonLockedState { + ApmCaptureNonLockedState() + : capture_processing_format(kSampleRate16kHz), + split_rate(kSampleRate16kHz), + stream_delay_ms(0) {} + // Only the rate and samples fields of capture_processing_format_ are used + // because the forward processing number of channels is mutable and is + // tracked by the capture_audio_. + StreamConfig capture_processing_format; + int split_rate; + int stream_delay_ms; + bool echo_controller_enabled = false; + } capture_nonlocked_; + + struct ApmRenderState { + ApmRenderState(); + ~ApmRenderState(); + std::unique_ptr render_converter; + std::unique_ptr render_audio; + } render_ RTC_GUARDED_BY(mutex_render_); + + // Class for statistics reporting. The class is thread-safe and no lock is + // needed when accessing it. + class ApmStatsReporter { + public: + ApmStatsReporter(); + ~ApmStatsReporter(); + + // Returns the most recently reported statistics. + AudioProcessingStats GetStatistics(); + + // Update the cached statistics. + void UpdateStatistics(const AudioProcessingStats& new_stats); + + private: + Mutex mutex_stats_; + AudioProcessingStats cached_stats_ RTC_GUARDED_BY(mutex_stats_); + SwapQueue stats_message_queue_; + } stats_reporter_; + + std::vector aecm_render_queue_buffer_ RTC_GUARDED_BY(mutex_render_); + std::vector aecm_capture_queue_buffer_ + RTC_GUARDED_BY(mutex_capture_); + + size_t agc_render_queue_element_max_size_ RTC_GUARDED_BY(mutex_render_) + RTC_GUARDED_BY(mutex_capture_) = 0; + std::vector agc_render_queue_buffer_ RTC_GUARDED_BY(mutex_render_); + std::vector agc_capture_queue_buffer_ RTC_GUARDED_BY(mutex_capture_); + + size_t red_render_queue_element_max_size_ RTC_GUARDED_BY(mutex_render_) + RTC_GUARDED_BY(mutex_capture_) = 0; + std::vector red_render_queue_buffer_ RTC_GUARDED_BY(mutex_render_); + std::vector red_capture_queue_buffer_ RTC_GUARDED_BY(mutex_capture_); + + RmsLevel capture_input_rms_ RTC_GUARDED_BY(mutex_capture_); + RmsLevel capture_output_rms_ RTC_GUARDED_BY(mutex_capture_); + int capture_rms_interval_counter_ RTC_GUARDED_BY(mutex_capture_) = 0; + + InputVolumeStatsReporter applied_input_volume_stats_reporter_ + RTC_GUARDED_BY(mutex_capture_); + InputVolumeStatsReporter recommended_input_volume_stats_reporter_ + RTC_GUARDED_BY(mutex_capture_); + + // Lock protection not needed. + std::unique_ptr< + SwapQueue, RenderQueueItemVerifier>> + aecm_render_signal_queue_; + std::unique_ptr< + SwapQueue, RenderQueueItemVerifier>> + agc_render_signal_queue_; + std::unique_ptr, RenderQueueItemVerifier>> + red_render_signal_queue_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AUDIO_PROCESSING_IMPL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc b/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc new file mode 100644 index 00000000..e9d76532 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h" + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/audio_buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +AudioSamplesScaler::AudioSamplesScaler(float initial_gain) + : previous_gain_(initial_gain), target_gain_(initial_gain) {} + +void AudioSamplesScaler::Process(AudioBuffer& audio_buffer) { + if (static_cast(audio_buffer.num_frames()) != samples_per_channel_) { + // Update the members depending on audio-buffer length if needed. + RTC_DCHECK_GT(audio_buffer.num_frames(), 0); + samples_per_channel_ = static_cast(audio_buffer.num_frames()); + one_by_samples_per_channel_ = 1.f / samples_per_channel_; + } + + if (target_gain_ == 1.f && previous_gain_ == target_gain_) { + // If only a gain of 1 is to be applied, do an early return without applying + // any gain. + return; + } + + float gain = previous_gain_; + if (previous_gain_ == target_gain_) { + // Apply a non-changing gain. + for (size_t channel = 0; channel < audio_buffer.num_channels(); ++channel) { + ArrayView channel_view(audio_buffer.channels()[channel], + samples_per_channel_); + for (float& sample : channel_view) { + sample *= gain; + } + } + } else { + const float increment = + (target_gain_ - previous_gain_) * one_by_samples_per_channel_; + + if (increment > 0.f) { + // Apply an increasing gain. + for (size_t channel = 0; channel < audio_buffer.num_channels(); + ++channel) { + gain = previous_gain_; + ArrayView channel_view(audio_buffer.channels()[channel], + samples_per_channel_); + for (float& sample : channel_view) { + gain = std::min(gain + increment, target_gain_); + sample *= gain; + } + } + } else { + // Apply a decreasing gain. + for (size_t channel = 0; channel < audio_buffer.num_channels(); + ++channel) { + gain = previous_gain_; + ArrayView channel_view(audio_buffer.channels()[channel], + samples_per_channel_); + for (float& sample : channel_view) { + gain = std::max(gain + increment, target_gain_); + sample *= gain; + } + } + } + } + previous_gain_ = target_gain_; + + // Saturate the samples to be in the S16 range. + for (size_t channel = 0; channel < audio_buffer.num_channels(); ++channel) { + ArrayView channel_view(audio_buffer.channels()[channel], + samples_per_channel_); + for (float& sample : channel_view) { + constexpr float kMinFloatS16Value = -32768.f; + constexpr float kMaxFloatS16Value = 32767.f; + sample = SafeClamp(sample, kMinFloatS16Value, kMaxFloatS16Value); + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h b/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h new file mode 100644 index 00000000..2ae85339 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_AUDIO_SAMPLES_SCALER_H_ +#define MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_AUDIO_SAMPLES_SCALER_H_ + +#include + +#include "modules/audio_processing/audio_buffer.h" + +namespace webrtc { + +// Handles and applies a gain to the samples in an audio buffer. +// The gain is applied for each sample and any changes in the gain take effect +// gradually (in a linear manner) over one frame. +class AudioSamplesScaler { + public: + // C-tor. The supplied `initial_gain` is used immediately at the first call to + // Process(), i.e., in contrast to the gain supplied by SetGain(...) there is + // no gradual change to the `initial_gain`. + explicit AudioSamplesScaler(float initial_gain); + AudioSamplesScaler(const AudioSamplesScaler&) = delete; + AudioSamplesScaler& operator=(const AudioSamplesScaler&) = delete; + + // Applies the specified gain to the audio in `audio_buffer`. + void Process(AudioBuffer& audio_buffer); + + // Sets the gain to apply to each sample. + void SetGain(float gain) { target_gain_ = gain; } + + private: + float previous_gain_ = 1.f; + float target_gain_ = 1.f; + int samples_per_channel_ = -1; + float one_by_samples_per_channel_ = -1.f; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_AUDIO_SAMPLES_SCALER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc b/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc new file mode 100644 index 00000000..f65aae89 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h" + +#include "modules/audio_processing/audio_buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +namespace { + +constexpr int kMinAnalogMicGainLevel = 0; +constexpr int kMaxAnalogMicGainLevel = 255; + +float ComputeLevelBasedGain(int emulated_analog_mic_gain_level) { + static_assert( + kMinAnalogMicGainLevel == 0, + "The minimum gain level must be 0 for the maths below to work."); + static_assert(kMaxAnalogMicGainLevel > 0, + "The minimum gain level must be larger than 0 for the maths " + "below to work."); + constexpr float kGainToLevelMultiplier = 1.f / kMaxAnalogMicGainLevel; + + RTC_DCHECK_GE(emulated_analog_mic_gain_level, kMinAnalogMicGainLevel); + RTC_DCHECK_LE(emulated_analog_mic_gain_level, kMaxAnalogMicGainLevel); + return kGainToLevelMultiplier * emulated_analog_mic_gain_level; +} + +float ComputePreGain(float pre_gain, + int emulated_analog_mic_gain_level, + bool emulated_analog_mic_gain_enabled) { + return emulated_analog_mic_gain_enabled + ? pre_gain * ComputeLevelBasedGain(emulated_analog_mic_gain_level) + : pre_gain; +} + +} // namespace + +CaptureLevelsAdjuster::CaptureLevelsAdjuster( + bool emulated_analog_mic_gain_enabled, + int emulated_analog_mic_gain_level, + float pre_gain, + float post_gain) + : emulated_analog_mic_gain_enabled_(emulated_analog_mic_gain_enabled), + emulated_analog_mic_gain_level_(emulated_analog_mic_gain_level), + pre_gain_(pre_gain), + pre_adjustment_gain_(ComputePreGain(pre_gain_, + emulated_analog_mic_gain_level_, + emulated_analog_mic_gain_enabled_)), + pre_scaler_(pre_adjustment_gain_), + post_scaler_(post_gain) {} + +void CaptureLevelsAdjuster::ApplyPreLevelAdjustment(AudioBuffer& audio_buffer) { + pre_scaler_.Process(audio_buffer); +} + +void CaptureLevelsAdjuster::ApplyPostLevelAdjustment( + AudioBuffer& audio_buffer) { + post_scaler_.Process(audio_buffer); +} + +void CaptureLevelsAdjuster::SetPreGain(float pre_gain) { + pre_gain_ = pre_gain; + UpdatePreAdjustmentGain(); +} + +void CaptureLevelsAdjuster::SetPostGain(float post_gain) { + post_scaler_.SetGain(post_gain); +} + +void CaptureLevelsAdjuster::SetAnalogMicGainLevel(int level) { + RTC_DCHECK_GE(level, kMinAnalogMicGainLevel); + RTC_DCHECK_LE(level, kMaxAnalogMicGainLevel); + int clamped_level = + SafeClamp(level, kMinAnalogMicGainLevel, kMaxAnalogMicGainLevel); + + emulated_analog_mic_gain_level_ = clamped_level; + UpdatePreAdjustmentGain(); +} + +void CaptureLevelsAdjuster::UpdatePreAdjustmentGain() { + pre_adjustment_gain_ = + ComputePreGain(pre_gain_, emulated_analog_mic_gain_level_, + emulated_analog_mic_gain_enabled_); + pre_scaler_.SetGain(pre_adjustment_gain_); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.go b/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.go new file mode 100644 index 00000000..19f42952 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.go @@ -0,0 +1,10 @@ +//go:build console + +package capture_levels_adjuster + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h b/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h new file mode 100644 index 00000000..38b68ad0 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_CAPTURE_LEVELS_ADJUSTER_H_ +#define MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_CAPTURE_LEVELS_ADJUSTER_H_ + +#include + +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h" + +namespace webrtc { + +// Adjusts the level of the capture signal before and after all capture-side +// processing is done using a combination of explicitly specified gains +// and an emulated analog gain functionality where a specified analog level +// results in an additional gain. The pre-adjustment is achieved by combining +// the gain value `pre_gain` and the level `emulated_analog_mic_gain_level` to +// form a combined gain of `pre_gain`*`emulated_analog_mic_gain_level`/255 which +// is multiplied to each sample. The intention of the +// `emulated_analog_mic_gain_level` is to be controlled by the analog AGC +// functionality and to produce an emulated analog mic gain equal to +// `emulated_analog_mic_gain_level`/255. The post level adjustment is achieved +// by multiplying each sample with the value of `post_gain`. Any changes in the +// gains take are done smoothly over one frame and the scaled samples are +// clamped to fit into the allowed S16 sample range. +class CaptureLevelsAdjuster { + public: + // C-tor. The values for the level and the gains must fulfill + // 0 <= emulated_analog_mic_gain_level <= 255. + // 0.f <= pre_gain. + // 0.f <= post_gain. + CaptureLevelsAdjuster(bool emulated_analog_mic_gain_enabled, + int emulated_analog_mic_gain_level, + float pre_gain, + float post_gain); + CaptureLevelsAdjuster(const CaptureLevelsAdjuster&) = delete; + CaptureLevelsAdjuster& operator=(const CaptureLevelsAdjuster&) = delete; + + // Adjusts the level of the signal. This should be called before any of the + // other processing is performed. + void ApplyPreLevelAdjustment(AudioBuffer& audio_buffer); + + // Adjusts the level of the signal. This should be called after all of the + // other processing have been performed. + void ApplyPostLevelAdjustment(AudioBuffer& audio_buffer); + + // Sets the gain to apply to each sample before any of the other processing is + // performed. + void SetPreGain(float pre_gain); + + // Returns the total pre-adjustment gain applied, comprising both the pre_gain + // as well as the gain from the emulated analog mic, to each sample before any + // of the other processing is performed. + float GetPreAdjustmentGain() const { return pre_adjustment_gain_; } + + // Sets the gain to apply to each sample after all of the other processing + // have been performed. + void SetPostGain(float post_gain); + + // Sets the analog gain level to use for the emulated analog gain. + // `level` must be in the range [0...255]. + void SetAnalogMicGainLevel(int level); + + // Returns the current analog gain level used for the emulated analog gain. + int GetAnalogMicGainLevel() const { return emulated_analog_mic_gain_level_; } + + private: + // Updates the value of `pre_adjustment_gain_` based on the supplied values + // for `pre_gain` and `emulated_analog_mic_gain_level_`. + void UpdatePreAdjustmentGain(); + + const bool emulated_analog_mic_gain_enabled_; + int emulated_analog_mic_gain_level_; + float pre_gain_; + float pre_adjustment_gain_; + AudioSamplesScaler pre_scaler_; + AudioSamplesScaler post_scaler_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_CAPTURE_LEVELS_ADJUSTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/echo_control_mobile_impl.cc b/pkg/apm/webrtc/modules/audio_processing/echo_control_mobile_impl.cc new file mode 100644 index 00000000..aff9d4e7 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/echo_control_mobile_impl.cc @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/echo_control_mobile_impl.h" + +#include + +#include + +#include "api/audio/audio_processing.h" +#include "modules/audio_processing/aecm/echo_control_mobile.h" +#include "modules/audio_processing/audio_buffer.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { +int16_t MapSetting(EchoControlMobileImpl::RoutingMode mode) { + switch (mode) { + case EchoControlMobileImpl::kQuietEarpieceOrHeadset: + return 0; + case EchoControlMobileImpl::kEarpiece: + return 1; + case EchoControlMobileImpl::kLoudEarpiece: + return 2; + case EchoControlMobileImpl::kSpeakerphone: + return 3; + case EchoControlMobileImpl::kLoudSpeakerphone: + return 4; + } + RTC_DCHECK_NOTREACHED(); + return -1; +} + +AudioProcessing::Error MapError(int err) { + switch (err) { + case AECM_UNSUPPORTED_FUNCTION_ERROR: + return AudioProcessing::kUnsupportedFunctionError; + case AECM_NULL_POINTER_ERROR: + return AudioProcessing::kNullPointerError; + case AECM_BAD_PARAMETER_ERROR: + return AudioProcessing::kBadParameterError; + case AECM_BAD_PARAMETER_WARNING: + return AudioProcessing::kBadStreamParameterWarning; + default: + // AECM_UNSPECIFIED_ERROR + // AECM_UNINITIALIZED_ERROR + return AudioProcessing::kUnspecifiedError; + } +} + +} // namespace + +struct EchoControlMobileImpl::StreamProperties { + StreamProperties() = delete; + StreamProperties(int sample_rate_hz, + size_t num_reverse_channels, + size_t num_output_channels) + : sample_rate_hz(sample_rate_hz), + num_reverse_channels(num_reverse_channels), + num_output_channels(num_output_channels) {} + + int sample_rate_hz; + size_t num_reverse_channels; + size_t num_output_channels; +}; + +class EchoControlMobileImpl::Canceller { + public: + Canceller() { + state_ = WebRtcAecm_Create(); + RTC_CHECK(state_); + } + + ~Canceller() { + RTC_DCHECK(state_); + WebRtcAecm_Free(state_); + } + + Canceller(const Canceller&) = delete; + Canceller& operator=(const Canceller&) = delete; + + void* state() { + RTC_DCHECK(state_); + return state_; + } + + void Initialize(int sample_rate_hz) { + RTC_DCHECK(state_); + int error = WebRtcAecm_Init(state_, sample_rate_hz); + RTC_DCHECK_EQ(AudioProcessing::kNoError, error); + } + + private: + void* state_; +}; + +EchoControlMobileImpl::EchoControlMobileImpl() + : routing_mode_(kSpeakerphone), comfort_noise_enabled_(false) {} + +EchoControlMobileImpl::~EchoControlMobileImpl() {} + +void EchoControlMobileImpl::ProcessRenderAudio( + ArrayView packed_render_audio) { + RTC_DCHECK(stream_properties_); + + size_t buffer_index = 0; + size_t num_frames_per_band = + packed_render_audio.size() / (stream_properties_->num_output_channels * + stream_properties_->num_reverse_channels); + + for (auto& canceller : cancellers_) { + WebRtcAecm_BufferFarend(canceller->state(), + &packed_render_audio[buffer_index], + num_frames_per_band); + + buffer_index += num_frames_per_band; + } +} + +void EchoControlMobileImpl::PackRenderAudioBuffer( + const AudioBuffer* audio, + size_t num_output_channels, + size_t num_channels, + std::vector* packed_buffer) { + RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, + audio->num_frames_per_band()); + RTC_DCHECK_EQ(num_channels, audio->num_channels()); + + // The ordering convention must be followed to pass to the correct AECM. + packed_buffer->clear(); + int render_channel = 0; + for (size_t i = 0; i < num_output_channels; i++) { + for (size_t j = 0; j < audio->num_channels(); j++) { + std::array data_to_buffer; + FloatS16ToS16(audio->split_bands_const(render_channel)[kBand0To8kHz], + audio->num_frames_per_band(), data_to_buffer.data()); + + // Buffer the samples in the render queue. + packed_buffer->insert( + packed_buffer->end(), data_to_buffer.data(), + data_to_buffer.data() + audio->num_frames_per_band()); + render_channel = (render_channel + 1) % audio->num_channels(); + } + } +} + +size_t EchoControlMobileImpl::NumCancellersRequired( + size_t num_output_channels, + size_t num_reverse_channels) { + return num_output_channels * num_reverse_channels; +} + +int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio, + int stream_delay_ms) { + RTC_DCHECK(stream_properties_); + RTC_DCHECK_GE(160, audio->num_frames_per_band()); + RTC_DCHECK_EQ(audio->num_channels(), stream_properties_->num_output_channels); + RTC_DCHECK_GE(cancellers_.size(), stream_properties_->num_reverse_channels * + audio->num_channels()); + + int err = AudioProcessing::kNoError; + + // The ordering convention must be followed to pass to the correct AECM. + size_t handle_index = 0; + for (size_t capture = 0; capture < audio->num_channels(); ++capture) { + // TODO(ajm): improve how this works, possibly inside AECM. + // This is kind of hacked up. + RTC_DCHECK_LT(capture, low_pass_reference_.size()); + const int16_t* noisy = + reference_copied_ ? low_pass_reference_[capture].data() : nullptr; + + RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, + audio->num_frames_per_band()); + + std::array split_bands_data; + int16_t* split_bands = split_bands_data.data(); + const int16_t* clean = split_bands_data.data(); + if (audio->split_bands(capture)[kBand0To8kHz]) { + FloatS16ToS16(audio->split_bands(capture)[kBand0To8kHz], + audio->num_frames_per_band(), split_bands_data.data()); + } else { + clean = nullptr; + split_bands = nullptr; + } + + if (noisy == NULL) { + noisy = clean; + clean = NULL; + } + for (size_t render = 0; render < stream_properties_->num_reverse_channels; + ++render) { + err = WebRtcAecm_Process(cancellers_[handle_index]->state(), noisy, clean, + split_bands, audio->num_frames_per_band(), + stream_delay_ms); + + if (split_bands) { + S16ToFloatS16(split_bands, audio->num_frames_per_band(), + audio->split_bands(capture)[kBand0To8kHz]); + } + + if (err != AudioProcessing::kNoError) { + return MapError(err); + } + + ++handle_index; + } + for (size_t band = 1u; band < audio->num_bands(); ++band) { + memset(audio->split_bands_f(capture)[band], 0, + audio->num_frames_per_band() * + sizeof(audio->split_bands_f(capture)[band][0])); + } + } + return AudioProcessing::kNoError; +} + +int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) { + if (MapSetting(mode) == -1) { + return AudioProcessing::kBadParameterError; + } + routing_mode_ = mode; + return Configure(); +} + +EchoControlMobileImpl::RoutingMode EchoControlMobileImpl::routing_mode() const { + return routing_mode_; +} + +int EchoControlMobileImpl::enable_comfort_noise(bool enable) { + comfort_noise_enabled_ = enable; + return Configure(); +} + +bool EchoControlMobileImpl::is_comfort_noise_enabled() const { + return comfort_noise_enabled_; +} + +void EchoControlMobileImpl::Initialize(int sample_rate_hz, + size_t num_reverse_channels, + size_t num_output_channels) { + low_pass_reference_.resize(num_output_channels); + for (auto& reference : low_pass_reference_) { + reference.fill(0); + } + + stream_properties_.reset(new StreamProperties( + sample_rate_hz, num_reverse_channels, num_output_channels)); + + // AECM only supports 16 kHz or lower sample rates. + RTC_DCHECK_LE(stream_properties_->sample_rate_hz, + AudioProcessing::kSampleRate16kHz); + + cancellers_.resize( + NumCancellersRequired(stream_properties_->num_output_channels, + stream_properties_->num_reverse_channels)); + + for (auto& canceller : cancellers_) { + if (!canceller) { + canceller.reset(new Canceller()); + } + canceller->Initialize(sample_rate_hz); + } + Configure(); +} + +int EchoControlMobileImpl::Configure() { + AecmConfig config; + config.cngMode = comfort_noise_enabled_; + config.echoMode = MapSetting(routing_mode_); + int error = AudioProcessing::kNoError; + for (auto& canceller : cancellers_) { + int handle_error = WebRtcAecm_set_config(canceller->state(), config); + if (handle_error != AudioProcessing::kNoError) { + error = handle_error; + } + } + return error; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/echo_control_mobile_impl.h b/pkg/apm/webrtc/modules/audio_processing/echo_control_mobile_impl.h new file mode 100644 index 00000000..0d605fe4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/echo_control_mobile_impl.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ +#define MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ + +#include +#include + +#include +#include + +#include "api/array_view.h" + +namespace webrtc { + +class AudioBuffer; + +// The acoustic echo control for mobile (AECM) component is a low complexity +// robust option intended for use on mobile devices. +class EchoControlMobileImpl { + public: + EchoControlMobileImpl(); + + ~EchoControlMobileImpl(); + + // Recommended settings for particular audio routes. In general, the louder + // the echo is expected to be, the higher this value should be set. The + // preferred setting may vary from device to device. + enum RoutingMode { + kQuietEarpieceOrHeadset, + kEarpiece, + kLoudEarpiece, + kSpeakerphone, + kLoudSpeakerphone + }; + + // Sets echo control appropriate for the audio routing `mode` on the device. + // It can and should be updated during a call if the audio routing changes. + int set_routing_mode(RoutingMode mode); + RoutingMode routing_mode() const; + + // Comfort noise replaces suppressed background noise to maintain a + // consistent signal level. + int enable_comfort_noise(bool enable); + bool is_comfort_noise_enabled() const; + + void ProcessRenderAudio(ArrayView packed_render_audio); + int ProcessCaptureAudio(AudioBuffer* audio, int stream_delay_ms); + + void Initialize(int sample_rate_hz, + size_t num_reverse_channels, + size_t num_output_channels); + + static void PackRenderAudioBuffer(const AudioBuffer* audio, + size_t num_output_channels, + size_t num_channels, + std::vector* packed_buffer); + + static size_t NumCancellersRequired(size_t num_output_channels, + size_t num_reverse_channels); + + private: + class Canceller; + struct StreamProperties; + + int Configure(); + + RoutingMode routing_mode_; + bool comfort_noise_enabled_; + + std::vector> cancellers_; + std::unique_ptr stream_properties_; + std::vector> low_pass_reference_; + bool reference_copied_ = false; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/echo_detector/circular_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/echo_detector/circular_buffer.cc new file mode 100644 index 00000000..c24f920c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/echo_detector/circular_buffer.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/echo_detector/circular_buffer.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +CircularBuffer::CircularBuffer(size_t size) : buffer_(size) {} +CircularBuffer::~CircularBuffer() = default; + +void CircularBuffer::Push(float value) { + buffer_[next_insertion_index_] = value; + ++next_insertion_index_; + next_insertion_index_ %= buffer_.size(); + RTC_DCHECK_LT(next_insertion_index_, buffer_.size()); + nr_elements_in_buffer_ = std::min(nr_elements_in_buffer_ + 1, buffer_.size()); + RTC_DCHECK_LE(nr_elements_in_buffer_, buffer_.size()); +} + +std::optional CircularBuffer::Pop() { + if (nr_elements_in_buffer_ == 0) { + return std::nullopt; + } + const size_t index = + (buffer_.size() + next_insertion_index_ - nr_elements_in_buffer_) % + buffer_.size(); + RTC_DCHECK_LT(index, buffer_.size()); + --nr_elements_in_buffer_; + return buffer_[index]; +} + +void CircularBuffer::Clear() { + std::fill(buffer_.begin(), buffer_.end(), 0.f); + next_insertion_index_ = 0; + nr_elements_in_buffer_ = 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/echo_detector/circular_buffer.h b/pkg/apm/webrtc/modules/audio_processing/echo_detector/circular_buffer.h new file mode 100644 index 00000000..5cce0d7a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/echo_detector/circular_buffer.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_CIRCULAR_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_CIRCULAR_BUFFER_H_ + +#include + +#include +#include + +namespace webrtc { + +// Ring buffer containing floating point values. +struct CircularBuffer { + public: + explicit CircularBuffer(size_t size); + ~CircularBuffer(); + + void Push(float value); + std::optional Pop(); + size_t Size() const { return nr_elements_in_buffer_; } + // This function fills the buffer with zeros, but does not change its size. + void Clear(); + + private: + std::vector buffer_; + size_t next_insertion_index_ = 0; + // This is the number of elements that have been pushed into the circular + // buffer, not the allocated buffer size. + size_t nr_elements_in_buffer_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_CIRCULAR_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/echo_detector/echo_detector.go b/pkg/apm/webrtc/modules/audio_processing/echo_detector/echo_detector.go new file mode 100644 index 00000000..acdbe0e9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/echo_detector/echo_detector.go @@ -0,0 +1,10 @@ +//go:build console + +package echo_detector + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.cc new file mode 100644 index 00000000..a8574038 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/echo_detector/mean_variance_estimator.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// Parameter controlling the adaptation speed. +constexpr float kAlpha = 0.001f; + +} // namespace + +void MeanVarianceEstimator::Update(float value) { + mean_ = (1.f - kAlpha) * mean_ + kAlpha * value; + variance_ = + (1.f - kAlpha) * variance_ + kAlpha * (value - mean_) * (value - mean_); + RTC_DCHECK(isfinite(mean_)); + RTC_DCHECK(isfinite(variance_)); +} + +float MeanVarianceEstimator::std_deviation() const { + RTC_DCHECK_GE(variance_, 0.f); + return sqrtf(variance_); +} + +float MeanVarianceEstimator::mean() const { + return mean_; +} + +void MeanVarianceEstimator::Clear() { + mean_ = 0.f; + variance_ = 0.f; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.h b/pkg/apm/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.h new file mode 100644 index 00000000..7f793df1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MEAN_VARIANCE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MEAN_VARIANCE_ESTIMATOR_H_ + +namespace webrtc { + +// This class iteratively estimates the mean and variance of a signal. +class MeanVarianceEstimator { + public: + void Update(float value); + float std_deviation() const; + float mean() const; + void Clear(); + + private: + // Estimate of the expected value of the input values. + float mean_ = 0.f; + // Estimate of the variance of the input values. + float variance_ = 0.f; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MEAN_VARIANCE_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/echo_detector/moving_max.cc b/pkg/apm/webrtc/modules/audio_processing/echo_detector/moving_max.cc new file mode 100644 index 00000000..3054e98b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/echo_detector/moving_max.cc @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/echo_detector/moving_max.h" + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// Parameter for controlling how fast the estimated maximum decays after the +// previous maximum is no longer valid. With a value of 0.99, the maximum will +// decay to 1% of its former value after 460 updates. +constexpr float kDecayFactor = 0.99f; + +} // namespace + +MovingMax::MovingMax(size_t window_size) : window_size_(window_size) { + RTC_DCHECK_GT(window_size, 0); +} + +MovingMax::~MovingMax() {} + +void MovingMax::Update(float value) { + if (counter_ >= window_size_ - 1) { + max_value_ *= kDecayFactor; + } else { + ++counter_; + } + if (value > max_value_) { + max_value_ = value; + counter_ = 0; + } +} + +float MovingMax::max() const { + return max_value_; +} + +void MovingMax::Clear() { + max_value_ = 0.f; + counter_ = 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/echo_detector/moving_max.h b/pkg/apm/webrtc/modules/audio_processing/echo_detector/moving_max.h new file mode 100644 index 00000000..f7d8ee81 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/echo_detector/moving_max.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MOVING_MAX_H_ +#define MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MOVING_MAX_H_ + +#include + +namespace webrtc { + +class MovingMax { + public: + explicit MovingMax(size_t window_size); + ~MovingMax(); + + void Update(float value); + float max() const; + // Reset all of the state in this class. + void Clear(); + + private: + float max_value_ = 0.f; + size_t counter_ = 0; + size_t window_size_ = 1; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MOVING_MAX_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc new file mode 100644 index 00000000..8ec9fe9f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/echo_detector/normalized_covariance_estimator.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// Parameter controlling the adaptation speed. +constexpr float kAlpha = 0.001f; + +} // namespace + +void NormalizedCovarianceEstimator::Update(float x, + float x_mean, + float x_sigma, + float y, + float y_mean, + float y_sigma) { + covariance_ = + (1.f - kAlpha) * covariance_ + kAlpha * (x - x_mean) * (y - y_mean); + normalized_cross_correlation_ = covariance_ / (x_sigma * y_sigma + .0001f); + RTC_DCHECK(isfinite(covariance_)); + RTC_DCHECK(isfinite(normalized_cross_correlation_)); +} + +void NormalizedCovarianceEstimator::Clear() { + covariance_ = 0.f; + normalized_cross_correlation_ = 0.f; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.h b/pkg/apm/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.h new file mode 100644 index 00000000..e3c36d88 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_NORMALIZED_COVARIANCE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_NORMALIZED_COVARIANCE_ESTIMATOR_H_ + +namespace webrtc { + +// This class iteratively estimates the normalized covariance between two +// signals. +class NormalizedCovarianceEstimator { + public: + void Update(float x, + float x_mean, + float x_var, + float y, + float y_mean, + float y_var); + // This function returns an estimate of the Pearson product-moment correlation + // coefficient of the two signals. + float normalized_cross_correlation() const { + return normalized_cross_correlation_; + } + float covariance() const { return covariance_; } + // This function resets the estimated values to zero. + void Clear(); + + private: + float normalized_cross_correlation_ = 0.f; + // Estimate of the covariance value. + float covariance_ = 0.f; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_NORMALIZED_COVARIANCE_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/gain_control_impl.cc b/pkg/apm/webrtc/modules/audio_processing/gain_control_impl.cc new file mode 100644 index 00000000..f20548e0 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/gain_control_impl.cc @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/gain_control_impl.h" + +#include +#include + +#include "api/audio/audio_processing.h" +#include "modules/audio_processing/agc/legacy/gain_control.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +typedef void Handle; + +namespace { +int16_t MapSetting(GainControl::Mode mode) { + switch (mode) { + case GainControl::kAdaptiveAnalog: + return kAgcModeAdaptiveAnalog; + case GainControl::kAdaptiveDigital: + return kAgcModeAdaptiveDigital; + case GainControl::kFixedDigital: + return kAgcModeFixedDigital; + } + RTC_DCHECK_NOTREACHED(); + return -1; +} + +// Applies the sub-frame `gains` to all the bands in `out` and clamps the output +// in the signed 16 bit range. +void ApplyDigitalGain(const int32_t gains[11], + size_t num_bands, + float* const* out) { + constexpr float kScaling = 1.f / 65536.f; + constexpr int kNumSubSections = 16; + constexpr float kOneByNumSubSections = 1.f / kNumSubSections; + + float gains_scaled[11]; + for (int k = 0; k < 11; ++k) { + gains_scaled[k] = gains[k] * kScaling; + } + + for (size_t b = 0; b < num_bands; ++b) { + float* out_band = out[b]; + for (int k = 0, sample = 0; k < 10; ++k) { + const float delta = + (gains_scaled[k + 1] - gains_scaled[k]) * kOneByNumSubSections; + float gain = gains_scaled[k]; + for (int n = 0; n < kNumSubSections; ++n, ++sample) { + RTC_DCHECK_EQ(k * kNumSubSections + n, sample); + out_band[sample] *= gain; + out_band[sample] = + std::min(32767.f, std::max(-32768.f, out_band[sample])); + gain += delta; + } + } + } +} + +} // namespace + +struct GainControlImpl::MonoAgcState { + MonoAgcState() { + state = WebRtcAgc_Create(); + RTC_CHECK(state); + } + + ~MonoAgcState() { + RTC_DCHECK(state); + WebRtcAgc_Free(state); + } + + MonoAgcState(const MonoAgcState&) = delete; + MonoAgcState& operator=(const MonoAgcState&) = delete; + int32_t gains[11]; + Handle* state; +}; + +int GainControlImpl::instance_counter_ = 0; + +GainControlImpl::GainControlImpl() + : data_dumper_(new ApmDataDumper(instance_counter_)), + mode_(kAdaptiveAnalog), + minimum_capture_level_(0), + maximum_capture_level_(255), + limiter_enabled_(true), + target_level_dbfs_(3), + compression_gain_db_(9), + analog_capture_level_(0), + was_analog_level_set_(false), + stream_is_saturated_(false) {} + +GainControlImpl::~GainControlImpl() = default; + +void GainControlImpl::ProcessRenderAudio( + ArrayView packed_render_audio) { + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + WebRtcAgc_AddFarend(mono_agcs_[ch]->state, packed_render_audio.data(), + packed_render_audio.size()); + } +} + +void GainControlImpl::PackRenderAudioBuffer( + const AudioBuffer& audio, + std::vector* packed_buffer) { + RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, audio.num_frames_per_band()); + std::array + mixed_16_kHz_render_data; + ArrayView mixed_16_kHz_render(mixed_16_kHz_render_data.data(), + audio.num_frames_per_band()); + if (audio.num_channels() == 1) { + FloatS16ToS16(audio.split_bands_const(0)[kBand0To8kHz], + audio.num_frames_per_band(), mixed_16_kHz_render_data.data()); + } else { + const int num_channels = static_cast(audio.num_channels()); + for (size_t i = 0; i < audio.num_frames_per_band(); ++i) { + int32_t sum = 0; + for (int ch = 0; ch < num_channels; ++ch) { + sum += FloatS16ToS16(audio.split_channels_const(kBand0To8kHz)[ch][i]); + } + mixed_16_kHz_render_data[i] = sum / num_channels; + } + } + + packed_buffer->clear(); + packed_buffer->insert( + packed_buffer->end(), mixed_16_kHz_render.data(), + (mixed_16_kHz_render.data() + audio.num_frames_per_band())); +} + +int GainControlImpl::AnalyzeCaptureAudio(const AudioBuffer& audio) { + RTC_DCHECK(num_proc_channels_); + RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, audio.num_frames_per_band()); + RTC_DCHECK_EQ(audio.num_channels(), *num_proc_channels_); + RTC_DCHECK_LE(*num_proc_channels_, mono_agcs_.size()); + + int16_t split_band_data[AudioBuffer::kMaxNumBands] + [AudioBuffer::kMaxSplitFrameLength]; + int16_t* split_bands[AudioBuffer::kMaxNumBands] = { + split_band_data[0], split_band_data[1], split_band_data[2]}; + + if (mode_ == kAdaptiveAnalog) { + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + capture_levels_[ch] = analog_capture_level_; + + audio.ExportSplitChannelData(ch, split_bands); + + int err = + WebRtcAgc_AddMic(mono_agcs_[ch]->state, split_bands, + audio.num_bands(), audio.num_frames_per_band()); + + if (err != AudioProcessing::kNoError) { + return AudioProcessing::kUnspecifiedError; + } + } + } else if (mode_ == kAdaptiveDigital) { + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + int32_t capture_level_out = 0; + + audio.ExportSplitChannelData(ch, split_bands); + + int err = + WebRtcAgc_VirtualMic(mono_agcs_[ch]->state, split_bands, + audio.num_bands(), audio.num_frames_per_band(), + analog_capture_level_, &capture_level_out); + + capture_levels_[ch] = capture_level_out; + + if (err != AudioProcessing::kNoError) { + return AudioProcessing::kUnspecifiedError; + } + } + } + + return AudioProcessing::kNoError; +} + +int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio, + bool stream_has_echo) { + if (mode_ == kAdaptiveAnalog && !was_analog_level_set_) { + return AudioProcessing::kStreamParameterNotSetError; + } + + RTC_DCHECK(num_proc_channels_); + RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, + audio->num_frames_per_band()); + RTC_DCHECK_EQ(audio->num_channels(), *num_proc_channels_); + + stream_is_saturated_ = false; + bool error_reported = false; + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + int16_t split_band_data[AudioBuffer::kMaxNumBands] + [AudioBuffer::kMaxSplitFrameLength]; + int16_t* split_bands[AudioBuffer::kMaxNumBands] = { + split_band_data[0], split_band_data[1], split_band_data[2]}; + audio->ExportSplitChannelData(ch, split_bands); + + // The call to stream_has_echo() is ok from a deadlock perspective + // as the capture lock is allready held. + int32_t new_capture_level = 0; + uint8_t saturation_warning = 0; + int err_analyze = WebRtcAgc_Analyze( + mono_agcs_[ch]->state, split_bands, audio->num_bands(), + audio->num_frames_per_band(), capture_levels_[ch], &new_capture_level, + stream_has_echo, &saturation_warning, mono_agcs_[ch]->gains); + capture_levels_[ch] = new_capture_level; + + error_reported = error_reported || err_analyze != AudioProcessing::kNoError; + + stream_is_saturated_ = stream_is_saturated_ || saturation_warning == 1; + } + + // Choose the minimun gain for application + size_t index_to_apply = 0; + for (size_t ch = 1; ch < mono_agcs_.size(); ++ch) { + if (mono_agcs_[index_to_apply]->gains[10] < mono_agcs_[ch]->gains[10]) { + index_to_apply = ch; + } + } + + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + ApplyDigitalGain(mono_agcs_[index_to_apply]->gains, audio->num_bands(), + audio->split_bands(ch)); + } + + RTC_DCHECK_LT(0ul, *num_proc_channels_); + if (mode_ == kAdaptiveAnalog) { + // Take the analog level to be the minimum accross all channels. + analog_capture_level_ = capture_levels_[0]; + for (size_t ch = 1; ch < mono_agcs_.size(); ++ch) { + analog_capture_level_ = + std::min(analog_capture_level_, capture_levels_[ch]); + } + } + + if (error_reported) { + return AudioProcessing::kUnspecifiedError; + } + + was_analog_level_set_ = false; + + return AudioProcessing::kNoError; +} + +// TODO(ajm): ensure this is called under kAdaptiveAnalog. +int GainControlImpl::set_stream_analog_level(int level) { + data_dumper_->DumpRaw("gain_control_set_stream_analog_level", 1, &level); + + was_analog_level_set_ = true; + if (level < minimum_capture_level_ || level > maximum_capture_level_) { + return AudioProcessing::kBadParameterError; + } + analog_capture_level_ = level; + + return AudioProcessing::kNoError; +} + +int GainControlImpl::stream_analog_level() const { + data_dumper_->DumpRaw("gain_control_stream_analog_level", 1, + &analog_capture_level_); + return analog_capture_level_; +} + +int GainControlImpl::set_mode(Mode mode) { + if (MapSetting(mode) == -1) { + return AudioProcessing::kBadParameterError; + } + + mode_ = mode; + RTC_DCHECK(num_proc_channels_); + RTC_DCHECK(sample_rate_hz_); + Initialize(*num_proc_channels_, *sample_rate_hz_); + return AudioProcessing::kNoError; +} + +int GainControlImpl::set_analog_level_limits(int minimum, int maximum) { + if (minimum < 0 || maximum > 65535 || maximum < minimum) { + return AudioProcessing::kBadParameterError; + } + + minimum_capture_level_ = minimum; + maximum_capture_level_ = maximum; + + RTC_DCHECK(num_proc_channels_); + RTC_DCHECK(sample_rate_hz_); + Initialize(*num_proc_channels_, *sample_rate_hz_); + return AudioProcessing::kNoError; +} + +int GainControlImpl::set_target_level_dbfs(int level) { + if (level > 31 || level < 0) { + return AudioProcessing::kBadParameterError; + } + target_level_dbfs_ = level; + return Configure(); +} + +int GainControlImpl::set_compression_gain_db(int gain) { + if (gain < 0 || gain > 90) { + RTC_LOG(LS_ERROR) << "set_compression_gain_db(" << gain << ") failed."; + return AudioProcessing::kBadParameterError; + } + compression_gain_db_ = gain; + return Configure(); +} + +int GainControlImpl::enable_limiter(bool enable) { + limiter_enabled_ = enable; + return Configure(); +} + +void GainControlImpl::Initialize(size_t num_proc_channels, int sample_rate_hz) { + data_dumper_->InitiateNewSetOfRecordings(); + + RTC_DCHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000 || + sample_rate_hz == 48000); + + num_proc_channels_ = num_proc_channels; + sample_rate_hz_ = sample_rate_hz; + + mono_agcs_.resize(*num_proc_channels_); + capture_levels_.resize(*num_proc_channels_); + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + if (!mono_agcs_[ch]) { + mono_agcs_[ch].reset(new MonoAgcState()); + } + + int error = WebRtcAgc_Init(mono_agcs_[ch]->state, minimum_capture_level_, + maximum_capture_level_, MapSetting(mode_), + *sample_rate_hz_); + RTC_DCHECK_EQ(error, 0); + capture_levels_[ch] = analog_capture_level_; + } + + Configure(); +} + +int GainControlImpl::Configure() { + WebRtcAgcConfig config; + // TODO(ajm): Flip the sign here (since AGC expects a positive value) if we + // change the interface. + // RTC_DCHECK_LE(target_level_dbfs_, 0); + // config.targetLevelDbfs = static_cast(-target_level_dbfs_); + config.targetLevelDbfs = static_cast(target_level_dbfs_); + config.compressionGaindB = static_cast(compression_gain_db_); + config.limiterEnable = limiter_enabled_; + + int error = AudioProcessing::kNoError; + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + int error_ch = WebRtcAgc_set_config(mono_agcs_[ch]->state, config); + if (error_ch != AudioProcessing::kNoError) { + error = error_ch; + } + } + return error; +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/gain_control_impl.h b/pkg/apm/webrtc/modules/audio_processing/gain_control_impl.h new file mode 100644 index 00000000..8a3e94d1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/gain_control_impl.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_GAIN_CONTROL_IMPL_H_ +#define MODULES_AUDIO_PROCESSING_GAIN_CONTROL_IMPL_H_ + +#include +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc/gain_control.h" + +namespace webrtc { + +class ApmDataDumper; +class AudioBuffer; + +class GainControlImpl : public GainControl { + public: + GainControlImpl(); + GainControlImpl(const GainControlImpl&) = delete; + GainControlImpl& operator=(const GainControlImpl&) = delete; + + ~GainControlImpl() override; + + void ProcessRenderAudio(ArrayView packed_render_audio); + int AnalyzeCaptureAudio(const AudioBuffer& audio); + int ProcessCaptureAudio(AudioBuffer* audio, bool stream_has_echo); + + void Initialize(size_t num_proc_channels, int sample_rate_hz); + + static void PackRenderAudioBuffer(const AudioBuffer& audio, + std::vector* packed_buffer); + + // GainControl implementation. + int stream_analog_level() const override; + bool is_limiter_enabled() const override { return limiter_enabled_; } + Mode mode() const override { return mode_; } + int set_mode(Mode mode) override; + int compression_gain_db() const override { return compression_gain_db_; } + int set_analog_level_limits(int minimum, int maximum) override; + int set_compression_gain_db(int gain) override; + int set_target_level_dbfs(int level) override; + int enable_limiter(bool enable) override; + int set_stream_analog_level(int level) override; + + private: + struct MonoAgcState; + + // GainControl implementation. + int target_level_dbfs() const override { return target_level_dbfs_; } + int analog_level_minimum() const override { return minimum_capture_level_; } + int analog_level_maximum() const override { return maximum_capture_level_; } + bool stream_is_saturated() const override { return stream_is_saturated_; } + + int Configure(); + + std::unique_ptr data_dumper_; + + Mode mode_; + int minimum_capture_level_; + int maximum_capture_level_; + bool limiter_enabled_; + int target_level_dbfs_; + int compression_gain_db_; + int analog_capture_level_ = 0; + bool was_analog_level_set_; + bool stream_is_saturated_; + + std::vector> mono_agcs_; + std::vector capture_levels_; + + std::optional num_proc_channels_; + std::optional sample_rate_hz_; + + static int instance_counter_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_GAIN_CONTROL_IMPL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/gain_controller2.cc b/pkg/apm/webrtc/modules/audio_processing/gain_controller2.cc new file mode 100644 index 00000000..6e4a2514 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/gain_controller2.cc @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/gain_controller2.h" + +#include +#include + +#include "api/audio/audio_frame.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/agc2/cpu_features.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/include/audio_frame_view.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { +namespace { + +using Agc2Config = AudioProcessing::Config::GainController2; +using InputVolumeControllerConfig = InputVolumeController::Config; + +constexpr int kLogLimiterStatsPeriodMs = 30'000; +constexpr int kFrameLengthMs = 10; +constexpr int kLogLimiterStatsPeriodNumFrames = + kLogLimiterStatsPeriodMs / kFrameLengthMs; + +// Detects the available CPU features and applies any kill-switches. +AvailableCpuFeatures GetAllowedCpuFeatures( + const FieldTrialsView& field_trials) { + AvailableCpuFeatures features = GetAvailableCpuFeatures(); + if (field_trials.IsEnabled("WebRTC-Agc2SimdSse2KillSwitch")) { + features.sse2 = false; + } + if (field_trials.IsEnabled("WebRTC-Agc2SimdAvx2KillSwitch")) { + features.avx2 = false; + } + if (field_trials.IsEnabled("WebRTC-Agc2SimdNeonKillSwitch")) { + features.neon = false; + } + return features; +} + +// Peak and RMS audio levels in dBFS. +struct AudioLevels { + float peak_dbfs; + float rms_dbfs; +}; + +// Speech level info. +struct SpeechLevel { + bool is_confident; + float rms_dbfs; +}; + +// Computes the audio levels for the first channel in `frame`. +AudioLevels ComputeAudioLevels(DeinterleavedView frame, + ApmDataDumper& data_dumper) { + float peak = 0.0f; + float rms = 0.0f; + for (const auto& x : frame[0]) { + peak = std::max(std::fabs(x), peak); + rms += x * x; + } + AudioLevels levels{ + FloatS16ToDbfs(peak), + FloatS16ToDbfs(std::sqrt(rms / frame.samples_per_channel()))}; + data_dumper.DumpRaw("agc2_input_rms_dbfs", levels.rms_dbfs); + data_dumper.DumpRaw("agc2_input_peak_dbfs", levels.peak_dbfs); + return levels; +} + +} // namespace + +std::atomic GainController2::instance_count_(0); + +GainController2::GainController2( + const Environment& env, + const Agc2Config& config, + const InputVolumeControllerConfig& input_volume_controller_config, + int sample_rate_hz, + int num_channels, + bool use_internal_vad) + : cpu_features_(GetAllowedCpuFeatures(env.field_trials())), + data_dumper_(instance_count_.fetch_add(1) + 1), + fixed_gain_applier_( + /*hard_clip_samples=*/false, + /*initial_gain_factor=*/DbToRatio(config.fixed_digital.gain_db)), + limiter_(&data_dumper_, + SampleRateToDefaultChannelSize(sample_rate_hz), + /*histogram_name_prefix=*/"Agc2"), + calls_since_last_limiter_log_(0) { + RTC_DCHECK(Validate(config)); + data_dumper_.InitiateNewSetOfRecordings(); + + if (config.input_volume_controller.enabled || + config.adaptive_digital.enabled) { + // Create dependencies. + speech_level_estimator_ = std::make_unique( + &data_dumper_, config.adaptive_digital, kAdjacentSpeechFramesThreshold); + if (use_internal_vad) + vad_ = std::make_unique( + kVadResetPeriodMs, cpu_features_, sample_rate_hz); + } + + if (config.input_volume_controller.enabled) { + // Create controller. + input_volume_controller_ = std::make_unique( + num_channels, input_volume_controller_config); + // TODO(bugs.webrtc.org/7494): Call `Initialize` in ctor and remove method. + input_volume_controller_->Initialize(); + } + + if (config.adaptive_digital.enabled) { + // Create dependencies. + noise_level_estimator_ = CreateNoiseFloorEstimator(&data_dumper_); + saturation_protector_ = CreateSaturationProtector( + kSaturationProtectorInitialHeadroomDb, kAdjacentSpeechFramesThreshold, + &data_dumper_); + // Create controller. + adaptive_digital_controller_ = + std::make_unique( + &data_dumper_, config.adaptive_digital, + kAdjacentSpeechFramesThreshold); + } +} + +GainController2::~GainController2() = default; + +// TODO(webrtc:7494): Pass the flag also to the other components. +void GainController2::SetCaptureOutputUsed(bool capture_output_used) { + if (input_volume_controller_) { + input_volume_controller_->HandleCaptureOutputUsedChange( + capture_output_used); + } +} + +void GainController2::SetFixedGainDb(float gain_db) { + const float gain_factor = DbToRatio(gain_db); + if (fixed_gain_applier_.GetGainFactor() != gain_factor) { + // Reset the limiter to quickly react on abrupt level changes caused by + // large changes of the fixed gain. + limiter_.Reset(); + } + fixed_gain_applier_.SetGainFactor(gain_factor); +} + +void GainController2::Analyze(int applied_input_volume, + const AudioBuffer& audio_buffer) { + recommended_input_volume_ = std::nullopt; + + RTC_DCHECK_GE(applied_input_volume, 0); + RTC_DCHECK_LE(applied_input_volume, 255); + + if (input_volume_controller_) { + input_volume_controller_->AnalyzeInputAudio(applied_input_volume, + audio_buffer); + } +} + +void GainController2::Process(std::optional speech_probability, + bool input_volume_changed, + AudioBuffer* audio) { + recommended_input_volume_ = std::nullopt; + + data_dumper_.DumpRaw("agc2_applied_input_volume_changed", + input_volume_changed); + if (input_volume_changed) { + // Handle input volume changes. + if (speech_level_estimator_) + speech_level_estimator_->Reset(); + if (saturation_protector_) + saturation_protector_->Reset(); + } + + DeinterleavedView float_frame = audio->view(); + + // Compute speech probability. + if (vad_) { + // When the VAD component runs, `speech_probability` should not be specified + // because APM should not run the same VAD twice (as an APM sub-module and + // internally in AGC2). + RTC_DCHECK(!speech_probability.has_value()); + speech_probability = vad_->Analyze(float_frame); + } + if (speech_probability.has_value()) { + RTC_DCHECK_GE(*speech_probability, 0.0f); + RTC_DCHECK_LE(*speech_probability, 1.0f); + } + // The speech probability may not be defined at this step (e.g., when the + // fixed digital controller alone is enabled). + if (speech_probability.has_value()) + data_dumper_.DumpRaw("agc2_speech_probability", *speech_probability); + + // Compute audio, noise and speech levels. + AudioLevels audio_levels = ComputeAudioLevels(float_frame, data_dumper_); + std::optional noise_rms_dbfs; + if (noise_level_estimator_) { + // TODO(bugs.webrtc.org/7494): Pass `audio_levels` to remove duplicated + // computation in `noise_level_estimator_`. + noise_rms_dbfs = noise_level_estimator_->Analyze(float_frame); + } + std::optional speech_level; + if (speech_level_estimator_) { + RTC_DCHECK(speech_probability.has_value()); + speech_level_estimator_->Update( + audio_levels.rms_dbfs, audio_levels.peak_dbfs, *speech_probability); + speech_level = + SpeechLevel{.is_confident = speech_level_estimator_->is_confident(), + .rms_dbfs = speech_level_estimator_->level_dbfs()}; + } + + // Update the recommended input volume. + if (input_volume_controller_) { + RTC_DCHECK(speech_level.has_value()); + RTC_DCHECK(speech_probability.has_value()); + if (speech_probability.has_value()) { + recommended_input_volume_ = + input_volume_controller_->RecommendInputVolume( + *speech_probability, + speech_level->is_confident + ? std::optional(speech_level->rms_dbfs) + : std::nullopt); + } + } + + if (adaptive_digital_controller_) { + RTC_DCHECK(saturation_protector_); + RTC_DCHECK(speech_probability.has_value()); + RTC_DCHECK(speech_level.has_value()); + saturation_protector_->Analyze(*speech_probability, audio_levels.peak_dbfs, + speech_level->rms_dbfs); + float headroom_db = saturation_protector_->HeadroomDb(); + data_dumper_.DumpRaw("agc2_headroom_db", headroom_db); + float limiter_envelope_dbfs = FloatS16ToDbfs(limiter_.LastAudioLevel()); + data_dumper_.DumpRaw("agc2_limiter_envelope_dbfs", limiter_envelope_dbfs); + RTC_DCHECK(noise_rms_dbfs.has_value()); + adaptive_digital_controller_->Process( + /*info=*/{.speech_probability = *speech_probability, + .speech_level_dbfs = speech_level->rms_dbfs, + .speech_level_reliable = speech_level->is_confident, + .noise_rms_dbfs = *noise_rms_dbfs, + .headroom_db = headroom_db, + .limiter_envelope_dbfs = limiter_envelope_dbfs}, + float_frame); + } + + // TODO(bugs.webrtc.org/7494): Pass `audio_levels` to remove duplicated + // computation in `limiter_`. + fixed_gain_applier_.ApplyGain(float_frame); + + limiter_.Process(float_frame); + + // Periodically log limiter stats. + if (++calls_since_last_limiter_log_ == kLogLimiterStatsPeriodNumFrames) { + calls_since_last_limiter_log_ = 0; + InterpolatedGainCurve::Stats stats = limiter_.GetGainCurveStats(); + RTC_LOG(LS_INFO) << "[AGC2] limiter stats" + << " | identity: " << stats.look_ups_identity_region + << " | knee: " << stats.look_ups_knee_region + << " | limiter: " << stats.look_ups_limiter_region + << " | saturation: " << stats.look_ups_saturation_region; + } +} + +bool GainController2::Validate( + const AudioProcessing::Config::GainController2& config) { + const auto& fixed = config.fixed_digital; + const auto& adaptive = config.adaptive_digital; + return fixed.gain_db >= 0.0f && fixed.gain_db < 50.0f && + adaptive.headroom_db >= 0.0f && adaptive.max_gain_db > 0.0f && + adaptive.initial_gain_db >= 0.0f && + adaptive.max_gain_change_db_per_second > 0.0f && + adaptive.max_output_noise_level_dbfs <= 0.0f; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/gain_controller2.h b/pkg/apm/webrtc/modules/audio_processing/gain_controller2.h new file mode 100644 index 00000000..5594e80b --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/gain_controller2.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_GAIN_CONTROLLER2_H_ +#define MODULES_AUDIO_PROCESSING_GAIN_CONTROLLER2_H_ + +#include +#include +#include + +#include "api/audio/audio_processing.h" +#include "api/environment/environment.h" +#include "modules/audio_processing/agc2/adaptive_digital_gain_controller.h" +#include "modules/audio_processing/agc2/cpu_features.h" +#include "modules/audio_processing/agc2/gain_applier.h" +#include "modules/audio_processing/agc2/input_volume_controller.h" +#include "modules/audio_processing/agc2/limiter.h" +#include "modules/audio_processing/agc2/noise_level_estimator.h" +#include "modules/audio_processing/agc2/saturation_protector.h" +#include "modules/audio_processing/agc2/speech_level_estimator.h" +#include "modules/audio_processing/agc2/vad_wrapper.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +class AudioBuffer; + +// Gain Controller 2 aims to automatically adjust levels by acting on the +// microphone gain and/or applying digital gain. +class GainController2 { + public: + // Ctor. If `use_internal_vad` is true, an internal voice activity + // detector is used for digital adaptive gain. + GainController2( + const Environment& env, + const AudioProcessing::Config::GainController2& config, + const InputVolumeController::Config& input_volume_controller_config, + int sample_rate_hz, + int num_channels, + bool use_internal_vad); + GainController2(const GainController2&) = delete; + GainController2& operator=(const GainController2&) = delete; + ~GainController2(); + + // Sets the fixed digital gain. + void SetFixedGainDb(float gain_db); + + // Updates the input volume controller about whether the capture output is + // used or not. + void SetCaptureOutputUsed(bool capture_output_used); + + // Analyzes `audio_buffer` before `Process()` is called so that the analysis + // can be performed before digital processing operations take place (e.g., + // echo cancellation). The analysis consists of input clipping detection and + // prediction (if enabled). The value of `applied_input_volume` is limited to + // [0, 255]. + void Analyze(int applied_input_volume, const AudioBuffer& audio_buffer); + + // Updates the recommended input volume, applies the adaptive digital and the + // fixed digital gains and runs a limiter on `audio`. + // When the internal VAD is not used, `speech_probability` should be specified + // and in the [0, 1] range. Otherwise ignores `speech_probability` and + // computes the speech probability via `vad_`. + // Handles input volume changes; if the caller cannot determine whether an + // input volume change occurred, set `input_volume_changed` to false. + // TODO(bugs.webrtc.org/7494): Remove `speech_probability`. + void Process(std::optional speech_probability, + bool input_volume_changed, + AudioBuffer* audio); + + static bool Validate(const AudioProcessing::Config::GainController2& config); + + AvailableCpuFeatures GetCpuFeatures() const { return cpu_features_; } + + std::optional recommended_input_volume() const { + return recommended_input_volume_; + } + + private: + static std::atomic instance_count_; + const AvailableCpuFeatures cpu_features_; + ApmDataDumper data_dumper_; + + GainApplier fixed_gain_applier_; + std::unique_ptr noise_level_estimator_; + std::unique_ptr vad_; + std::unique_ptr speech_level_estimator_; + std::unique_ptr input_volume_controller_; + // TODO(bugs.webrtc.org/7494): Rename to `CrestFactorEstimator`. + std::unique_ptr saturation_protector_; + std::unique_ptr adaptive_digital_controller_; + Limiter limiter_; + + int calls_since_last_limiter_log_; + + // TODO(bugs.webrtc.org/7494): Remove intermediate storing at this level once + // APM refactoring is completed. + // Recommended input volume from `InputVolumecontroller`. Non-empty after + // `Process()` if input volume controller is enabled and + // `InputVolumeController::Process()` has returned a non-empty value. + std::optional recommended_input_volume_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_GAIN_CONTROLLER2_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/high_pass_filter.cc b/pkg/apm/webrtc/modules/audio_processing/high_pass_filter.cc new file mode 100644 index 00000000..9d8659ee --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/high_pass_filter.cc @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/high_pass_filter.h" + +#include "api/array_view.h" +#include "modules/audio_processing/audio_buffer.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { +// [B,A] = butter(2,100/8000,'high') +constexpr CascadedBiQuadFilter::BiQuadCoefficients + kHighPassFilterCoefficients16kHz = { + {0.972613898f, -1.945227797f, 0.972613898f}, + {-1.944477658f, 0.945977936f}}; + +// [B,A] = butter(2,100/16000,'high') +constexpr CascadedBiQuadFilter::BiQuadCoefficients + kHighPassFilterCoefficients32kHz = { + {0.986211925f, -1.972423849f, 0.986211925f}, + {-1.972233729f, 0.972613969f}}; + +// [B,A] = butter(2,100/24000,'high') +constexpr CascadedBiQuadFilter::BiQuadCoefficients + kHighPassFilterCoefficients48kHz = { + {0.990786698f, -1.981573396f, 0.990786698f}, + {-1.981488509f, 0.981658283f}}; + +constexpr size_t kNumberOfHighPassBiQuads = 1; + +const CascadedBiQuadFilter::BiQuadCoefficients& ChooseCoefficients( + int sample_rate_hz) { + switch (sample_rate_hz) { + case 16000: + return kHighPassFilterCoefficients16kHz; + case 32000: + return kHighPassFilterCoefficients32kHz; + case 48000: + return kHighPassFilterCoefficients48kHz; + default: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return kHighPassFilterCoefficients16kHz; +} + +} // namespace + +HighPassFilter::HighPassFilter(int sample_rate_hz, size_t num_channels) + : sample_rate_hz_(sample_rate_hz) { + filters_.resize(num_channels); + const auto& coefficients = ChooseCoefficients(sample_rate_hz_); + for (size_t k = 0; k < filters_.size(); ++k) { + filters_[k].reset( + new CascadedBiQuadFilter(coefficients, kNumberOfHighPassBiQuads)); + } +} + +HighPassFilter::~HighPassFilter() = default; + +void HighPassFilter::Process(AudioBuffer* audio, bool use_split_band_data) { + RTC_DCHECK(audio); + RTC_DCHECK_EQ(filters_.size(), audio->num_channels()); + if (use_split_band_data) { + for (size_t k = 0; k < audio->num_channels(); ++k) { + ArrayView channel_data = ArrayView( + audio->split_bands(k)[0], audio->num_frames_per_band()); + filters_[k]->Process(channel_data); + } + } else { + for (size_t k = 0; k < audio->num_channels(); ++k) { + ArrayView channel_data = + ArrayView(&audio->channels()[k][0], audio->num_frames()); + filters_[k]->Process(channel_data); + } + } +} + +void HighPassFilter::Process(std::vector>* audio) { + RTC_DCHECK_EQ(filters_.size(), audio->size()); + for (size_t k = 0; k < audio->size(); ++k) { + filters_[k]->Process((*audio)[k]); + } +} + +void HighPassFilter::Reset() { + for (size_t k = 0; k < filters_.size(); ++k) { + filters_[k]->Reset(); + } +} + +void HighPassFilter::Reset(size_t num_channels) { + const size_t old_num_channels = filters_.size(); + filters_.resize(num_channels); + if (filters_.size() < old_num_channels) { + Reset(); + } else { + for (size_t k = 0; k < old_num_channels; ++k) { + filters_[k]->Reset(); + } + const auto& coefficients = ChooseCoefficients(sample_rate_hz_); + for (size_t k = old_num_channels; k < filters_.size(); ++k) { + filters_[k].reset( + new CascadedBiQuadFilter(coefficients, kNumberOfHighPassBiQuads)); + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/high_pass_filter.h b/pkg/apm/webrtc/modules/audio_processing/high_pass_filter.h new file mode 100644 index 00000000..7e7c370c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/high_pass_filter.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/utility/cascaded_biquad_filter.h" + +namespace webrtc { + +class AudioBuffer; + +class HighPassFilter { + public: + HighPassFilter(int sample_rate_hz, size_t num_channels); + ~HighPassFilter(); + HighPassFilter(const HighPassFilter&) = delete; + HighPassFilter& operator=(const HighPassFilter&) = delete; + + void Process(AudioBuffer* audio, bool use_split_band_data); + void Process(std::vector>* audio); + void Reset(); + void Reset(size_t num_channels); + + int sample_rate_hz() const { return sample_rate_hz_; } + size_t num_channels() const { return filters_.size(); } + + private: + const int sample_rate_hz_; + std::vector> filters_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/include/aec_dump.cc b/pkg/apm/webrtc/modules/audio_processing/include/aec_dump.cc new file mode 100644 index 00000000..8f788cb8 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/include/aec_dump.cc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/include/aec_dump.h" + +namespace webrtc { +InternalAPMConfig::InternalAPMConfig() = default; +InternalAPMConfig::InternalAPMConfig(const InternalAPMConfig&) = default; +InternalAPMConfig::InternalAPMConfig(InternalAPMConfig&&) = default; +InternalAPMConfig& InternalAPMConfig::operator=(const InternalAPMConfig&) = + default; + +bool InternalAPMConfig::operator==(const InternalAPMConfig& other) const { + return aec_enabled == other.aec_enabled && + aec_delay_agnostic_enabled == other.aec_delay_agnostic_enabled && + aec_drift_compensation_enabled == + other.aec_drift_compensation_enabled && + aec_extended_filter_enabled == other.aec_extended_filter_enabled && + aec_suppression_level == other.aec_suppression_level && + aecm_enabled == other.aecm_enabled && + aecm_comfort_noise_enabled == other.aecm_comfort_noise_enabled && + aecm_routing_mode == other.aecm_routing_mode && + agc_enabled == other.agc_enabled && agc_mode == other.agc_mode && + agc_limiter_enabled == other.agc_limiter_enabled && + hpf_enabled == other.hpf_enabled && ns_enabled == other.ns_enabled && + ns_level == other.ns_level && + transient_suppression_enabled == other.transient_suppression_enabled && + noise_robust_agc_enabled == other.noise_robust_agc_enabled && + pre_amplifier_enabled == other.pre_amplifier_enabled && + pre_amplifier_fixed_gain_factor == + other.pre_amplifier_fixed_gain_factor && + experiments_description == other.experiments_description; +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/include/aec_dump.h b/pkg/apm/webrtc/modules/audio_processing/include/aec_dump.h new file mode 100644 index 00000000..532fa210 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/include/aec_dump.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ + +#include + +#include +#include + +#include "absl/base/attributes.h" +#include "api/audio/audio_processing.h" +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { + +// Struct for passing current config from APM without having to +// include protobuf headers. +struct InternalAPMConfig { + InternalAPMConfig(); + InternalAPMConfig(const InternalAPMConfig&); + InternalAPMConfig(InternalAPMConfig&&); + + InternalAPMConfig& operator=(const InternalAPMConfig&); + InternalAPMConfig& operator=(InternalAPMConfig&&) = delete; + + bool operator==(const InternalAPMConfig& other) const; + + bool aec_enabled = false; + bool aec_delay_agnostic_enabled = false; + bool aec_drift_compensation_enabled = false; + bool aec_extended_filter_enabled = false; + int aec_suppression_level = 0; + bool aecm_enabled = false; + bool aecm_comfort_noise_enabled = false; + int aecm_routing_mode = 0; + bool agc_enabled = false; + int agc_mode = 0; + bool agc_limiter_enabled = false; + bool hpf_enabled = false; + bool ns_enabled = false; + int ns_level = 0; + bool transient_suppression_enabled = false; + bool noise_robust_agc_enabled = false; + bool pre_amplifier_enabled = false; + float pre_amplifier_fixed_gain_factor = 1.f; + std::string experiments_description = ""; +}; + +// An interface for recording configuration and input/output streams +// of the Audio Processing Module. The recordings are called +// 'aec-dumps' and are stored in a protobuf format defined in +// debug.proto. +// The Write* methods are always safe to call concurrently or +// otherwise for all implementing subclasses. The intended mode of +// operation is to create a protobuf object from the input, and send +// it away to be written to file asynchronously. +class AecDump { + public: + struct AudioProcessingState { + int delay; + int drift; + std::optional applied_input_volume; + bool keypress; + }; + + virtual ~AecDump() = default; + + // Logs Event::Type INIT message. + virtual void WriteInitMessage(const ProcessingConfig& api_format, + int64_t time_now_ms) = 0; + ABSL_DEPRECATED("") + void WriteInitMessage(const ProcessingConfig& api_format) { + WriteInitMessage(api_format, 0); + } + + // Logs Event::Type STREAM message. To log an input/output pair, + // call the AddCapture* and AddAudioProcessingState methods followed + // by a WriteCaptureStreamMessage call. + virtual void AddCaptureStreamInput( + const AudioFrameView& src) = 0; + virtual void AddCaptureStreamOutput( + const AudioFrameView& src) = 0; + virtual void AddCaptureStreamInput(const int16_t* const data, + int num_channels, + int samples_per_channel) = 0; + virtual void AddCaptureStreamOutput(const int16_t* const data, + int num_channels, + int samples_per_channel) = 0; + virtual void AddAudioProcessingState(const AudioProcessingState& state) = 0; + virtual void WriteCaptureStreamMessage() = 0; + + // Logs Event::Type REVERSE_STREAM message. + virtual void WriteRenderStreamMessage(const int16_t* const data, + int num_channels, + int samples_per_channel) = 0; + virtual void WriteRenderStreamMessage( + const AudioFrameView& src) = 0; + + virtual void WriteRuntimeSetting( + const AudioProcessing::RuntimeSetting& runtime_setting) = 0; + + // Logs Event::Type CONFIG message. + virtual void WriteConfig(const InternalAPMConfig& config) = 0; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/include/audio_frame_proxies.cc b/pkg/apm/webrtc/modules/audio_processing/include/audio_frame_proxies.cc new file mode 100644 index 00000000..e37645ef --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/include/audio_frame_proxies.cc @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/include/audio_frame_proxies.h" + +#include "api/audio/audio_frame.h" +#include "api/audio/audio_processing.h" + +namespace webrtc { + +int ProcessAudioFrame(AudioProcessing* ap, AudioFrame* frame) { + if (!frame || !ap) { + return AudioProcessing::Error::kNullPointerError; + } + + StreamConfig input_config(frame->sample_rate_hz_, frame->num_channels_); + StreamConfig output_config(frame->sample_rate_hz_, frame->num_channels_); + RTC_DCHECK_EQ(frame->samples_per_channel(), input_config.num_frames()); + + int result = ap->ProcessStream(frame->data(), input_config, output_config, + frame->mutable_data()); + + AudioProcessingStats stats = ap->GetStatistics(); + + if (stats.voice_detected) { + frame->vad_activity_ = *stats.voice_detected + ? AudioFrame::VADActivity::kVadActive + : AudioFrame::VADActivity::kVadPassive; + } + + return result; +} + +int ProcessReverseAudioFrame(AudioProcessing* ap, AudioFrame* frame) { + if (!frame || !ap) { + return AudioProcessing::Error::kNullPointerError; + } + + // Must be a native rate. + if (frame->sample_rate_hz_ != AudioProcessing::NativeRate::kSampleRate8kHz && + frame->sample_rate_hz_ != AudioProcessing::NativeRate::kSampleRate16kHz && + frame->sample_rate_hz_ != AudioProcessing::NativeRate::kSampleRate32kHz && + frame->sample_rate_hz_ != AudioProcessing::NativeRate::kSampleRate48kHz) { + return AudioProcessing::Error::kBadSampleRateError; + } + + if (frame->num_channels_ <= 0) { + return AudioProcessing::Error::kBadNumberChannelsError; + } + + StreamConfig input_config(frame->sample_rate_hz_, frame->num_channels_); + StreamConfig output_config(frame->sample_rate_hz_, frame->num_channels_); + + int result = ap->ProcessReverseStream(frame->data(), input_config, + output_config, frame->mutable_data()); + return result; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/include/audio_frame_proxies.h b/pkg/apm/webrtc/modules/audio_processing/include/audio_frame_proxies.h new file mode 100644 index 00000000..5dd111ca --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/include/audio_frame_proxies.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_PROXIES_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_PROXIES_H_ + +namespace webrtc { + +class AudioFrame; +class AudioProcessing; + +// Processes a 10 ms `frame` of the primary audio stream using the provided +// AudioProcessing object. On the client-side, this is the near-end (or +// captured) audio. The `sample_rate_hz_`, `num_channels_`, and +// `samples_per_channel_` members of `frame` must be valid. If changed from the +// previous call to this function, it will trigger an initialization of the +// provided AudioProcessing object. +// The function returns any error codes passed from the AudioProcessing +// ProcessStream method. +int ProcessAudioFrame(AudioProcessing* ap, AudioFrame* frame); + +// Processes a 10 ms `frame` of the reverse direction audio stream using the +// provided AudioProcessing object. The frame may be modified. On the +// client-side, this is the far-end (or to be rendered) audio. The +// `sample_rate_hz_`, `num_channels_`, and `samples_per_channel_` members of +// `frame` must be valid. If changed from the previous call to this function, it +// will trigger an initialization of the provided AudioProcessing object. +// The function returns any error codes passed from the AudioProcessing +// ProcessReverseStream method. +int ProcessReverseAudioFrame(AudioProcessing* ap, AudioFrame* frame); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_PROXIES_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/include/audio_frame_view.h b/pkg/apm/webrtc/modules/audio_processing/include/audio_frame_view.h new file mode 100644 index 00000000..27e20090 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/include/audio_frame_view.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_VIEW_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_VIEW_H_ + +#include "api/audio/audio_view.h" + +namespace webrtc { + +// Class to pass audio data in T** format, where T is a numeric type. +template +class AudioFrameView { + public: + // `num_channels` and `channel_size` describe the T** + // `audio_samples`. `audio_samples` is assumed to point to a + // two-dimensional |num_channels * channel_size| array of floats. + // + // Note: The implementation now only requires the first channel pointer. + // The previous implementation retained a pointer to externally owned array + // of channel pointers, but since the channel size and count are provided + // and the array is assumed to be a single two-dimensional array, the other + // channel pointers can be calculated based on that (which is what the class + // now uses `DeinterleavedView<>` internally for). + AudioFrameView(T* const* audio_samples, int num_channels, int channel_size) + : view_(num_channels && channel_size ? audio_samples[0] : nullptr, + channel_size, + num_channels) { + RTC_DCHECK_GE(view_.num_channels(), 0); + RTC_DCHECK_GE(view_.samples_per_channel(), 0); + } + + // Implicit cast to allow converting AudioFrameView to + // AudioFrameView. + template + AudioFrameView(AudioFrameView other) : view_(other.view()) {} + + // Allow constructing AudioFrameView from a DeinterleavedView. + template + explicit AudioFrameView(DeinterleavedView view) : view_(view) {} + + AudioFrameView() = delete; + + int num_channels() const { return view_.num_channels(); } + int samples_per_channel() const { return view_.samples_per_channel(); } + MonoView channel(int idx) { return view_[idx]; } + MonoView channel(int idx) const { return view_[idx]; } + MonoView operator[](int idx) { return view_[idx]; } + MonoView operator[](int idx) const { return view_[idx]; } + + DeinterleavedView view() { return view_; } + DeinterleavedView view() const { return view_; } + + private: + DeinterleavedView view_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_VIEW_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/include/audio_processing.h b/pkg/apm/webrtc/modules/audio_processing/include/audio_processing.h new file mode 100644 index 00000000..fe938f86 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/include/audio_processing.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ + +// This is a transitional header forwarding to the new version in the api/ +// folder. +#include "api/audio/audio_processing.h" + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/include/audio_processing_statistics.h b/pkg/apm/webrtc/modules/audio_processing/include/audio_processing_statistics.h new file mode 100644 index 00000000..594d3f5a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/include/audio_processing_statistics.h @@ -0,0 +1,18 @@ +/* + * 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 MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_STATISTICS_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_STATISTICS_H_ + +// This is a transitional header forwarding to the new version in the api/ +// folder. +#include "api/audio/audio_processing_statistics.h" + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_STATISTICS_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/include/include.go b/pkg/apm/webrtc/modules/audio_processing/include/include.go new file mode 100644 index 00000000..a84bc198 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/include/include.go @@ -0,0 +1,10 @@ +//go:build console + +package include + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/logging/apm_data_dumper.cc b/pkg/apm/webrtc/modules/audio_processing/logging/apm_data_dumper.cc new file mode 100644 index 00000000..ec2cc7f9 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/logging/apm_data_dumper.cc @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/logging/apm_data_dumper.h" + +#include "absl/strings/string_view.h" +#include "rtc_base/strings/string_builder.h" + +// Check to verify that the define is properly set. +#if !defined(WEBRTC_APM_DEBUG_DUMP) || \ + (WEBRTC_APM_DEBUG_DUMP != 0 && WEBRTC_APM_DEBUG_DUMP != 1) +#error "Set WEBRTC_APM_DEBUG_DUMP to either 0 or 1" +#endif + +namespace webrtc { +namespace { + +#if WEBRTC_APM_DEBUG_DUMP == 1 + +#if defined(WEBRTC_WIN) +constexpr char kPathDelimiter = '\\'; +#else +constexpr char kPathDelimiter = '/'; +#endif + +std::string FormFileName(absl::string_view output_dir, + absl::string_view name, + int instance_index, + int reinit_index, + absl::string_view suffix) { + char buf[1024]; + webrtc::SimpleStringBuilder ss(buf); + if (!output_dir.empty()) { + ss << output_dir; + if (output_dir.back() != kPathDelimiter) { + ss << kPathDelimiter; + } + } + ss << name << "_" << instance_index << "-" << reinit_index << suffix; + return ss.str(); +} +#endif + +} // namespace + +#if WEBRTC_APM_DEBUG_DUMP == 1 +ApmDataDumper::ApmDataDumper(int instance_index) + : instance_index_(instance_index) {} +#else +ApmDataDumper::ApmDataDumper(int /* instance_index */) {} +#endif + +ApmDataDumper::~ApmDataDumper() = default; + +#if WEBRTC_APM_DEBUG_DUMP == 1 +bool ApmDataDumper::recording_activated_ = false; +std::optional ApmDataDumper::dump_set_to_use_; +char ApmDataDumper::output_dir_[] = ""; + +FILE* ApmDataDumper::GetRawFile(absl::string_view name) { + std::string filename = FormFileName(output_dir_, name, instance_index_, + recording_set_index_, ".dat"); + auto& f = raw_files_[filename]; + if (!f) { + f.reset(fopen(filename.c_str(), "wb")); + RTC_CHECK(f.get()) << "Cannot write to " << filename << "."; + } + return f.get(); +} + +WavWriter* ApmDataDumper::GetWavFile(absl::string_view name, + int sample_rate_hz, + int num_channels, + WavFile::SampleFormat format) { + std::string filename = FormFileName(output_dir_, name, instance_index_, + recording_set_index_, ".wav"); + auto& f = wav_files_[filename]; + if (!f) { + f.reset( + new WavWriter(filename.c_str(), sample_rate_hz, num_channels, format)); + } + return f.get(); +} +#endif + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/logging/apm_data_dumper.h b/pkg/apm/webrtc/modules/audio_processing/logging/apm_data_dumper.h new file mode 100644 index 00000000..a8178bf1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/logging/apm_data_dumper.h @@ -0,0 +1,417 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_LOGGING_APM_DATA_DUMPER_H_ +#define MODULES_AUDIO_PROCESSING_LOGGING_APM_DATA_DUMPER_H_ + +#include +#include + +#if WEBRTC_APM_DEBUG_DUMP == 1 +#include +#include +#include +#endif + +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#if WEBRTC_APM_DEBUG_DUMP == 1 +#include "common_audio/wav_file.h" +#include "rtc_base/checks.h" +#include "rtc_base/string_utils.h" +#endif + +// Check to verify that the define is properly set. +#if !defined(WEBRTC_APM_DEBUG_DUMP) || \ + (WEBRTC_APM_DEBUG_DUMP != 0 && WEBRTC_APM_DEBUG_DUMP != 1) +#error "Set WEBRTC_APM_DEBUG_DUMP to either 0 or 1" +#endif + +namespace webrtc { + +#if WEBRTC_APM_DEBUG_DUMP == 1 +// Functor used to use as a custom deleter in the map of file pointers to raw +// files. +struct RawFileCloseFunctor { + void operator()(FILE* f) const { fclose(f); } +}; +#endif + +// Class that handles dumping of variables into files. +class ApmDataDumper { + public: + // Constructor that takes an instance index that may + // be used to distinguish data dumped from different + // instances of the code. + explicit ApmDataDumper(int instance_index); + + ApmDataDumper() = delete; + ApmDataDumper(const ApmDataDumper&) = delete; + ApmDataDumper& operator=(const ApmDataDumper&) = delete; + + ~ApmDataDumper(); + + // Activates or deactivate the dumping functionality. + static void SetActivated([[maybe_unused]] bool activated) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + recording_activated_ = activated; +#endif + } + + // Returns whether dumping functionality is enabled/available. + static bool IsAvailable() { +#if WEBRTC_APM_DEBUG_DUMP == 1 + return true; +#else + return false; +#endif + } + + // Default dump set. + static constexpr size_t kDefaultDumpSet = 0; + + // Specifies what dump set to use. All dump commands with a different dump set + // than the one specified will be discarded. If not specificed, all dump sets + // will be used. + static void SetDumpSetToUse([[maybe_unused]] int dump_set_to_use) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + dump_set_to_use_ = dump_set_to_use; +#endif + } + + // Set an optional output directory. + static void SetOutputDirectory( + [[maybe_unused]] absl::string_view output_dir) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + RTC_CHECK_LT(output_dir.size(), kOutputDirMaxLength); + webrtc::strcpyn(output_dir_, kOutputDirMaxLength, output_dir); +#endif + } + + // Reinitializes the data dumping such that new versions + // of all files being dumped to are created. + void InitiateNewSetOfRecordings() { +#if WEBRTC_APM_DEBUG_DUMP == 1 + ++recording_set_index_; +#endif + } + + // Methods for performing dumping of data of various types into + // various formats. + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] double v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(&v, sizeof(v), 1, file); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] size_t v_length, + [[maybe_unused]] const double* v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(v, sizeof(v[0]), v_length, file); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] ArrayView v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + DumpRaw(name, v.size(), v.data()); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] float v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(&v, sizeof(v), 1, file); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] size_t v_length, + [[maybe_unused]] const float* v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(v, sizeof(v[0]), v_length, file); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] ArrayView v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + DumpRaw(name, v.size(), v.data()); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] bool v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + DumpRaw(name, static_cast(v)); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] size_t v_length, + [[maybe_unused]] const bool* v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + FILE* file = GetRawFile(name); + for (size_t k = 0; k < v_length; ++k) { + int16_t value = static_cast(v[k]); + fwrite(&value, sizeof(value), 1, file); + } + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] ArrayView v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + DumpRaw(name, v.size(), v.data()); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] int16_t v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(&v, sizeof(v), 1, file); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] size_t v_length, + [[maybe_unused]] const int16_t* v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(v, sizeof(v[0]), v_length, file); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] ArrayView v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + DumpRaw(name, v.size(), v.data()); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] int32_t v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(&v, sizeof(v), 1, file); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] size_t v_length, + [[maybe_unused]] const int32_t* v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(v, sizeof(v[0]), v_length, file); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] size_t v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(&v, sizeof(v), 1, file); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] size_t v_length, + [[maybe_unused]] const size_t* v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(v, sizeof(v[0]), v_length, file); + } +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] ArrayView v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + DumpRaw(name, v.size(), v.data()); + } +#endif + } + + void DumpRaw(absl::string_view name, + ArrayView v, + int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + DumpRaw(name, v.size(), v.data()); +#endif + } + + void DumpWav([[maybe_unused]] absl::string_view name, + [[maybe_unused]] size_t v_length, + [[maybe_unused]] const float* v, + [[maybe_unused]] int sample_rate_hz, + [[maybe_unused]] int num_channels, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + WavWriter* file = GetWavFile(name, sample_rate_hz, num_channels, + WavFile::SampleFormat::kFloat); + file->WriteSamples(v, v_length); + } +#endif + } + + void DumpWav([[maybe_unused]] absl::string_view name, + [[maybe_unused]] ArrayView v, + [[maybe_unused]] int sample_rate_hz, + [[maybe_unused]] int num_channels, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) + return; + + if (recording_activated_) { + DumpWav(name, v.size(), v.data(), sample_rate_hz, num_channels); + } +#endif + } + + private: +#if WEBRTC_APM_DEBUG_DUMP == 1 + static bool recording_activated_; + static std::optional dump_set_to_use_; + static constexpr size_t kOutputDirMaxLength = 1024; + static char output_dir_[kOutputDirMaxLength]; + const int instance_index_; + int recording_set_index_ = 0; + std::unordered_map> + raw_files_; + std::unordered_map> wav_files_; + + FILE* GetRawFile(absl::string_view name); + WavWriter* GetWavFile(absl::string_view name, + int sample_rate_hz, + int num_channels, + WavFile::SampleFormat format); +#endif +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_LOGGING_APM_DATA_DUMPER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/logging/logging.go b/pkg/apm/webrtc/modules/audio_processing/logging/logging.go new file mode 100644 index 00000000..9d2b4a63 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/logging/logging.go @@ -0,0 +1,10 @@ +//go:build console + +package logging + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/fast_math.cc b/pkg/apm/webrtc/modules/audio_processing/ns/fast_math.cc new file mode 100644 index 00000000..72f6fb21 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/fast_math.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/fast_math.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +float FastLog2f(float in) { + RTC_DCHECK_GT(in, .0f); + // Read and interpret float as uint32_t and then cast to float. + // This is done to extract the exponent (bits 30 - 23). + // "Right shift" of the exponent is then performed by multiplying + // with the constant (1/2^23). Finally, we subtract a constant to + // remove the bias (https://en.wikipedia.org/wiki/Exponent_bias). + union { + float dummy; + uint32_t a; + } x = {in}; + float out = x.a; + out *= 1.1920929e-7f; // 1/2^23 + out -= 126.942695f; // Remove bias. + return out; +} + +} // namespace + +float SqrtFastApproximation(float f) { + // TODO(peah): Add fast approximate implementation. + return sqrtf(f); +} + +float Pow2Approximation(float p) { + // TODO(peah): Add fast approximate implementation. + return powf(2.f, p); +} + +float PowApproximation(float x, float p) { + return Pow2Approximation(p * FastLog2f(x)); +} + +float LogApproximation(float x) { + constexpr float kLogOf2 = 0.69314718056f; + return FastLog2f(x) * kLogOf2; +} + +void LogApproximation(ArrayView x, ArrayView y) { + for (size_t k = 0; k < x.size(); ++k) { + y[k] = LogApproximation(x[k]); + } +} + +float ExpApproximation(float x) { + constexpr float kLog10Ofe = 0.4342944819f; + return PowApproximation(10.f, x * kLog10Ofe); +} + +void ExpApproximation(ArrayView x, ArrayView y) { + for (size_t k = 0; k < x.size(); ++k) { + y[k] = ExpApproximation(x[k]); + } +} + +void ExpApproximationSignFlip(ArrayView x, ArrayView y) { + for (size_t k = 0; k < x.size(); ++k) { + y[k] = ExpApproximation(-x[k]); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/fast_math.h b/pkg/apm/webrtc/modules/audio_processing/ns/fast_math.h new file mode 100644 index 00000000..598b31cf --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/fast_math.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_FAST_MATH_H_ +#define MODULES_AUDIO_PROCESSING_NS_FAST_MATH_H_ + +#include "api/array_view.h" + +namespace webrtc { + +// Sqrt approximation. +float SqrtFastApproximation(float f); + +// Log base conversion log(x) = log2(x)/log2(e). +float LogApproximation(float x); +void LogApproximation(ArrayView x, ArrayView y); + +// 2^x approximation. +float Pow2Approximation(float p); + +// x^p approximation. +float PowApproximation(float x, float p); + +// e^x approximation. +float ExpApproximation(float x); +void ExpApproximation(ArrayView x, ArrayView y); +void ExpApproximationSignFlip(ArrayView x, ArrayView y); +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_FAST_MATH_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/histograms.cc b/pkg/apm/webrtc/modules/audio_processing/ns/histograms.cc new file mode 100644 index 00000000..1d4f4590 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/histograms.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/histograms.h" + +namespace webrtc { + +Histograms::Histograms() { + Clear(); +} + +void Histograms::Clear() { + lrt_.fill(0); + spectral_flatness_.fill(0); + spectral_diff_.fill(0); +} + +void Histograms::Update(const SignalModel& features_) { + // Update the histogram for the LRT. + constexpr float kOneByBinSizeLrt = 1.f / kBinSizeLrt; + if (features_.lrt < kHistogramSize * kBinSizeLrt && features_.lrt >= 0.f) { + ++lrt_[kOneByBinSizeLrt * features_.lrt]; + } + + // Update histogram for the spectral flatness. + constexpr float kOneByBinSizeSpecFlat = 1.f / kBinSizeSpecFlat; + if (features_.spectral_flatness < kHistogramSize * kBinSizeSpecFlat && + features_.spectral_flatness >= 0.f) { + ++spectral_flatness_[features_.spectral_flatness * kOneByBinSizeSpecFlat]; + } + + // Update histogram for the spectral difference. + constexpr float kOneByBinSizeSpecDiff = 1.f / kBinSizeSpecDiff; + if (features_.spectral_diff < kHistogramSize * kBinSizeSpecDiff && + features_.spectral_diff >= 0.f) { + ++spectral_diff_[features_.spectral_diff * kOneByBinSizeSpecDiff]; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/histograms.h b/pkg/apm/webrtc/modules/audio_processing/ns/histograms.h new file mode 100644 index 00000000..54efbe09 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/histograms.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_HISTOGRAMS_H_ +#define MODULES_AUDIO_PROCESSING_NS_HISTOGRAMS_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/signal_model.h" + +namespace webrtc { + +constexpr int kHistogramSize = 1000; + +// Class for handling the updating of histograms. +class Histograms { + public: + Histograms(); + Histograms(const Histograms&) = delete; + Histograms& operator=(const Histograms&) = delete; + + // Clears the histograms. + void Clear(); + + // Extracts thresholds for feature parameters and updates the corresponding + // histogram. + void Update(const SignalModel& features_); + + // Methods for accessing the histograms. + ArrayView get_lrt() const { return lrt_; } + ArrayView get_spectral_flatness() const { + return spectral_flatness_; + } + ArrayView get_spectral_diff() const { + return spectral_diff_; + } + + private: + std::array lrt_; + std::array spectral_flatness_; + std::array spectral_diff_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_HISTOGRAMS_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/noise_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/ns/noise_estimator.cc new file mode 100644 index 00000000..bf21d38d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/noise_estimator.cc @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/noise_estimator.h" + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/fast_math.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/suppression_params.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Log(i). +constexpr std::array log_table = { + 0.f, 0.f, 0.f, 0.f, 0.f, 1.609438f, 1.791759f, + 1.945910f, 2.079442f, 2.197225f, 2.302585f, 2.397895f, 2.484907f, 2.564949f, + 2.639057f, 2.708050f, 2.772589f, 2.833213f, 2.890372f, 2.944439f, 2.995732f, + 3.044522f, 3.091043f, 3.135494f, 3.178054f, 3.218876f, 3.258097f, 3.295837f, + 3.332205f, 3.367296f, 3.401197f, 3.433987f, 3.465736f, 3.496507f, 3.526361f, + 3.555348f, 3.583519f, 3.610918f, 3.637586f, 3.663562f, 3.688879f, 3.713572f, + 3.737669f, 3.761200f, 3.784190f, 3.806663f, 3.828641f, 3.850147f, 3.871201f, + 3.891820f, 3.912023f, 3.931826f, 3.951244f, 3.970292f, 3.988984f, 4.007333f, + 4.025352f, 4.043051f, 4.060443f, 4.077538f, 4.094345f, 4.110874f, 4.127134f, + 4.143135f, 4.158883f, 4.174387f, 4.189655f, 4.204693f, 4.219508f, 4.234107f, + 4.248495f, 4.262680f, 4.276666f, 4.290460f, 4.304065f, 4.317488f, 4.330733f, + 4.343805f, 4.356709f, 4.369448f, 4.382027f, 4.394449f, 4.406719f, 4.418841f, + 4.430817f, 4.442651f, 4.454347f, 4.465908f, 4.477337f, 4.488636f, 4.499810f, + 4.510859f, 4.521789f, 4.532599f, 4.543295f, 4.553877f, 4.564348f, 4.574711f, + 4.584968f, 4.595119f, 4.605170f, 4.615121f, 4.624973f, 4.634729f, 4.644391f, + 4.653960f, 4.663439f, 4.672829f, 4.682131f, 4.691348f, 4.700480f, 4.709530f, + 4.718499f, 4.727388f, 4.736198f, 4.744932f, 4.753591f, 4.762174f, 4.770685f, + 4.779124f, 4.787492f, 4.795791f, 4.804021f, 4.812184f, 4.820282f, 4.828314f, + 4.836282f, 4.844187f, 4.852030f}; + +} // namespace + +NoiseEstimator::NoiseEstimator(const SuppressionParams& suppression_params) + : suppression_params_(suppression_params) { + noise_spectrum_.fill(0.f); + prev_noise_spectrum_.fill(0.f); + conservative_noise_spectrum_.fill(0.f); + parametric_noise_spectrum_.fill(0.f); +} + +void NoiseEstimator::PrepareAnalysis() { + std::copy(noise_spectrum_.begin(), noise_spectrum_.end(), + prev_noise_spectrum_.begin()); +} + +void NoiseEstimator::PreUpdate( + int32_t num_analyzed_frames, + ArrayView signal_spectrum, + float signal_spectral_sum) { + quantile_noise_estimator_.Estimate(signal_spectrum, noise_spectrum_); + + if (num_analyzed_frames < kShortStartupPhaseBlocks) { + // Compute simplified noise model during startup. + const size_t kStartBand = 5; + float sum_log_i_log_magn = 0.f; + float sum_log_i = 0.f; + float sum_log_i_square = 0.f; + float sum_log_magn = 0.f; + for (size_t i = kStartBand; i < kFftSizeBy2Plus1; ++i) { + float log_i = log_table[i]; + sum_log_i += log_i; + sum_log_i_square += log_i * log_i; + float log_signal = LogApproximation(signal_spectrum[i]); + sum_log_magn += log_signal; + sum_log_i_log_magn += log_i * log_signal; + } + + // Estimate the parameter for the level of the white noise. + constexpr float kOneByFftSizeBy2Plus1 = 1.f / kFftSizeBy2Plus1; + white_noise_level_ += signal_spectral_sum * kOneByFftSizeBy2Plus1 * + suppression_params_.over_subtraction_factor; + + // Estimate pink noise parameters. + float denom = sum_log_i_square * (kFftSizeBy2Plus1 - kStartBand) - + sum_log_i * sum_log_i; + float num = + sum_log_i_square * sum_log_magn - sum_log_i * sum_log_i_log_magn; + RTC_DCHECK_NE(denom, 0.f); + float pink_noise_adjustment = num / denom; + + // Constrain the estimated spectrum to be positive. + pink_noise_adjustment = std::max(pink_noise_adjustment, 0.f); + pink_noise_numerator_ += pink_noise_adjustment; + num = sum_log_i * sum_log_magn - + (kFftSizeBy2Plus1 - kStartBand) * sum_log_i_log_magn; + RTC_DCHECK_NE(denom, 0.f); + pink_noise_adjustment = num / denom; + + // Constrain the pink noise power to be in the interval [0, 1]. + pink_noise_adjustment = std::max(std::min(pink_noise_adjustment, 1.f), 0.f); + + pink_noise_exp_ += pink_noise_adjustment; + + const float one_by_num_analyzed_frames_plus_1 = + 1.f / (num_analyzed_frames + 1.f); + + // Calculate the frequency-independent parts of parametric noise estimate. + float parametric_exp = 0.f; + float parametric_num = 0.f; + if (pink_noise_exp_ > 0.f) { + // Use pink noise estimate. + parametric_num = ExpApproximation(pink_noise_numerator_ * + one_by_num_analyzed_frames_plus_1); + parametric_num *= num_analyzed_frames + 1.f; + parametric_exp = pink_noise_exp_ * one_by_num_analyzed_frames_plus_1; + } + + constexpr float kOneByShortStartupPhaseBlocks = + 1.f / kShortStartupPhaseBlocks; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + // Estimate the background noise using the white and pink noise + // parameters. + if (pink_noise_exp_ == 0.f) { + // Use white noise estimate. + parametric_noise_spectrum_[i] = white_noise_level_; + } else { + // Use pink noise estimate. + float use_band = i < kStartBand ? kStartBand : i; + float parametric_denom = PowApproximation(use_band, parametric_exp); + RTC_DCHECK_NE(parametric_denom, 0.f); + parametric_noise_spectrum_[i] = parametric_num / parametric_denom; + } + } + + // Weight quantile noise with modeled noise. + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + noise_spectrum_[i] *= num_analyzed_frames; + float tmp = parametric_noise_spectrum_[i] * + (kShortStartupPhaseBlocks - num_analyzed_frames); + noise_spectrum_[i] += tmp * one_by_num_analyzed_frames_plus_1; + noise_spectrum_[i] *= kOneByShortStartupPhaseBlocks; + } + } +} + +void NoiseEstimator::PostUpdate( + ArrayView speech_probability, + ArrayView signal_spectrum) { + // Time-avg parameter for noise_spectrum update. + constexpr float kNoiseUpdate = 0.9f; + + float gamma = kNoiseUpdate; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + const float prob_speech = speech_probability[i]; + const float prob_non_speech = 1.f - prob_speech; + + // Temporary noise update used for speech frames if update value is less + // than previous. + float noise_update_tmp = + gamma * prev_noise_spectrum_[i] + + (1.f - gamma) * (prob_non_speech * signal_spectrum[i] + + prob_speech * prev_noise_spectrum_[i]); + + // Time-constant based on speech/noise_spectrum state. + float gamma_old = gamma; + + // Increase gamma for frame likely to be seech. + constexpr float kProbRange = .2f; + gamma = prob_speech > kProbRange ? .99f : kNoiseUpdate; + + // Conservative noise_spectrum update. + if (prob_speech < kProbRange) { + conservative_noise_spectrum_[i] += + 0.05f * (signal_spectrum[i] - conservative_noise_spectrum_[i]); + } + + // Noise_spectrum update. + if (gamma == gamma_old) { + noise_spectrum_[i] = noise_update_tmp; + } else { + noise_spectrum_[i] = + gamma * prev_noise_spectrum_[i] + + (1.f - gamma) * (prob_non_speech * signal_spectrum[i] + + prob_speech * prev_noise_spectrum_[i]); + // Allow for noise_spectrum update downwards: If noise_spectrum update + // decreases the noise_spectrum, it is safe, so allow it to happen. + noise_spectrum_[i] = std::min(noise_spectrum_[i], noise_update_tmp); + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/noise_estimator.h b/pkg/apm/webrtc/modules/audio_processing/ns/noise_estimator.h new file mode 100644 index 00000000..124ab930 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/noise_estimator.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_NOISE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_NOISE_ESTIMATOR_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/quantile_noise_estimator.h" +#include "modules/audio_processing/ns/suppression_params.h" + +namespace webrtc { + +// Class for estimating the spectral characteristics of the noise in an incoming +// signal. +class NoiseEstimator { + public: + explicit NoiseEstimator(const SuppressionParams& suppression_params); + + // Prepare the estimator for analysis of a new frame. + void PrepareAnalysis(); + + // Performs the first step of the estimator update. + void PreUpdate(int32_t num_analyzed_frames, + ArrayView signal_spectrum, + float signal_spectral_sum); + + // Performs the second step of the estimator update. + void PostUpdate(ArrayView speech_probability, + ArrayView signal_spectrum); + + // Returns the noise spectral estimate. + ArrayView get_noise_spectrum() const { + return noise_spectrum_; + } + + // Returns the noise from the previous frame. + ArrayView get_prev_noise_spectrum() const { + return prev_noise_spectrum_; + } + + // Returns a noise spectral estimate based on white and pink noise parameters. + ArrayView get_parametric_noise_spectrum() + const { + return parametric_noise_spectrum_; + } + ArrayView get_conservative_noise_spectrum() + const { + return conservative_noise_spectrum_; + } + + private: + const SuppressionParams& suppression_params_; + float white_noise_level_ = 0.f; + float pink_noise_numerator_ = 0.f; + float pink_noise_exp_ = 0.f; + std::array prev_noise_spectrum_; + std::array conservative_noise_spectrum_; + std::array parametric_noise_spectrum_; + std::array noise_spectrum_; + QuantileNoiseEstimator quantile_noise_estimator_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_NOISE_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/noise_suppressor.cc b/pkg/apm/webrtc/modules/audio_processing/ns/noise_suppressor.cc new file mode 100644 index 00000000..070d046c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/noise_suppressor.cc @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/noise_suppressor.h" + +#include +#include +#include + +#include + +#include "modules/audio_processing/ns/fast_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Maps sample rate to number of bands. +size_t NumBandsForRate(size_t sample_rate_hz) { + RTC_DCHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000 || + sample_rate_hz == 48000); + return sample_rate_hz / 16000; +} + +// Maximum number of channels for which the channel data is stored on +// the stack. If the number of channels are larger than this, they are stored +// using scratch memory that is pre-allocated on the heap. The reason for this +// partitioning is not to waste heap space for handling the more common numbers +// of channels, while at the same time not limiting the support for higher +// numbers of channels by enforcing the channel data to be stored on the +// stack using a fixed maximum value. +constexpr size_t kMaxNumChannelsOnStack = 2; + +// Chooses the number of channels to store on the heap when that is required due +// to the number of channels being larger than the pre-defined number +// of channels to store on the stack. +size_t NumChannelsOnHeap(size_t num_channels) { + return num_channels > kMaxNumChannelsOnStack ? num_channels : 0; +} + +// Hybrib Hanning and flat window for the filterbank. +constexpr std::array kBlocks160w256FirstHalf = { + 0.00000000f, 0.01636173f, 0.03271908f, 0.04906767f, 0.06540313f, + 0.08172107f, 0.09801714f, 0.11428696f, 0.13052619f, 0.14673047f, + 0.16289547f, 0.17901686f, 0.19509032f, 0.21111155f, 0.22707626f, + 0.24298018f, 0.25881905f, 0.27458862f, 0.29028468f, 0.30590302f, + 0.32143947f, 0.33688985f, 0.35225005f, 0.36751594f, 0.38268343f, + 0.39774847f, 0.41270703f, 0.42755509f, 0.44228869f, 0.45690388f, + 0.47139674f, 0.48576339f, 0.50000000f, 0.51410274f, 0.52806785f, + 0.54189158f, 0.55557023f, 0.56910015f, 0.58247770f, 0.59569930f, + 0.60876143f, 0.62166057f, 0.63439328f, 0.64695615f, 0.65934582f, + 0.67155895f, 0.68359230f, 0.69544264f, 0.70710678f, 0.71858162f, + 0.72986407f, 0.74095113f, 0.75183981f, 0.76252720f, 0.77301045f, + 0.78328675f, 0.79335334f, 0.80320753f, 0.81284668f, 0.82226822f, + 0.83146961f, 0.84044840f, 0.84920218f, 0.85772861f, 0.86602540f, + 0.87409034f, 0.88192126f, 0.88951608f, 0.89687274f, 0.90398929f, + 0.91086382f, 0.91749450f, 0.92387953f, 0.93001722f, 0.93590593f, + 0.94154407f, 0.94693013f, 0.95206268f, 0.95694034f, 0.96156180f, + 0.96592583f, 0.97003125f, 0.97387698f, 0.97746197f, 0.98078528f, + 0.98384601f, 0.98664333f, 0.98917651f, 0.99144486f, 0.99344778f, + 0.99518473f, 0.99665524f, 0.99785892f, 0.99879546f, 0.99946459f, + 0.99986614f}; + +// Applies the filterbank window to a buffer. +void ApplyFilterBankWindow(ArrayView x) { + for (size_t i = 0; i < 96; ++i) { + x[i] = kBlocks160w256FirstHalf[i] * x[i]; + } + + for (size_t i = 161, k = 95; i < kFftSize; ++i, --k) { + RTC_DCHECK_NE(0, k); + x[i] = kBlocks160w256FirstHalf[k] * x[i]; + } +} + +// Extends a frame with previous data. +void FormExtendedFrame(ArrayView frame, + ArrayView old_data, + ArrayView extended_frame) { + std::copy(old_data.begin(), old_data.end(), extended_frame.begin()); + std::copy(frame.begin(), frame.end(), + extended_frame.begin() + old_data.size()); + std::copy(extended_frame.end() - old_data.size(), extended_frame.end(), + old_data.begin()); +} + +// Uses overlap-and-add to produce an output frame. +void OverlapAndAdd(ArrayView extended_frame, + ArrayView overlap_memory, + ArrayView output_frame) { + for (size_t i = 0; i < kOverlapSize; ++i) { + output_frame[i] = overlap_memory[i] + extended_frame[i]; + } + std::copy(extended_frame.begin() + kOverlapSize, + extended_frame.begin() + kNsFrameSize, + output_frame.begin() + kOverlapSize); + std::copy(extended_frame.begin() + kNsFrameSize, extended_frame.end(), + overlap_memory.begin()); +} + +// Produces a delayed frame. +void DelaySignal(ArrayView frame, + ArrayView delay_buffer, + ArrayView delayed_frame) { + constexpr size_t kSamplesFromFrame = kNsFrameSize - (kFftSize - kNsFrameSize); + std::copy(delay_buffer.begin(), delay_buffer.end(), delayed_frame.begin()); + std::copy(frame.begin(), frame.begin() + kSamplesFromFrame, + delayed_frame.begin() + delay_buffer.size()); + + std::copy(frame.begin() + kSamplesFromFrame, frame.end(), + delay_buffer.begin()); +} + +// Computes the energy of an extended frame. +float ComputeEnergyOfExtendedFrame(ArrayView x) { + float energy = 0.f; + for (float x_k : x) { + energy += x_k * x_k; + } + + return energy; +} + +// Computes the energy of an extended frame based on its subcomponents. +float ComputeEnergyOfExtendedFrame( + ArrayView frame, + ArrayView old_data) { + float energy = 0.f; + for (float v : old_data) { + energy += v * v; + } + for (float v : frame) { + energy += v * v; + } + + return energy; +} + +// Computes the magnitude spectrum based on an FFT output. +void ComputeMagnitudeSpectrum( + ArrayView real, + ArrayView imag, + ArrayView signal_spectrum) { + signal_spectrum[0] = fabsf(real[0]) + 1.f; + signal_spectrum[kFftSizeBy2Plus1 - 1] = + fabsf(real[kFftSizeBy2Plus1 - 1]) + 1.f; + + for (size_t i = 1; i < kFftSizeBy2Plus1 - 1; ++i) { + signal_spectrum[i] = + SqrtFastApproximation(real[i] * real[i] + imag[i] * imag[i]) + 1.f; + } +} + +// Compute prior and post SNR. +void ComputeSnr(ArrayView filter, + ArrayView prev_signal_spectrum, + ArrayView signal_spectrum, + ArrayView prev_noise_spectrum, + ArrayView noise_spectrum, + ArrayView prior_snr, + ArrayView post_snr) { + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + // Previous post SNR. + // Previous estimate: based on previous frame with gain filter. + float prev_estimate = prev_signal_spectrum[i] / + (prev_noise_spectrum[i] + 0.0001f) * filter[i]; + // Post SNR. + if (signal_spectrum[i] > noise_spectrum[i]) { + post_snr[i] = signal_spectrum[i] / (noise_spectrum[i] + 0.0001f) - 1.f; + } else { + post_snr[i] = 0.f; + } + // The directed decision estimate of the prior SNR is a sum the current and + // previous estimates. + prior_snr[i] = 0.98f * prev_estimate + (1.f - 0.98f) * post_snr[i]; + } +} + +// Computes the attenuating gain for the noise suppression of the upper bands. +float ComputeUpperBandsGain( + float minimum_attenuating_gain, + ArrayView filter, + ArrayView speech_probability, + ArrayView prev_analysis_signal_spectrum, + ArrayView signal_spectrum) { + // Average speech prob and filter gain for the end of the lowest band. + constexpr int kNumAvgBins = 32; + constexpr float kOneByNumAvgBins = 1.f / kNumAvgBins; + + float avg_prob_speech = 0.f; + float avg_filter_gain = 0.f; + for (size_t i = kFftSizeBy2Plus1 - kNumAvgBins - 1; i < kFftSizeBy2Plus1 - 1; + i++) { + avg_prob_speech += speech_probability[i]; + avg_filter_gain += filter[i]; + } + avg_prob_speech = avg_prob_speech * kOneByNumAvgBins; + avg_filter_gain = avg_filter_gain * kOneByNumAvgBins; + + // If the speech was suppressed by a component between Analyze and Process, an + // example being by an AEC, it should not be considered speech for the purpose + // of high band suppression. To that end, the speech probability is scaled + // accordingly. + float sum_analysis_spectrum = 0.f; + float sum_processing_spectrum = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + sum_analysis_spectrum += prev_analysis_signal_spectrum[i]; + sum_processing_spectrum += signal_spectrum[i]; + } + + // The magnitude spectrum computation enforces the spectrum to be strictly + // positive. + RTC_DCHECK_GT(sum_analysis_spectrum, 0.f); + avg_prob_speech *= sum_processing_spectrum / sum_analysis_spectrum; + + // Compute gain based on speech probability. + float gain = + 0.5f * (1.f + static_cast(tanh(2.f * avg_prob_speech - 1.f))); + + // Combine gain with low band gain. + if (avg_prob_speech >= 0.5f) { + gain = 0.25f * gain + 0.75f * avg_filter_gain; + } else { + gain = 0.5f * gain + 0.5f * avg_filter_gain; + } + + // Make sure gain is within flooring range. + return std::min(std::max(gain, minimum_attenuating_gain), 1.f); +} + +} // namespace + +NoiseSuppressor::ChannelState::ChannelState( + const SuppressionParams& suppression_params, + size_t num_bands) + : wiener_filter(suppression_params), + noise_estimator(suppression_params), + process_delay_memory(num_bands > 1 ? num_bands - 1 : 0) { + analyze_analysis_memory.fill(0.f); + prev_analysis_signal_spectrum.fill(1.f); + process_analysis_memory.fill(0.f); + process_synthesis_memory.fill(0.f); + for (auto& d : process_delay_memory) { + d.fill(0.f); + } +} + +NoiseSuppressor::NoiseSuppressor(const NsConfig& config, + size_t sample_rate_hz, + size_t num_channels) + : num_bands_(NumBandsForRate(sample_rate_hz)), + num_channels_(num_channels), + suppression_params_(config.target_level), + filter_bank_states_heap_(NumChannelsOnHeap(num_channels_)), + upper_band_gains_heap_(NumChannelsOnHeap(num_channels_)), + energies_before_filtering_heap_(NumChannelsOnHeap(num_channels_)), + gain_adjustments_heap_(NumChannelsOnHeap(num_channels_)), + channels_(num_channels_) { + for (size_t ch = 0; ch < num_channels_; ++ch) { + channels_[ch] = + std::make_unique(suppression_params_, num_bands_); + } +} + +void NoiseSuppressor::AggregateWienerFilters( + ArrayView filter) const { + ArrayView filter0 = + channels_[0]->wiener_filter.get_filter(); + std::copy(filter0.begin(), filter0.end(), filter.begin()); + + for (size_t ch = 1; ch < num_channels_; ++ch) { + ArrayView filter_ch = + channels_[ch]->wiener_filter.get_filter(); + + for (size_t k = 0; k < kFftSizeBy2Plus1; ++k) { + filter[k] = std::min(filter[k], filter_ch[k]); + } + } +} + +void NoiseSuppressor::Analyze(const AudioBuffer& audio) { + // Prepare the noise estimator for the analysis stage. + for (size_t ch = 0; ch < num_channels_; ++ch) { + channels_[ch]->noise_estimator.PrepareAnalysis(); + } + + // Check for zero frames. + bool zero_frame = true; + for (size_t ch = 0; ch < num_channels_; ++ch) { + ArrayView y_band0( + &audio.split_bands_const(ch)[0][0], kNsFrameSize); + float energy = ComputeEnergyOfExtendedFrame( + y_band0, channels_[ch]->analyze_analysis_memory); + if (energy > 0.f) { + zero_frame = false; + break; + } + } + + if (zero_frame) { + // We want to avoid updating statistics in this case: + // Updating feature statistics when we have zeros only will cause + // thresholds to move towards zero signal situations. This in turn has the + // effect that once the signal is "turned on" (non-zero values) everything + // will be treated as speech and there is no noise suppression effect. + // Depending on the duration of the inactive signal it takes a + // considerable amount of time for the system to learn what is noise and + // what is speech. + return; + } + + // Only update analysis counter for frames that are properly analyzed. + if (++num_analyzed_frames_ < 0) { + num_analyzed_frames_ = 0; + } + + // Analyze all channels. + for (size_t ch = 0; ch < num_channels_; ++ch) { + std::unique_ptr& ch_p = channels_[ch]; + ArrayView y_band0( + &audio.split_bands_const(ch)[0][0], kNsFrameSize); + + // Form an extended frame and apply analysis filter bank windowing. + std::array extended_frame; + FormExtendedFrame(y_band0, ch_p->analyze_analysis_memory, extended_frame); + ApplyFilterBankWindow(extended_frame); + + // Compute the magnitude spectrum. + std::array real; + std::array imag; + fft_.Fft(extended_frame, real, imag); + + std::array signal_spectrum; + ComputeMagnitudeSpectrum(real, imag, signal_spectrum); + + // Compute energies. + float signal_energy = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + signal_energy += real[i] * real[i] + imag[i] * imag[i]; + } + signal_energy /= kFftSizeBy2Plus1; + + float signal_spectral_sum = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + signal_spectral_sum += signal_spectrum[i]; + } + + // Estimate the noise spectra and the probability estimates of speech + // presence. + ch_p->noise_estimator.PreUpdate(num_analyzed_frames_, signal_spectrum, + signal_spectral_sum); + + std::array post_snr; + std::array prior_snr; + ComputeSnr(ch_p->wiener_filter.get_filter(), + ch_p->prev_analysis_signal_spectrum, signal_spectrum, + ch_p->noise_estimator.get_prev_noise_spectrum(), + ch_p->noise_estimator.get_noise_spectrum(), prior_snr, post_snr); + + ch_p->speech_probability_estimator.Update( + num_analyzed_frames_, prior_snr, post_snr, + ch_p->noise_estimator.get_conservative_noise_spectrum(), + signal_spectrum, signal_spectral_sum, signal_energy); + + ch_p->noise_estimator.PostUpdate( + ch_p->speech_probability_estimator.get_probability(), signal_spectrum); + + // Store the magnitude spectrum to make it avalilable for the process + // method. + std::copy(signal_spectrum.begin(), signal_spectrum.end(), + ch_p->prev_analysis_signal_spectrum.begin()); + } +} + +void NoiseSuppressor::Process(AudioBuffer* audio) { + // Select the space for storing data during the processing. + std::array filter_bank_states_stack; + ArrayView filter_bank_states(filter_bank_states_stack.data(), + num_channels_); + std::array upper_band_gains_stack; + ArrayView upper_band_gains(upper_band_gains_stack.data(), + num_channels_); + std::array energies_before_filtering_stack; + ArrayView energies_before_filtering( + energies_before_filtering_stack.data(), num_channels_); + std::array gain_adjustments_stack; + ArrayView gain_adjustments(gain_adjustments_stack.data(), + num_channels_); + if (NumChannelsOnHeap(num_channels_) > 0) { + // If the stack-allocated space is too small, use the heap for storing the + // data. + filter_bank_states = ArrayView( + filter_bank_states_heap_.data(), num_channels_); + upper_band_gains = + ArrayView(upper_band_gains_heap_.data(), num_channels_); + energies_before_filtering = + ArrayView(energies_before_filtering_heap_.data(), num_channels_); + gain_adjustments = + ArrayView(gain_adjustments_heap_.data(), num_channels_); + } + + // Compute the suppression filters for all channels. + for (size_t ch = 0; ch < num_channels_; ++ch) { + // Form an extended frame and apply analysis filter bank windowing. + ArrayView y_band0(&audio->split_bands(ch)[0][0], + kNsFrameSize); + + FormExtendedFrame(y_band0, channels_[ch]->process_analysis_memory, + filter_bank_states[ch].extended_frame); + + ApplyFilterBankWindow(filter_bank_states[ch].extended_frame); + + energies_before_filtering[ch] = + ComputeEnergyOfExtendedFrame(filter_bank_states[ch].extended_frame); + + // Perform filter bank analysis and compute the magnitude spectrum. + fft_.Fft(filter_bank_states[ch].extended_frame, filter_bank_states[ch].real, + filter_bank_states[ch].imag); + + std::array signal_spectrum; + ComputeMagnitudeSpectrum(filter_bank_states[ch].real, + filter_bank_states[ch].imag, signal_spectrum); + + // Compute the frequency domain gain filter for noise attenuation. + channels_[ch]->wiener_filter.Update( + num_analyzed_frames_, + channels_[ch]->noise_estimator.get_noise_spectrum(), + channels_[ch]->noise_estimator.get_prev_noise_spectrum(), + channels_[ch]->noise_estimator.get_parametric_noise_spectrum(), + signal_spectrum); + + if (num_bands_ > 1) { + // Compute the time-domain gain for attenuating the noise in the upper + // bands. + + upper_band_gains[ch] = ComputeUpperBandsGain( + suppression_params_.minimum_attenuating_gain, + channels_[ch]->wiener_filter.get_filter(), + channels_[ch]->speech_probability_estimator.get_probability(), + channels_[ch]->prev_analysis_signal_spectrum, signal_spectrum); + } + } + + // Only do the below processing if the output of the audio processing module + // is used. + if (!capture_output_used_) { + return; + } + + // Aggregate the Wiener filters for all channels. + std::array filter_data; + ArrayView filter = filter_data; + if (num_channels_ == 1) { + filter = channels_[0]->wiener_filter.get_filter(); + } else { + AggregateWienerFilters(filter_data); + } + + for (size_t ch = 0; ch < num_channels_; ++ch) { + // Apply the filter to the lower band. + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + filter_bank_states[ch].real[i] *= filter[i]; + filter_bank_states[ch].imag[i] *= filter[i]; + } + } + + // Perform filter bank synthesis + for (size_t ch = 0; ch < num_channels_; ++ch) { + fft_.Ifft(filter_bank_states[ch].real, filter_bank_states[ch].imag, + filter_bank_states[ch].extended_frame); + } + + for (size_t ch = 0; ch < num_channels_; ++ch) { + const float energy_after_filtering = + ComputeEnergyOfExtendedFrame(filter_bank_states[ch].extended_frame); + + // Apply synthesis window. + ApplyFilterBankWindow(filter_bank_states[ch].extended_frame); + + // Compute the adjustment of the noise attenuation filter based on the + // effect of the attenuation. + gain_adjustments[ch] = + channels_[ch]->wiener_filter.ComputeOverallScalingFactor( + num_analyzed_frames_, + channels_[ch]->speech_probability_estimator.get_prior_probability(), + energies_before_filtering[ch], energy_after_filtering); + } + + // Select and apply adjustment of the noise attenuation filter based on the + // effect of the attenuation. + float gain_adjustment = gain_adjustments[0]; + for (size_t ch = 1; ch < num_channels_; ++ch) { + gain_adjustment = std::min(gain_adjustment, gain_adjustments[ch]); + } + for (size_t ch = 0; ch < num_channels_; ++ch) { + for (size_t i = 0; i < kFftSize; ++i) { + filter_bank_states[ch].extended_frame[i] = + gain_adjustment * filter_bank_states[ch].extended_frame[i]; + } + } + + // Use overlap-and-add to form the output frame of the lowest band. + for (size_t ch = 0; ch < num_channels_; ++ch) { + ArrayView y_band0(&audio->split_bands(ch)[0][0], + kNsFrameSize); + OverlapAndAdd(filter_bank_states[ch].extended_frame, + channels_[ch]->process_synthesis_memory, y_band0); + } + + if (num_bands_ > 1) { + // Select the noise attenuating gain to apply to the upper band. + float upper_band_gain = upper_band_gains[0]; + for (size_t ch = 1; ch < num_channels_; ++ch) { + upper_band_gain = std::min(upper_band_gain, upper_band_gains[ch]); + } + + // Process the upper bands. + for (size_t ch = 0; ch < num_channels_; ++ch) { + for (size_t b = 1; b < num_bands_; ++b) { + // Delay the upper bands to match the delay of the filterbank applied to + // the lowest band. + ArrayView y_band(&audio->split_bands(ch)[b][0], + kNsFrameSize); + std::array delayed_frame; + DelaySignal(y_band, channels_[ch]->process_delay_memory[b - 1], + delayed_frame); + + // Apply the time-domain noise-attenuating gain. + for (size_t j = 0; j < kNsFrameSize; j++) { + y_band[j] = upper_band_gain * delayed_frame[j]; + } + } + } + } + + // Limit the output the allowed range. + for (size_t ch = 0; ch < num_channels_; ++ch) { + for (size_t b = 0; b < num_bands_; ++b) { + ArrayView y_band(&audio->split_bands(ch)[b][0], + kNsFrameSize); + for (size_t j = 0; j < kNsFrameSize; j++) { + y_band[j] = std::min(std::max(y_band[j], -32768.f), 32767.f); + } + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/noise_suppressor.h b/pkg/apm/webrtc/modules/audio_processing/ns/noise_suppressor.h new file mode 100644 index 00000000..8ca0735f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/noise_suppressor.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_NOISE_SUPPRESSOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_NOISE_SUPPRESSOR_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/ns/noise_estimator.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/ns_config.h" +#include "modules/audio_processing/ns/ns_fft.h" +#include "modules/audio_processing/ns/speech_probability_estimator.h" +#include "modules/audio_processing/ns/wiener_filter.h" + +namespace webrtc { + +// Class for suppressing noise in a signal. +class NoiseSuppressor { + public: + NoiseSuppressor(const NsConfig& config, + size_t sample_rate_hz, + size_t num_channels); + NoiseSuppressor(const NoiseSuppressor&) = delete; + NoiseSuppressor& operator=(const NoiseSuppressor&) = delete; + + // Analyses the signal (typically applied before the AEC to avoid analyzing + // any comfort noise signal). + void Analyze(const AudioBuffer& audio); + + // Applies noise suppression. + void Process(AudioBuffer* audio); + + // Specifies whether the capture output will be used. The purpose of this is + // to allow the noise suppressor to deactivate some of the processing when the + // resulting output is anyway not used, for instance when the endpoint is + // muted. + void SetCaptureOutputUsage(bool capture_output_used) { + capture_output_used_ = capture_output_used; + } + + private: + const size_t num_bands_; + const size_t num_channels_; + const SuppressionParams suppression_params_; + int32_t num_analyzed_frames_ = -1; + NrFft fft_; + bool capture_output_used_ = true; + + struct ChannelState { + ChannelState(const SuppressionParams& suppression_params, size_t num_bands); + + SpeechProbabilityEstimator speech_probability_estimator; + WienerFilter wiener_filter; + NoiseEstimator noise_estimator; + std::array prev_analysis_signal_spectrum; + std::array analyze_analysis_memory; + std::array process_analysis_memory; + std::array process_synthesis_memory; + std::vector> process_delay_memory; + }; + + struct FilterBankState { + std::array real; + std::array imag; + std::array extended_frame; + }; + + std::vector filter_bank_states_heap_; + std::vector upper_band_gains_heap_; + std::vector energies_before_filtering_heap_; + std::vector gain_adjustments_heap_; + std::vector> channels_; + + // Aggregates the Wiener filters into a single filter to use. + void AggregateWienerFilters(ArrayView filter) const; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_NOISE_SUPPRESSOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/ns.go b/pkg/apm/webrtc/modules/audio_processing/ns/ns.go new file mode 100644 index 00000000..25631999 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/ns.go @@ -0,0 +1,10 @@ +//go:build console + +package ns + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/ns_common.h b/pkg/apm/webrtc/modules/audio_processing/ns/ns_common.h new file mode 100644 index 00000000..d6149f72 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/ns_common.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_NS_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_NS_NS_COMMON_H_ + +#include + +namespace webrtc { + +constexpr size_t kFftSize = 256; +constexpr size_t kFftSizeBy2Plus1 = kFftSize / 2 + 1; +constexpr size_t kNsFrameSize = 160; +constexpr size_t kOverlapSize = kFftSize - kNsFrameSize; + +constexpr int kShortStartupPhaseBlocks = 50; +constexpr int kLongStartupPhaseBlocks = 200; +constexpr int kFeatureUpdateWindowSize = 500; + +constexpr float kLtrFeatureThr = 0.5f; +constexpr float kBinSizeLrt = 0.1f; +constexpr float kBinSizeSpecFlat = 0.05f; +constexpr float kBinSizeSpecDiff = 0.1f; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_NS_COMMON_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/ns_config.h b/pkg/apm/webrtc/modules/audio_processing/ns/ns_config.h new file mode 100644 index 00000000..0a285e9c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/ns_config.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_NS_CONFIG_H_ +#define MODULES_AUDIO_PROCESSING_NS_NS_CONFIG_H_ + +namespace webrtc { + +// Config struct for the noise suppressor +struct NsConfig { + enum class SuppressionLevel { k6dB, k12dB, k18dB, k21dB }; + SuppressionLevel target_level = SuppressionLevel::k12dB; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_NS_CONFIG_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/ns_fft.cc b/pkg/apm/webrtc/modules/audio_processing/ns/ns_fft.cc new file mode 100644 index 00000000..bdbda8e1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/ns_fft.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/ns_fft.h" + +#include "common_audio/third_party/ooura/fft_size_256/fft4g.h" + +namespace webrtc { + +NrFft::NrFft() : bit_reversal_state_(kFftSize / 2), tables_(kFftSize / 2) { + // Initialize WebRtc_rdt (setting (bit_reversal_state_[0] to 0 triggers + // initialization) + bit_reversal_state_[0] = 0.f; + std::array tmp_buffer; + tmp_buffer.fill(0.f); + WebRtc_rdft(kFftSize, 1, tmp_buffer.data(), bit_reversal_state_.data(), + tables_.data()); +} + +void NrFft::Fft(ArrayView time_data, + ArrayView real, + ArrayView imag) { + WebRtc_rdft(kFftSize, 1, time_data.data(), bit_reversal_state_.data(), + tables_.data()); + + imag[0] = 0; + real[0] = time_data[0]; + + imag[kFftSizeBy2Plus1 - 1] = 0; + real[kFftSizeBy2Plus1 - 1] = time_data[1]; + + for (size_t i = 1; i < kFftSizeBy2Plus1 - 1; ++i) { + real[i] = time_data[2 * i]; + imag[i] = time_data[2 * i + 1]; + } +} + +void NrFft::Ifft(ArrayView real, + ArrayView imag, + ArrayView time_data) { + time_data[0] = real[0]; + time_data[1] = real[kFftSizeBy2Plus1 - 1]; + for (size_t i = 1; i < kFftSizeBy2Plus1 - 1; ++i) { + time_data[2 * i] = real[i]; + time_data[2 * i + 1] = imag[i]; + } + WebRtc_rdft(kFftSize, -1, time_data.data(), bit_reversal_state_.data(), + tables_.data()); + + // Scale the output + constexpr float kScaling = 2.f / kFftSize; + for (float& d : time_data) { + d *= kScaling; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/ns_fft.h b/pkg/apm/webrtc/modules/audio_processing/ns/ns_fft.h new file mode 100644 index 00000000..8cba188f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/ns_fft.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_NS_FFT_H_ +#define MODULES_AUDIO_PROCESSING_NS_NS_FFT_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" + +namespace webrtc { + +// Wrapper class providing 256 point FFT functionality. +class NrFft { + public: + NrFft(); + NrFft(const NrFft&) = delete; + NrFft& operator=(const NrFft&) = delete; + + // Transforms the signal from time to frequency domain. + void Fft(ArrayView time_data, + ArrayView real, + ArrayView imag); + + // Transforms the signal from frequency to time domain. + void Ifft(ArrayView real, + ArrayView imag, + ArrayView time_data); + + private: + std::vector bit_reversal_state_; + std::vector tables_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_NS_FFT_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model.cc b/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model.cc new file mode 100644 index 00000000..f25a1e20 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model.cc @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/prior_signal_model.h" + +namespace webrtc { + +PriorSignalModel::PriorSignalModel(float lrt_initial_value) + : lrt(lrt_initial_value) {} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model.h b/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model.h new file mode 100644 index 00000000..dcfa7ea7 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_H_ +#define MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_H_ + +namespace webrtc { + +// Struct for storing the prior signal model parameters. +struct PriorSignalModel { + explicit PriorSignalModel(float lrt_initial_value); + PriorSignalModel(const PriorSignalModel&) = delete; + PriorSignalModel& operator=(const PriorSignalModel&) = delete; + + float lrt; + float flatness_threshold = .5f; + float template_diff_threshold = .5f; + float lrt_weighting = 1.f; + float flatness_weighting = 0.f; + float difference_weighting = 0.f; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.cc new file mode 100644 index 00000000..3a03dc9f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.cc @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/prior_signal_model_estimator.h" + +#include + +#include + +#include "modules/audio_processing/ns/fast_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Identifies the first of the two largest peaks in the histogram. +void FindFirstOfTwoLargestPeaks( + float bin_size, + ArrayView spectral_flatness, + float* peak_position, + int* peak_weight) { + RTC_DCHECK(peak_position); + RTC_DCHECK(peak_weight); + + int peak_value = 0; + int secondary_peak_value = 0; + *peak_position = 0.f; + float secondary_peak_position = 0.f; + *peak_weight = 0; + int secondary_peak_weight = 0; + + // Identify the two largest peaks. + for (int i = 0; i < kHistogramSize; ++i) { + const float bin_mid = (i + 0.5f) * bin_size; + if (spectral_flatness[i] > peak_value) { + // Found new "first" peak candidate. + secondary_peak_value = peak_value; + secondary_peak_weight = *peak_weight; + secondary_peak_position = *peak_position; + + peak_value = spectral_flatness[i]; + *peak_weight = spectral_flatness[i]; + *peak_position = bin_mid; + } else if (spectral_flatness[i] > secondary_peak_value) { + // Found new "second" peak candidate. + secondary_peak_value = spectral_flatness[i]; + secondary_peak_weight = spectral_flatness[i]; + secondary_peak_position = bin_mid; + } + } + + // Merge the peaks if they are close. + if ((fabs(secondary_peak_position - *peak_position) < 2 * bin_size) && + (secondary_peak_weight > 0.5f * (*peak_weight))) { + *peak_weight += secondary_peak_weight; + *peak_position = 0.5f * (*peak_position + secondary_peak_position); + } +} + +void UpdateLrt(ArrayView lrt_histogram, + float* prior_model_lrt, + bool* low_lrt_fluctuations) { + RTC_DCHECK(prior_model_lrt); + RTC_DCHECK(low_lrt_fluctuations); + + float average = 0.f; + float average_compl = 0.f; + float average_squared = 0.f; + int count = 0; + + for (int i = 0; i < 10; ++i) { + float bin_mid = (i + 0.5f) * kBinSizeLrt; + average += lrt_histogram[i] * bin_mid; + count += lrt_histogram[i]; + } + if (count > 0) { + average = average / count; + } + + for (int i = 0; i < kHistogramSize; ++i) { + float bin_mid = (i + 0.5f) * kBinSizeLrt; + average_squared += lrt_histogram[i] * bin_mid * bin_mid; + average_compl += lrt_histogram[i] * bin_mid; + } + constexpr float kOneFeatureUpdateWindowSize = 1.f / kFeatureUpdateWindowSize; + average_squared = average_squared * kOneFeatureUpdateWindowSize; + average_compl = average_compl * kOneFeatureUpdateWindowSize; + + // Fluctuation limit of LRT feature. + *low_lrt_fluctuations = average_squared - average * average_compl < 0.05f; + + // Get threshold for LRT feature. + constexpr float kMaxLrt = 1.f; + constexpr float kMinLrt = .2f; + if (*low_lrt_fluctuations) { + // Very low fluctuation, so likely noise. + *prior_model_lrt = kMaxLrt; + } else { + *prior_model_lrt = std::min(kMaxLrt, std::max(kMinLrt, 1.2f * average)); + } +} + +} // namespace + +PriorSignalModelEstimator::PriorSignalModelEstimator(float lrt_initial_value) + : prior_model_(lrt_initial_value) {} + +// Extract thresholds for feature parameters and computes the threshold/weights. +void PriorSignalModelEstimator::Update(const Histograms& histograms) { + bool low_lrt_fluctuations; + UpdateLrt(histograms.get_lrt(), &prior_model_.lrt, &low_lrt_fluctuations); + + // For spectral flatness and spectral difference: compute the main peaks of + // the histograms. + float spectral_flatness_peak_position; + int spectral_flatness_peak_weight; + FindFirstOfTwoLargestPeaks( + kBinSizeSpecFlat, histograms.get_spectral_flatness(), + &spectral_flatness_peak_position, &spectral_flatness_peak_weight); + + float spectral_diff_peak_position = 0.f; + int spectral_diff_peak_weight = 0; + FindFirstOfTwoLargestPeaks(kBinSizeSpecDiff, histograms.get_spectral_diff(), + &spectral_diff_peak_position, + &spectral_diff_peak_weight); + + // Reject if weight of peaks is not large enough, or peak value too small. + // Peak limit for spectral flatness (varies between 0 and 1). + const int use_spec_flat = spectral_flatness_peak_weight < 0.3f * 500 || + spectral_flatness_peak_position < 0.6f + ? 0 + : 1; + + // Reject if weight of peaks is not large enough or if fluctuation of the LRT + // feature are very low, indicating a noise state. + const int use_spec_diff = + spectral_diff_peak_weight < 0.3f * 500 || low_lrt_fluctuations ? 0 : 1; + + // Update the model. + prior_model_.template_diff_threshold = 1.2f * spectral_diff_peak_position; + prior_model_.template_diff_threshold = + std::min(1.f, std::max(0.16f, prior_model_.template_diff_threshold)); + + float one_by_feature_sum = 1.f / (1.f + use_spec_flat + use_spec_diff); + prior_model_.lrt_weighting = one_by_feature_sum; + + if (use_spec_flat == 1) { + prior_model_.flatness_threshold = 0.9f * spectral_flatness_peak_position; + prior_model_.flatness_threshold = + std::min(.95f, std::max(0.1f, prior_model_.flatness_threshold)); + prior_model_.flatness_weighting = one_by_feature_sum; + } else { + prior_model_.flatness_weighting = 0.f; + } + + if (use_spec_diff == 1) { + prior_model_.difference_weighting = one_by_feature_sum; + } else { + prior_model_.difference_weighting = 0.f; + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.h b/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.h new file mode 100644 index 00000000..d178323d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_ESTIMATOR_H_ + +#include "modules/audio_processing/ns/histograms.h" +#include "modules/audio_processing/ns/prior_signal_model.h" + +namespace webrtc { + +// Estimator of the prior signal model parameters. +class PriorSignalModelEstimator { + public: + explicit PriorSignalModelEstimator(float lrt_initial_value); + PriorSignalModelEstimator(const PriorSignalModelEstimator&) = delete; + PriorSignalModelEstimator& operator=(const PriorSignalModelEstimator&) = + delete; + + // Updates the model estimate. + void Update(const Histograms& h); + + // Returns the estimated model. + const PriorSignalModel& get_prior_model() const { return prior_model_; } + + private: + PriorSignalModel prior_model_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/quantile_noise_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/ns/quantile_noise_estimator.cc new file mode 100644 index 00000000..b7847551 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/quantile_noise_estimator.cc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/quantile_noise_estimator.h" + +#include + +#include "modules/audio_processing/ns/fast_math.h" + +namespace webrtc { + +QuantileNoiseEstimator::QuantileNoiseEstimator() { + quantile_.fill(0.f); + density_.fill(0.3f); + log_quantile_.fill(8.f); + + constexpr float kOneBySimult = 1.f / kSimult; + for (size_t i = 0; i < kSimult; ++i) { + counter_[i] = floor(kLongStartupPhaseBlocks * (i + 1.f) * kOneBySimult); + } +} + +void QuantileNoiseEstimator::Estimate( + ArrayView signal_spectrum, + ArrayView noise_spectrum) { + std::array log_spectrum; + LogApproximation(signal_spectrum, log_spectrum); + + int quantile_index_to_return = -1; + // Loop over simultaneous estimates. + for (int s = 0, k = 0; s < kSimult; + ++s, k += static_cast(kFftSizeBy2Plus1)) { + const float one_by_counter_plus_1 = 1.f / (counter_[s] + 1.f); + for (int i = 0, j = k; i < static_cast(kFftSizeBy2Plus1); ++i, ++j) { + // Update log quantile estimate. + const float delta = density_[j] > 1.f ? 40.f / density_[j] : 40.f; + + const float multiplier = delta * one_by_counter_plus_1; + if (log_spectrum[i] > log_quantile_[j]) { + log_quantile_[j] += 0.25f * multiplier; + } else { + log_quantile_[j] -= 0.75f * multiplier; + } + + // Update density estimate. + constexpr float kWidth = 0.01f; + constexpr float kOneByWidthPlus2 = 1.f / (2.f * kWidth); + if (fabs(log_spectrum[i] - log_quantile_[j]) < kWidth) { + density_[j] = (counter_[s] * density_[j] + kOneByWidthPlus2) * + one_by_counter_plus_1; + } + } + + if (counter_[s] >= kLongStartupPhaseBlocks) { + counter_[s] = 0; + if (num_updates_ >= kLongStartupPhaseBlocks) { + quantile_index_to_return = k; + } + } + + ++counter_[s]; + } + + // Sequentially update the noise during startup. + if (num_updates_ < kLongStartupPhaseBlocks) { + // Use the last "s" to get noise during startup that differ from zero. + quantile_index_to_return = kFftSizeBy2Plus1 * (kSimult - 1); + ++num_updates_; + } + + if (quantile_index_to_return >= 0) { + ExpApproximation( + ArrayView(&log_quantile_[quantile_index_to_return], + kFftSizeBy2Plus1), + quantile_); + } + + std::copy(quantile_.begin(), quantile_.end(), noise_spectrum.begin()); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/quantile_noise_estimator.h b/pkg/apm/webrtc/modules/audio_processing/ns/quantile_noise_estimator.h new file mode 100644 index 00000000..79a1b3e1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/quantile_noise_estimator.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_QUANTILE_NOISE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_QUANTILE_NOISE_ESTIMATOR_H_ + +#include + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" + +namespace webrtc { + +constexpr int kSimult = 3; + +// For quantile noise estimation. +class QuantileNoiseEstimator { + public: + QuantileNoiseEstimator(); + QuantileNoiseEstimator(const QuantileNoiseEstimator&) = delete; + QuantileNoiseEstimator& operator=(const QuantileNoiseEstimator&) = delete; + + // Estimate noise. + void Estimate(ArrayView signal_spectrum, + ArrayView noise_spectrum); + + private: + std::array density_; + std::array log_quantile_; + std::array quantile_; + std::array counter_; + int num_updates_ = 1; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_QUANTILE_NOISE_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/signal_model.cc b/pkg/apm/webrtc/modules/audio_processing/ns/signal_model.cc new file mode 100644 index 00000000..364bfd00 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/signal_model.cc @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/signal_model.h" + +namespace webrtc { + +SignalModel::SignalModel() { + constexpr float kSfFeatureThr = 0.5f; + + lrt = kLtrFeatureThr; + spectral_flatness = kSfFeatureThr; + spectral_diff = kSfFeatureThr; + avg_log_lrt.fill(kLtrFeatureThr); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/signal_model.h b/pkg/apm/webrtc/modules/audio_processing/ns/signal_model.h new file mode 100644 index 00000000..6614d38a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/signal_model.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_H_ +#define MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_H_ + +#include + +#include "modules/audio_processing/ns/ns_common.h" + +namespace webrtc { + +struct SignalModel { + SignalModel(); + SignalModel(const SignalModel&) = delete; + SignalModel& operator=(const SignalModel&) = delete; + + float lrt; + float spectral_diff; + float spectral_flatness; + // Log LRT factor with time-smoothing. + std::array avg_log_lrt; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/signal_model_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/ns/signal_model_estimator.cc new file mode 100644 index 00000000..55c36ef4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/signal_model_estimator.cc @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/signal_model_estimator.h" + +#include "modules/audio_processing/ns/fast_math.h" + +namespace webrtc { + +namespace { + +constexpr float kOneByFftSizeBy2Plus1 = 1.f / kFftSizeBy2Plus1; + +// Computes the difference measure between input spectrum and a template/learned +// noise spectrum. +float ComputeSpectralDiff( + ArrayView conservative_noise_spectrum, + ArrayView signal_spectrum, + float signal_spectral_sum, + float diff_normalization) { + // spectral_diff = var(signal_spectrum) - cov(signal_spectrum, magnAvgPause)^2 + // / var(magnAvgPause) + + // Compute average quantities. + float noise_average = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + // Conservative smooth noise spectrum from pause frames. + noise_average += conservative_noise_spectrum[i]; + } + noise_average = noise_average * kOneByFftSizeBy2Plus1; + float signal_average = signal_spectral_sum * kOneByFftSizeBy2Plus1; + + // Compute variance and covariance quantities. + float covariance = 0.f; + float noise_variance = 0.f; + float signal_variance = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + float signal_diff = signal_spectrum[i] - signal_average; + float noise_diff = conservative_noise_spectrum[i] - noise_average; + covariance += signal_diff * noise_diff; + noise_variance += noise_diff * noise_diff; + signal_variance += signal_diff * signal_diff; + } + covariance *= kOneByFftSizeBy2Plus1; + noise_variance *= kOneByFftSizeBy2Plus1; + signal_variance *= kOneByFftSizeBy2Plus1; + + // Update of average magnitude spectrum. + float spectral_diff = + signal_variance - (covariance * covariance) / (noise_variance + 0.0001f); + // Normalize. + return spectral_diff / (diff_normalization + 0.0001f); +} + +// Updates the spectral flatness based on the input spectrum. +void UpdateSpectralFlatness( + ArrayView signal_spectrum, + float signal_spectral_sum, + float* spectral_flatness) { + RTC_DCHECK(spectral_flatness); + + // Compute log of ratio of the geometric to arithmetic mean (handle the log(0) + // separately). + constexpr float kAveraging = 0.3f; + float avg_spect_flatness_num = 0.f; + for (size_t i = 1; i < kFftSizeBy2Plus1; ++i) { + if (signal_spectrum[i] == 0.f) { + *spectral_flatness -= kAveraging * (*spectral_flatness); + return; + } + } + + for (size_t i = 1; i < kFftSizeBy2Plus1; ++i) { + avg_spect_flatness_num += LogApproximation(signal_spectrum[i]); + } + + float avg_spect_flatness_denom = signal_spectral_sum - signal_spectrum[0]; + + avg_spect_flatness_denom = avg_spect_flatness_denom * kOneByFftSizeBy2Plus1; + avg_spect_flatness_num = avg_spect_flatness_num * kOneByFftSizeBy2Plus1; + + float spectral_tmp = + ExpApproximation(avg_spect_flatness_num) / avg_spect_flatness_denom; + + // Time-avg update of spectral flatness feature. + *spectral_flatness += kAveraging * (spectral_tmp - *spectral_flatness); +} + +// Updates the log LRT measures. +void UpdateSpectralLrt(ArrayView prior_snr, + ArrayView post_snr, + ArrayView avg_log_lrt, + float* lrt) { + RTC_DCHECK(lrt); + + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + float tmp1 = 1.f + 2.f * prior_snr[i]; + float tmp2 = 2.f * prior_snr[i] / (tmp1 + 0.0001f); + float bessel_tmp = (post_snr[i] + 1.f) * tmp2; + avg_log_lrt[i] += + .5f * (bessel_tmp - LogApproximation(tmp1) - avg_log_lrt[i]); + } + + float log_lrt_time_avg_k_sum = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + log_lrt_time_avg_k_sum += avg_log_lrt[i]; + } + *lrt = log_lrt_time_avg_k_sum * kOneByFftSizeBy2Plus1; +} + +} // namespace + +SignalModelEstimator::SignalModelEstimator() + : prior_model_estimator_(kLtrFeatureThr) {} + +void SignalModelEstimator::AdjustNormalization(int32_t num_analyzed_frames, + float signal_energy) { + diff_normalization_ *= num_analyzed_frames; + diff_normalization_ += signal_energy; + diff_normalization_ /= (num_analyzed_frames + 1); +} + +// Update the noise features. +void SignalModelEstimator::Update( + ArrayView prior_snr, + ArrayView post_snr, + ArrayView conservative_noise_spectrum, + ArrayView signal_spectrum, + float signal_spectral_sum, + float signal_energy) { + // Compute spectral flatness on input spectrum. + UpdateSpectralFlatness(signal_spectrum, signal_spectral_sum, + &features_.spectral_flatness); + + // Compute difference of input spectrum with learned/estimated noise spectrum. + float spectral_diff = + ComputeSpectralDiff(conservative_noise_spectrum, signal_spectrum, + signal_spectral_sum, diff_normalization_); + // Compute time-avg update of difference feature. + features_.spectral_diff += 0.3f * (spectral_diff - features_.spectral_diff); + + signal_energy_sum_ += signal_energy; + + // Compute histograms for parameter decisions (thresholds and weights for + // features). Parameters are extracted periodically. + if (--histogram_analysis_counter_ > 0) { + histograms_.Update(features_); + } else { + // Compute model parameters. + prior_model_estimator_.Update(histograms_); + + // Clear histograms for next update. + histograms_.Clear(); + + histogram_analysis_counter_ = kFeatureUpdateWindowSize; + + // Update every window: + // Compute normalization for the spectral difference for next estimation. + signal_energy_sum_ = signal_energy_sum_ / kFeatureUpdateWindowSize; + diff_normalization_ = 0.5f * (signal_energy_sum_ + diff_normalization_); + signal_energy_sum_ = 0.f; + } + + // Compute the LRT. + UpdateSpectralLrt(prior_snr, post_snr, features_.avg_log_lrt, &features_.lrt); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/signal_model_estimator.h b/pkg/apm/webrtc/modules/audio_processing/ns/signal_model_estimator.h new file mode 100644 index 00000000..834af1ed --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/signal_model_estimator.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_ESTIMATOR_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/histograms.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/prior_signal_model.h" +#include "modules/audio_processing/ns/prior_signal_model_estimator.h" +#include "modules/audio_processing/ns/signal_model.h" + +namespace webrtc { + +class SignalModelEstimator { + public: + SignalModelEstimator(); + SignalModelEstimator(const SignalModelEstimator&) = delete; + SignalModelEstimator& operator=(const SignalModelEstimator&) = delete; + + // Compute signal normalization during the initial startup phase. + void AdjustNormalization(int32_t num_analyzed_frames, float signal_energy); + + void Update( + ArrayView prior_snr, + ArrayView post_snr, + ArrayView conservative_noise_spectrum, + ArrayView signal_spectrum, + float signal_spectral_sum, + float signal_energy); + + const PriorSignalModel& get_prior_model() const { + return prior_model_estimator_.get_prior_model(); + } + const SignalModel& get_model() { return features_; } + + private: + float diff_normalization_ = 0.f; + float signal_energy_sum_ = 0.f; + Histograms histograms_; + int histogram_analysis_counter_ = 500; + PriorSignalModelEstimator prior_model_estimator_; + SignalModel features_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/speech_probability_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/ns/speech_probability_estimator.cc new file mode 100644 index 00000000..ddd20bdd --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/speech_probability_estimator.cc @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/speech_probability_estimator.h" + +#include + +#include + +#include "modules/audio_processing/ns/fast_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +SpeechProbabilityEstimator::SpeechProbabilityEstimator() { + speech_probability_.fill(0.f); +} + +void SpeechProbabilityEstimator::Update( + int32_t num_analyzed_frames, + ArrayView prior_snr, + ArrayView post_snr, + ArrayView conservative_noise_spectrum, + ArrayView signal_spectrum, + float signal_spectral_sum, + float signal_energy) { + // Update models. + if (num_analyzed_frames < kLongStartupPhaseBlocks) { + signal_model_estimator_.AdjustNormalization(num_analyzed_frames, + signal_energy); + } + signal_model_estimator_.Update(prior_snr, post_snr, + conservative_noise_spectrum, signal_spectrum, + signal_spectral_sum, signal_energy); + + const SignalModel& model = signal_model_estimator_.get_model(); + const PriorSignalModel& prior_model = + signal_model_estimator_.get_prior_model(); + + // Width parameter in sigmoid map for prior model. + constexpr float kWidthPrior0 = 4.f; + // Width for pause region: lower range, so increase width in tanh map. + constexpr float kWidthPrior1 = 2.f * kWidthPrior0; + + // Average LRT feature: use larger width in tanh map for pause regions. + float width_prior = model.lrt < prior_model.lrt ? kWidthPrior1 : kWidthPrior0; + + // Compute indicator function: sigmoid map. + float indicator0 = + 0.5f * (tanh(width_prior * (model.lrt - prior_model.lrt)) + 1.f); + + // Spectral flatness feature: use larger width in tanh map for pause regions. + width_prior = model.spectral_flatness > prior_model.flatness_threshold + ? kWidthPrior1 + : kWidthPrior0; + + // Compute indicator function: sigmoid map. + float indicator1 = + 0.5f * (tanh(1.f * width_prior * + (prior_model.flatness_threshold - model.spectral_flatness)) + + 1.f); + + // For template spectrum-difference : use larger width in tanh map for pause + // regions. + width_prior = model.spectral_diff < prior_model.template_diff_threshold + ? kWidthPrior1 + : kWidthPrior0; + + // Compute indicator function: sigmoid map. + float indicator2 = + 0.5f * (tanh(width_prior * (model.spectral_diff - + prior_model.template_diff_threshold)) + + 1.f); + + // Combine the indicator function with the feature weights. + float ind_prior = prior_model.lrt_weighting * indicator0 + + prior_model.flatness_weighting * indicator1 + + prior_model.difference_weighting * indicator2; + + // Compute the prior probability. + prior_speech_prob_ += 0.1f * (ind_prior - prior_speech_prob_); + + // Make sure probabilities are within range: keep floor to 0.01. + prior_speech_prob_ = std::max(std::min(prior_speech_prob_, 1.f), 0.01f); + + // Final speech probability: combine prior model with LR factor:. + float gain_prior = + (1.f - prior_speech_prob_) / (prior_speech_prob_ + 0.0001f); + + std::array inv_lrt; + ExpApproximationSignFlip(model.avg_log_lrt, inv_lrt); + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + speech_probability_[i] = 1.f / (1.f + gain_prior * inv_lrt[i]); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/speech_probability_estimator.h b/pkg/apm/webrtc/modules/audio_processing/ns/speech_probability_estimator.h new file mode 100644 index 00000000..64ed6024 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/speech_probability_estimator.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_SPEECH_PROBABILITY_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_SPEECH_PROBABILITY_ESTIMATOR_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/signal_model_estimator.h" + +namespace webrtc { + +// Class for estimating the probability of speech. +class SpeechProbabilityEstimator { + public: + SpeechProbabilityEstimator(); + SpeechProbabilityEstimator(const SpeechProbabilityEstimator&) = delete; + SpeechProbabilityEstimator& operator=(const SpeechProbabilityEstimator&) = + delete; + + // Compute speech probability. + void Update( + int32_t num_analyzed_frames, + ArrayView prior_snr, + ArrayView post_snr, + ArrayView conservative_noise_spectrum, + ArrayView signal_spectrum, + float signal_spectral_sum, + float signal_energy); + + float get_prior_probability() const { return prior_speech_prob_; } + ArrayView get_probability() { return speech_probability_; } + + private: + SignalModelEstimator signal_model_estimator_; + float prior_speech_prob_ = .5f; + std::array speech_probability_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_SPEECH_PROBABILITY_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/suppression_params.cc b/pkg/apm/webrtc/modules/audio_processing/ns/suppression_params.cc new file mode 100644 index 00000000..7bf18346 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/suppression_params.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/suppression_params.h" + +#include "rtc_base/checks.h" + +namespace webrtc { + +SuppressionParams::SuppressionParams( + NsConfig::SuppressionLevel suppression_level) { + switch (suppression_level) { + case NsConfig::SuppressionLevel::k6dB: + over_subtraction_factor = 1.f; + // 6 dB attenuation. + minimum_attenuating_gain = 0.5f; + use_attenuation_adjustment = false; + break; + case NsConfig::SuppressionLevel::k12dB: + over_subtraction_factor = 1.f; + // 12 dB attenuation. + minimum_attenuating_gain = 0.25f; + use_attenuation_adjustment = true; + break; + case NsConfig::SuppressionLevel::k18dB: + over_subtraction_factor = 1.1f; + // 18 dB attenuation. + minimum_attenuating_gain = 0.125f; + use_attenuation_adjustment = true; + break; + case NsConfig::SuppressionLevel::k21dB: + over_subtraction_factor = 1.25f; + // 20.9 dB attenuation. + minimum_attenuating_gain = 0.09f; + use_attenuation_adjustment = true; + break; + default: + RTC_DCHECK_NOTREACHED(); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/suppression_params.h b/pkg/apm/webrtc/modules/audio_processing/ns/suppression_params.h new file mode 100644 index 00000000..ad11977d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/suppression_params.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_SUPPRESSION_PARAMS_H_ +#define MODULES_AUDIO_PROCESSING_NS_SUPPRESSION_PARAMS_H_ + +#include "modules/audio_processing/ns/ns_config.h" + +namespace webrtc { + +struct SuppressionParams { + explicit SuppressionParams(NsConfig::SuppressionLevel suppression_level); + SuppressionParams(const SuppressionParams&) = delete; + SuppressionParams& operator=(const SuppressionParams&) = delete; + + float over_subtraction_factor; + float minimum_attenuating_gain; + bool use_attenuation_adjustment; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_SUPPRESSION_PARAMS_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/wiener_filter.cc b/pkg/apm/webrtc/modules/audio_processing/ns/wiener_filter.cc new file mode 100644 index 00000000..83e40dcb --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/wiener_filter.cc @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/ns/wiener_filter.h" + +#include +#include +#include + +#include + +#include "modules/audio_processing/ns/fast_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +WienerFilter::WienerFilter(const SuppressionParams& suppression_params) + : suppression_params_(suppression_params) { + filter_.fill(1.f); + initial_spectral_estimate_.fill(0.f); + spectrum_prev_process_.fill(0.f); +} + +void WienerFilter::Update( + int32_t num_analyzed_frames, + ArrayView noise_spectrum, + ArrayView prev_noise_spectrum, + ArrayView parametric_noise_spectrum, + ArrayView signal_spectrum) { + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + // Previous estimate based on previous frame with gain filter. + float prev_tsa = spectrum_prev_process_[i] / + (prev_noise_spectrum[i] + 0.0001f) * filter_[i]; + + // Current estimate. + float current_tsa; + if (signal_spectrum[i] > noise_spectrum[i]) { + current_tsa = signal_spectrum[i] / (noise_spectrum[i] + 0.0001f) - 1.f; + } else { + current_tsa = 0.f; + } + + // Directed decision estimate is sum of two terms: current estimate and + // previous estimate. + float snr_prior = 0.98f * prev_tsa + (1.f - 0.98f) * current_tsa; + filter_[i] = + snr_prior / (suppression_params_.over_subtraction_factor + snr_prior); + filter_[i] = std::max(std::min(filter_[i], 1.f), + suppression_params_.minimum_attenuating_gain); + } + + if (num_analyzed_frames < kShortStartupPhaseBlocks) { + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + initial_spectral_estimate_[i] += signal_spectrum[i]; + float filter_initial = initial_spectral_estimate_[i] - + suppression_params_.over_subtraction_factor * + parametric_noise_spectrum[i]; + filter_initial /= initial_spectral_estimate_[i] + 0.0001f; + + filter_initial = std::max(std::min(filter_initial, 1.f), + suppression_params_.minimum_attenuating_gain); + + // Weight the two suppression filters. + constexpr float kOnyByShortStartupPhaseBlocks = + 1.f / kShortStartupPhaseBlocks; + filter_initial *= kShortStartupPhaseBlocks - num_analyzed_frames; + filter_[i] *= num_analyzed_frames; + filter_[i] += filter_initial; + filter_[i] *= kOnyByShortStartupPhaseBlocks; + } + } + + std::copy(signal_spectrum.begin(), signal_spectrum.end(), + spectrum_prev_process_.begin()); +} + +float WienerFilter::ComputeOverallScalingFactor( + int32_t num_analyzed_frames, + float prior_speech_probability, + float energy_before_filtering, + float energy_after_filtering) const { + if (!suppression_params_.use_attenuation_adjustment || + num_analyzed_frames <= kLongStartupPhaseBlocks) { + return 1.f; + } + + float gain = SqrtFastApproximation(energy_after_filtering / + (energy_before_filtering + 1.f)); + + // Scaling for new version. Threshold in final energy gain factor calculation. + constexpr float kBLim = 0.5f; + float scale_factor1 = 1.f; + if (gain > kBLim) { + scale_factor1 = 1.f + 1.3f * (gain - kBLim); + if (gain * scale_factor1 > 1.f) { + scale_factor1 = 1.f / gain; + } + } + + float scale_factor2 = 1.f; + if (gain < kBLim) { + // Do not reduce scale too much for pause regions: attenuation here should + // be controlled by flooring. + gain = std::max(gain, suppression_params_.minimum_attenuating_gain); + scale_factor2 = 1.f - 0.3f * (kBLim - gain); + } + + // Combine both scales with speech/noise prob: note prior + // (prior_speech_probability) is not frequency dependent. + return prior_speech_probability * scale_factor1 + + (1.f - prior_speech_probability) * scale_factor2; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/ns/wiener_filter.h b/pkg/apm/webrtc/modules/audio_processing/ns/wiener_filter.h new file mode 100644 index 00000000..cd45541e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/ns/wiener_filter.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_WIENER_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_NS_WIENER_FILTER_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/suppression_params.h" + +namespace webrtc { + +// Estimates a Wiener-filter based frequency domain noise reduction filter. +class WienerFilter { + public: + explicit WienerFilter(const SuppressionParams& suppression_params); + WienerFilter(const WienerFilter&) = delete; + WienerFilter& operator=(const WienerFilter&) = delete; + + // Updates the filter estimate. + void Update( + int32_t num_analyzed_frames, + ArrayView noise_spectrum, + ArrayView prev_noise_spectrum, + ArrayView parametric_noise_spectrum, + ArrayView signal_spectrum); + + // Compute an overall gain scaling factor. + float ComputeOverallScalingFactor(int32_t num_analyzed_frames, + float prior_speech_probability, + float energy_before_filtering, + float energy_after_filtering) const; + + // Returns the filter. + ArrayView get_filter() const { + return filter_; + } + + private: + const SuppressionParams& suppression_params_; + std::array spectrum_prev_process_; + std::array initial_spectral_estimate_; + std::array filter_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_WIENER_FILTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/render_queue_item_verifier.h b/pkg/apm/webrtc/modules/audio_processing/render_queue_item_verifier.h new file mode 100644 index 00000000..b8aff4a1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/render_queue_item_verifier.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_RENDER_QUEUE_ITEM_VERIFIER_H_ +#define MODULES_AUDIO_PROCESSING_RENDER_QUEUE_ITEM_VERIFIER_H_ + +#include + +namespace webrtc { + +// Functor to use when supplying a verifier function for the queue item +// verifcation. +template +class RenderQueueItemVerifier { + public: + explicit RenderQueueItemVerifier(size_t minimum_capacity) + : minimum_capacity_(minimum_capacity) {} + + bool operator()(const std::vector& v) const { + return v.capacity() >= minimum_capacity_; + } + + private: + size_t minimum_capacity_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_RENDER_QUEUE_ITEM_VERIFIER_H__ diff --git a/pkg/apm/webrtc/modules/audio_processing/residual_echo_detector.cc b/pkg/apm/webrtc/modules/audio_processing/residual_echo_detector.cc new file mode 100644 index 00000000..437d857c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/residual_echo_detector.cc @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/residual_echo_detector.h" + +#include +#include +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/audio_processing.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/metrics.h" + +namespace { + +float Power(webrtc::ArrayView input) { + if (input.empty()) { + return 0.f; + } + return std::inner_product(input.begin(), input.end(), input.begin(), 0.f) / + input.size(); +} + +constexpr size_t kLookbackFrames = 650; +// TODO(ivoc): Verify the size of this buffer. +constexpr size_t kRenderBufferSize = 30; +constexpr float kAlpha = 0.001f; +// 10 seconds of data, updated every 10 ms. +constexpr size_t kAggregationBufferSize = 10 * 100; + +} // namespace + +namespace webrtc { + +std::atomic ResidualEchoDetector::instance_count_(0); + +ResidualEchoDetector::ResidualEchoDetector() + : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), + render_buffer_(kRenderBufferSize), + render_power_(kLookbackFrames), + render_power_mean_(kLookbackFrames), + render_power_std_dev_(kLookbackFrames), + covariances_(kLookbackFrames), + recent_likelihood_max_(kAggregationBufferSize) {} + +ResidualEchoDetector::~ResidualEchoDetector() = default; + +void ResidualEchoDetector::AnalyzeRenderAudio( + ArrayView render_audio) { + // Dump debug data assuming 48 kHz sample rate (if this assumption is not + // valid the dumped audio will need to be converted offline accordingly). + data_dumper_->DumpWav("ed_render", render_audio.size(), render_audio.data(), + 48000, 1); + + if (render_buffer_.Size() == 0) { + frames_since_zero_buffer_size_ = 0; + } else if (frames_since_zero_buffer_size_ >= kRenderBufferSize) { + // This can happen in a few cases: at the start of a call, due to a glitch + // or due to clock drift. The excess capture value will be ignored. + // TODO(ivoc): Include how often this happens in APM stats. + render_buffer_.Pop(); + frames_since_zero_buffer_size_ = 0; + } + ++frames_since_zero_buffer_size_; + float power = Power(render_audio); + render_buffer_.Push(power); +} + +void ResidualEchoDetector::AnalyzeCaptureAudio( + ArrayView capture_audio) { + // Dump debug data assuming 48 kHz sample rate (if this assumption is not + // valid the dumped audio will need to be converted offline accordingly). + data_dumper_->DumpWav("ed_capture", capture_audio.size(), + capture_audio.data(), 48000, 1); + + if (first_process_call_) { + // On the first process call (so the start of a call), we must flush the + // render buffer, otherwise the render data will be delayed. + render_buffer_.Clear(); + first_process_call_ = false; + } + + // Get the next render value. + const std::optional buffered_render_power = render_buffer_.Pop(); + if (!buffered_render_power) { + // This can happen in a few cases: at the start of a call, due to a glitch + // or due to clock drift. The excess capture value will be ignored. + // TODO(ivoc): Include how often this happens in APM stats. + return; + } + // Update the render statistics, and store the statistics in circular buffers. + render_statistics_.Update(*buffered_render_power); + RTC_DCHECK_LT(next_insertion_index_, kLookbackFrames); + render_power_[next_insertion_index_] = *buffered_render_power; + render_power_mean_[next_insertion_index_] = render_statistics_.mean(); + render_power_std_dev_[next_insertion_index_] = + render_statistics_.std_deviation(); + + // Get the next capture value, update capture statistics and add the relevant + // values to the buffers. + const float capture_power = Power(capture_audio); + capture_statistics_.Update(capture_power); + const float capture_mean = capture_statistics_.mean(); + const float capture_std_deviation = capture_statistics_.std_deviation(); + + // Update the covariance values and determine the new echo likelihood. + echo_likelihood_ = 0.f; + size_t read_index = next_insertion_index_; + + int best_delay = -1; + for (size_t delay = 0; delay < covariances_.size(); ++delay) { + RTC_DCHECK_LT(read_index, render_power_.size()); + covariances_[delay].Update(capture_power, capture_mean, + capture_std_deviation, render_power_[read_index], + render_power_mean_[read_index], + render_power_std_dev_[read_index]); + read_index = read_index > 0 ? read_index - 1 : kLookbackFrames - 1; + + if (covariances_[delay].normalized_cross_correlation() > echo_likelihood_) { + echo_likelihood_ = covariances_[delay].normalized_cross_correlation(); + best_delay = static_cast(delay); + } + } + // This is a temporary log message to help find the underlying cause for echo + // likelihoods > 1.0. + // TODO(ivoc): Remove once the issue is resolved. + if (echo_likelihood_ > 1.1f) { + // Make sure we don't spam the log. + if (log_counter_ < 5 && best_delay != -1) { + size_t read_index_high_echo = + kLookbackFrames + next_insertion_index_ - best_delay; + if (read_index_high_echo >= kLookbackFrames) { + read_index_high_echo -= kLookbackFrames; + } + RTC_DCHECK_LT(read_index_high_echo, render_power_.size()); + RTC_LOG_F(LS_ERROR) + << "Echo detector internal state: {" + "Echo likelihood: " + << echo_likelihood_ << ", Best Delay: " << best_delay + << ", Covariance: " << covariances_[best_delay].covariance() + << ", Last capture power: " << capture_power + << ", Capture mean: " << capture_mean + << ", Capture_standard deviation: " << capture_std_deviation + << ", Last render power: " << render_power_[read_index_high_echo] + << ", Render mean: " << render_power_mean_[read_index_high_echo] + << ", Render standard deviation: " + << render_power_std_dev_[read_index_high_echo] + << ", Reliability: " << reliability_ << "}"; + log_counter_++; + } + } + RTC_DCHECK_LT(echo_likelihood_, 1.1f); + + reliability_ = (1.0f - kAlpha) * reliability_ + kAlpha * 1.0f; + echo_likelihood_ *= reliability_; + // This is a temporary fix to prevent echo likelihood values > 1.0. + // TODO(ivoc): Find the root cause of this issue and fix it. + echo_likelihood_ = std::min(echo_likelihood_, 1.0f); + int echo_percentage = static_cast(echo_likelihood_ * 100); + RTC_HISTOGRAM_COUNTS("WebRTC.Audio.ResidualEchoDetector.EchoLikelihood", + echo_percentage, 0, 100, 100 /* number of bins */); + + // Update the buffer of recent likelihood values. + recent_likelihood_max_.Update(echo_likelihood_); + + // Update the next insertion index. + next_insertion_index_ = next_insertion_index_ < (kLookbackFrames - 1) + ? next_insertion_index_ + 1 + : 0; +} + +void ResidualEchoDetector::Initialize(int /*capture_sample_rate_hz*/, + int /*num_capture_channels*/, + int /*render_sample_rate_hz*/, + int /*num_render_channels*/) { + render_buffer_.Clear(); + std::fill(render_power_.begin(), render_power_.end(), 0.f); + std::fill(render_power_mean_.begin(), render_power_mean_.end(), 0.f); + std::fill(render_power_std_dev_.begin(), render_power_std_dev_.end(), 0.f); + render_statistics_.Clear(); + capture_statistics_.Clear(); + recent_likelihood_max_.Clear(); + for (auto& cov : covariances_) { + cov.Clear(); + } + echo_likelihood_ = 0.f; + next_insertion_index_ = 0; + reliability_ = 0.f; +} + +EchoDetector::Metrics ResidualEchoDetector::GetMetrics() const { + EchoDetector::Metrics metrics; + metrics.echo_likelihood = echo_likelihood_; + metrics.echo_likelihood_recent_max = recent_likelihood_max_.max(); + return metrics; +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/residual_echo_detector.h b/pkg/apm/webrtc/modules/audio_processing/residual_echo_detector.h new file mode 100644 index 00000000..495cc557 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/residual_echo_detector.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_RESIDUAL_ECHO_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_RESIDUAL_ECHO_DETECTOR_H_ + +#include +#include + +#include "api/array_view.h" +#include "api/audio/audio_processing.h" +#include "modules/audio_processing/echo_detector/circular_buffer.h" +#include "modules/audio_processing/echo_detector/mean_variance_estimator.h" +#include "modules/audio_processing/echo_detector/moving_max.h" +#include "modules/audio_processing/echo_detector/normalized_covariance_estimator.h" + +namespace webrtc { + +class ApmDataDumper; +class AudioBuffer; + +class ResidualEchoDetector : public EchoDetector { + public: + ResidualEchoDetector(); + ~ResidualEchoDetector() override; + + // This function should be called while holding the render lock. + void AnalyzeRenderAudio(ArrayView render_audio) override; + + // This function should be called while holding the capture lock. + void AnalyzeCaptureAudio(ArrayView capture_audio) override; + + // This function should be called while holding the capture lock. + void Initialize(int capture_sample_rate_hz, + int num_capture_channels, + int render_sample_rate_hz, + int num_render_channels) override; + + // This function is for testing purposes only. + void SetReliabilityForTest(float value) { reliability_ = value; } + + // This function should be called while holding the capture lock. + EchoDetector::Metrics GetMetrics() const override; + + private: + static std::atomic instance_count_; + std::unique_ptr data_dumper_; + // Keep track if the `Process` function has been previously called. + bool first_process_call_ = true; + // Buffer for storing the power of incoming farend buffers. This is needed for + // cases where calls to BufferFarend and Process are jittery. + CircularBuffer render_buffer_; + // Count how long ago it was that the size of `render_buffer_` was zero. This + // value is also reset to zero when clock drift is detected and a value from + // the renderbuffer is discarded, even though the buffer is not actually zero + // at that point. This is done to avoid repeatedly removing elements in this + // situation. + size_t frames_since_zero_buffer_size_ = 0; + + // Circular buffers containing delayed versions of the power, mean and + // standard deviation, for calculating the delayed covariance values. + std::vector render_power_; + std::vector render_power_mean_; + std::vector render_power_std_dev_; + // Covariance estimates for different delay values. + std::vector covariances_; + // Index where next element should be inserted in all of the above circular + // buffers. + size_t next_insertion_index_ = 0; + + MeanVarianceEstimator render_statistics_; + MeanVarianceEstimator capture_statistics_; + // Current echo likelihood. + float echo_likelihood_ = 0.f; + // Reliability of the current likelihood. + float reliability_ = 0.f; + MovingMax recent_likelihood_max_; + + int log_counter_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_RESIDUAL_ECHO_DETECTOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/rms_level.cc b/pkg/apm/webrtc/modules/audio_processing/rms_level.cc new file mode 100644 index 00000000..ffb8e914 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/rms_level.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/rms_level.h" + +#include +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { +static constexpr float kMaxSquaredLevel = 32768 * 32768; +// kMinLevel is the level corresponding to kMinLevelDb, that is 10^(-127/10). +static constexpr float kMinLevel = 1.995262314968883e-13f; + +// Calculates the normalized RMS value from a mean square value. The input +// should be the sum of squared samples divided by the number of samples. The +// value will be normalized to full range before computing the RMS, wich is +// returned as a negated dBfs. That is, 0 is full amplitude while 127 is very +// faint. +int ComputeRms(float mean_square) { + if (mean_square <= kMinLevel * kMaxSquaredLevel) { + // Very faint; simply return the minimum value. + return RmsLevel::kMinLevelDb; + } + // Normalize by the max level. + const float mean_square_norm = mean_square / kMaxSquaredLevel; + RTC_DCHECK_GT(mean_square_norm, kMinLevel); + // 20log_10(x^0.5) = 10log_10(x) + const float rms = 10.f * std::log10(mean_square_norm); + RTC_DCHECK_LE(rms, 0.f); + RTC_DCHECK_GT(rms, -RmsLevel::kMinLevelDb); + // Return the negated value. + return static_cast(-rms + 0.5f); +} +} // namespace + +RmsLevel::RmsLevel() { + Reset(); +} + +RmsLevel::~RmsLevel() = default; + +void RmsLevel::Reset() { + sum_square_ = 0.f; + sample_count_ = 0; + max_sum_square_ = 0.f; + block_size_ = std::nullopt; +} + +void RmsLevel::Analyze(ArrayView data) { + if (data.empty()) { + return; + } + + CheckBlockSize(data.size()); + + const float sum_square = + std::accumulate(data.begin(), data.end(), 0.f, + [](float a, int16_t b) { return a + b * b; }); + RTC_DCHECK_GE(sum_square, 0.f); + sum_square_ += sum_square; + sample_count_ += data.size(); + + max_sum_square_ = std::max(max_sum_square_, sum_square); +} + +void RmsLevel::Analyze(ArrayView data) { + if (data.empty()) { + return; + } + + CheckBlockSize(data.size()); + + float sum_square = 0.f; + + for (float data_k : data) { + int16_t tmp = + static_cast(std::min(std::max(data_k, -32768.f), 32767.f)); + sum_square += tmp * tmp; + } + RTC_DCHECK_GE(sum_square, 0.f); + sum_square_ += sum_square; + sample_count_ += data.size(); + + max_sum_square_ = std::max(max_sum_square_, sum_square); +} + +void RmsLevel::AnalyzeMuted(size_t length) { + CheckBlockSize(length); + sample_count_ += length; +} + +int RmsLevel::Average() { + const bool have_samples = (sample_count_ != 0); + int rms = have_samples ? ComputeRms(sum_square_ / sample_count_) + : RmsLevel::kMinLevelDb; + + // To ensure that kMinLevelDb represents digital silence (muted audio + // sources) we'll check here if the sum_square is actually 0. If it's not + // we'll bump up the return value to `kInaudibleButNotMuted`. + // https://datatracker.ietf.org/doc/html/rfc6464 + if (have_samples && rms == RmsLevel::kMinLevelDb && sum_square_ != 0.0f) { + rms = kInaudibleButNotMuted; + } + + Reset(); + return rms; +} + +RmsLevel::Levels RmsLevel::AverageAndPeak() { + // Note that block_size_ should by design always be non-empty when + // sample_count_ != 0. Also, the * operator of std::optional enforces this + // with a DCHECK. + Levels levels = (sample_count_ == 0) + ? Levels{RmsLevel::kMinLevelDb, RmsLevel::kMinLevelDb} + : Levels{ComputeRms(sum_square_ / sample_count_), + ComputeRms(max_sum_square_ / *block_size_)}; + Reset(); + return levels; +} + +void RmsLevel::CheckBlockSize(size_t block_size) { + if (block_size_ != block_size) { + Reset(); + block_size_ = block_size; + } +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/rms_level.h b/pkg/apm/webrtc/modules/audio_processing/rms_level.h new file mode 100644 index 00000000..879fdff2 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/rms_level.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ +#define MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ + +#include +#include + +#include + +#include "api/array_view.h" + +namespace webrtc { + +// Computes the root mean square (RMS) level in dBFs (decibels from digital +// full-scale) of audio data. The computation follows RFC 6465: +// https://tools.ietf.org/html/rfc6465 +// with the intent that it can provide the RTP audio level indication. +// +// The expected approach is to provide constant-sized chunks of audio to +// Analyze(). When enough chunks have been accumulated to form a packet, call +// Average() to get the audio level indicator for the RTP header. +class RmsLevel { + public: + struct Levels { + int average; + int peak; + }; + + enum : int { kMinLevelDb = 127, kInaudibleButNotMuted = 126 }; + + RmsLevel(); + ~RmsLevel(); + + // Can be called to reset internal states, but is not required during normal + // operation. + void Reset(); + + // Pass each chunk of audio to Analyze() to accumulate the level. + void Analyze(ArrayView data); + void Analyze(ArrayView data); + + // If all samples with the given `length` have a magnitude of zero, this is + // a shortcut to avoid some computation. + void AnalyzeMuted(size_t length); + + // Computes the RMS level over all data passed to Analyze() since the last + // call to Average(). The returned value is positive but should be interpreted + // as negative as per the RFC. It is constrained to [0, 127]. Resets the + // internal state to start a new measurement period. + int Average(); + + // Like Average() above, but also returns the RMS peak value. Resets the + // internal state to start a new measurement period. + Levels AverageAndPeak(); + + private: + // Compares `block_size` with `block_size_`. If they are different, calls + // Reset() and stores the new size. + void CheckBlockSize(size_t block_size); + + float sum_square_; + size_t sample_count_; + float max_sum_square_; + std::optional block_size_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/splitting_filter.cc b/pkg/apm/webrtc/modules/audio_processing/splitting_filter.cc new file mode 100644 index 00000000..f6075523 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/splitting_filter.cc @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/splitting_filter.h" + +#include + +#include "api/array_view.h" +#include "common_audio/channel_buffer.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr size_t kSamplesPerBand = 160; +constexpr size_t kTwoBandFilterSamplesPerFrame = 320; + +} // namespace + +SplittingFilter::SplittingFilter(size_t num_channels, + size_t num_bands, + size_t /* num_frames */) + : num_bands_(num_bands), + two_bands_states_(num_bands_ == 2 ? num_channels : 0), + three_band_filter_banks_(num_bands_ == 3 ? num_channels : 0) { + RTC_CHECK(num_bands_ == 2 || num_bands_ == 3); +} + +SplittingFilter::~SplittingFilter() = default; + +void SplittingFilter::Analysis(const ChannelBuffer* data, + ChannelBuffer* bands) { + RTC_DCHECK_EQ(num_bands_, bands->num_bands()); + RTC_DCHECK_EQ(data->num_channels(), bands->num_channels()); + RTC_DCHECK_EQ(data->num_frames(), + bands->num_frames_per_band() * bands->num_bands()); + if (bands->num_bands() == 2) { + TwoBandsAnalysis(data, bands); + } else if (bands->num_bands() == 3) { + ThreeBandsAnalysis(data, bands); + } +} + +void SplittingFilter::Synthesis(const ChannelBuffer* bands, + ChannelBuffer* data) { + RTC_DCHECK_EQ(num_bands_, bands->num_bands()); + RTC_DCHECK_EQ(data->num_channels(), bands->num_channels()); + RTC_DCHECK_EQ(data->num_frames(), + bands->num_frames_per_band() * bands->num_bands()); + if (bands->num_bands() == 2) { + TwoBandsSynthesis(bands, data); + } else if (bands->num_bands() == 3) { + ThreeBandsSynthesis(bands, data); + } +} + +void SplittingFilter::TwoBandsAnalysis(const ChannelBuffer* data, + ChannelBuffer* bands) { + RTC_DCHECK_EQ(two_bands_states_.size(), data->num_channels()); + RTC_DCHECK_EQ(data->num_frames(), kTwoBandFilterSamplesPerFrame); + + for (size_t i = 0; i < two_bands_states_.size(); ++i) { + std::array, 2> bands16; + std::array full_band16; + FloatS16ToS16(data->channels(0)[i], full_band16.size(), full_band16.data()); + WebRtcSpl_AnalysisQMF(full_band16.data(), data->num_frames(), + bands16[0].data(), bands16[1].data(), + two_bands_states_[i].analysis_state1, + two_bands_states_[i].analysis_state2); + S16ToFloatS16(bands16[0].data(), bands16[0].size(), bands->channels(0)[i]); + S16ToFloatS16(bands16[1].data(), bands16[1].size(), bands->channels(1)[i]); + } +} + +void SplittingFilter::TwoBandsSynthesis(const ChannelBuffer* bands, + ChannelBuffer* data) { + RTC_DCHECK_LE(data->num_channels(), two_bands_states_.size()); + RTC_DCHECK_EQ(data->num_frames(), kTwoBandFilterSamplesPerFrame); + for (size_t i = 0; i < data->num_channels(); ++i) { + std::array, 2> bands16; + std::array full_band16; + FloatS16ToS16(bands->channels(0)[i], bands16[0].size(), bands16[0].data()); + FloatS16ToS16(bands->channels(1)[i], bands16[1].size(), bands16[1].data()); + WebRtcSpl_SynthesisQMF(bands16[0].data(), bands16[1].data(), + bands->num_frames_per_band(), full_band16.data(), + two_bands_states_[i].synthesis_state1, + two_bands_states_[i].synthesis_state2); + S16ToFloatS16(full_band16.data(), full_band16.size(), data->channels(0)[i]); + } +} + +void SplittingFilter::ThreeBandsAnalysis(const ChannelBuffer* data, + ChannelBuffer* bands) { + RTC_DCHECK_EQ(three_band_filter_banks_.size(), data->num_channels()); + RTC_DCHECK_LE(data->num_channels(), three_band_filter_banks_.size()); + RTC_DCHECK_LE(data->num_channels(), bands->num_channels()); + RTC_DCHECK_EQ(data->num_frames(), ThreeBandFilterBank::kFullBandSize); + RTC_DCHECK_EQ(bands->num_frames(), ThreeBandFilterBank::kFullBandSize); + RTC_DCHECK_EQ(bands->num_bands(), ThreeBandFilterBank::kNumBands); + RTC_DCHECK_EQ(bands->num_frames_per_band(), + ThreeBandFilterBank::kSplitBandSize); + + for (size_t i = 0; i < three_band_filter_banks_.size(); ++i) { + three_band_filter_banks_[i].Analysis( + ArrayView( + data->channels_view()[i].data(), + ThreeBandFilterBank::kFullBandSize), + ArrayView, ThreeBandFilterBank::kNumBands>( + bands->bands_view(i).data(), ThreeBandFilterBank::kNumBands)); + } +} + +void SplittingFilter::ThreeBandsSynthesis(const ChannelBuffer* bands, + ChannelBuffer* data) { + RTC_DCHECK_LE(data->num_channels(), three_band_filter_banks_.size()); + RTC_DCHECK_LE(data->num_channels(), bands->num_channels()); + RTC_DCHECK_LE(data->num_channels(), three_band_filter_banks_.size()); + RTC_DCHECK_EQ(data->num_frames(), ThreeBandFilterBank::kFullBandSize); + RTC_DCHECK_EQ(bands->num_frames(), ThreeBandFilterBank::kFullBandSize); + RTC_DCHECK_EQ(bands->num_bands(), ThreeBandFilterBank::kNumBands); + RTC_DCHECK_EQ(bands->num_frames_per_band(), + ThreeBandFilterBank::kSplitBandSize); + + for (size_t i = 0; i < data->num_channels(); ++i) { + three_band_filter_banks_[i].Synthesis( + ArrayView, ThreeBandFilterBank::kNumBands>( + bands->bands_view(i).data(), ThreeBandFilterBank::kNumBands), + ArrayView( + data->channels_view()[i].data(), + ThreeBandFilterBank::kFullBandSize)); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/splitting_filter.h b/pkg/apm/webrtc/modules/audio_processing/splitting_filter.h new file mode 100644 index 00000000..e578dd07 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/splitting_filter.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_SPLITTING_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_SPLITTING_FILTER_H_ + +#include +#include +#include + +#include "common_audio/channel_buffer.h" +#include "modules/audio_processing/three_band_filter_bank.h" + +namespace webrtc { + +struct TwoBandsStates { + TwoBandsStates() { + memset(analysis_state1, 0, sizeof(analysis_state1)); + memset(analysis_state2, 0, sizeof(analysis_state2)); + memset(synthesis_state1, 0, sizeof(synthesis_state1)); + memset(synthesis_state2, 0, sizeof(synthesis_state2)); + } + + static const int kStateSize = 6; + int analysis_state1[kStateSize]; + int analysis_state2[kStateSize]; + int synthesis_state1[kStateSize]; + int synthesis_state2[kStateSize]; +}; + +// Splitting filter which is able to split into and merge from 2 or 3 frequency +// bands. The number of channels needs to be provided at construction time. +// +// For each block, Analysis() is called to split into bands and then Synthesis() +// to merge these bands again. The input and output signals are contained in +// ChannelBuffers and for the different bands an array of ChannelBuffers is +// used. +class SplittingFilter { + public: + SplittingFilter(size_t num_channels, size_t num_bands, size_t num_frames); + ~SplittingFilter(); + + void Analysis(const ChannelBuffer* data, ChannelBuffer* bands); + void Synthesis(const ChannelBuffer* bands, ChannelBuffer* data); + + private: + // Two-band analysis and synthesis work for 640 samples or less. + void TwoBandsAnalysis(const ChannelBuffer* data, + ChannelBuffer* bands); + void TwoBandsSynthesis(const ChannelBuffer* bands, + ChannelBuffer* data); + void ThreeBandsAnalysis(const ChannelBuffer* data, + ChannelBuffer* bands); + void ThreeBandsSynthesis(const ChannelBuffer* bands, + ChannelBuffer* data); + void InitBuffers(); + + const size_t num_bands_; + std::vector two_bands_states_; + std::vector three_band_filter_banks_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_SPLITTING_FILTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/three_band_filter_bank.cc b/pkg/apm/webrtc/modules/audio_processing/three_band_filter_bank.cc new file mode 100644 index 00000000..d69db9bf --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/three_band_filter_bank.cc @@ -0,0 +1,273 @@ +/* + * 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. + */ + +// An implementation of a 3-band FIR filter-bank with DCT modulation, similar to +// the proposed in "Multirate Signal Processing for Communication Systems" by +// Fredric J Harris. +// +// The idea is to take a heterodyne system and change the order of the +// components to get something which is efficient to implement digitally. +// +// It is possible to separate the filter using the noble identity as follows: +// +// H(z) = H0(z^3) + z^-1 * H1(z^3) + z^-2 * H2(z^3) +// +// This is used in the analysis stage to first downsample serial to parallel +// and then filter each branch with one of these polyphase decompositions of the +// lowpass prototype. Because each filter is only a modulation of the prototype, +// it is enough to multiply each coefficient by the respective cosine value to +// shift it to the desired band. But because the cosine period is 12 samples, +// it requires separating the prototype even further using the noble identity. +// After filtering and modulating for each band, the output of all filters is +// accumulated to get the downsampled bands. +// +// A similar logic can be applied to the synthesis stage. + +#include "modules/audio_processing/three_band_filter_bank.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// Factors to take into account when choosing `kFilterSize`: +// 1. Higher `kFilterSize`, means faster transition, which ensures less +// aliasing. This is especially important when there is non-linear +// processing between the splitting and merging. +// 2. The delay that this filter bank introduces is +// `kNumBands` * `kSparsity` * `kFilterSize` / 2, so it increases linearly +// with `kFilterSize`. +// 3. The computation complexity also increases linearly with `kFilterSize`. + +// The Matlab code to generate these `kFilterCoeffs` is: +// +// N = kNumBands * kSparsity * kFilterSize - 1; +// h = fir1(N, 1 / (2 * kNumBands), kaiser(N + 1, 3.5)); +// reshape(h, kNumBands * kSparsity, kFilterSize); +// +// The code below uses the values of kFilterSize, kNumBands and kSparsity +// specified in the header. + +// Because the total bandwidth of the lower and higher band is double the middle +// one (because of the spectrum parity), the low-pass prototype is half the +// bandwidth of 1 / (2 * `kNumBands`) and is then shifted with cosine modulation +// to the right places. +// A Kaiser window is used because of its flexibility and the alpha is set to +// 3.5, since that sets a stop band attenuation of 40dB ensuring a fast +// transition. + +constexpr int kSubSampling = ThreeBandFilterBank::kNumBands; +constexpr int kDctSize = ThreeBandFilterBank::kNumBands; +static_assert(ThreeBandFilterBank::kNumBands * + ThreeBandFilterBank::kSplitBandSize == + ThreeBandFilterBank::kFullBandSize, + "The full band must be split in equally sized subbands"); + +const float + kFilterCoeffs[ThreeBandFilterBank::kNumNonZeroFilters][kFilterSize] = { + {-0.00047749f, -0.00496888f, +0.16547118f, +0.00425496f}, + {-0.00173287f, -0.01585778f, +0.14989004f, +0.00994113f}, + {-0.00304815f, -0.02536082f, +0.12154542f, +0.01157993f}, + {-0.00346946f, -0.02587886f, +0.04760441f, +0.00607594f}, + {-0.00154717f, -0.01136076f, +0.01387458f, +0.00186353f}, + {+0.00186353f, +0.01387458f, -0.01136076f, -0.00154717f}, + {+0.00607594f, +0.04760441f, -0.02587886f, -0.00346946f}, + {+0.00983212f, +0.08543175f, -0.02982767f, -0.00383509f}, + {+0.00994113f, +0.14989004f, -0.01585778f, -0.00173287f}, + {+0.00425496f, +0.16547118f, -0.00496888f, -0.00047749f}}; + +constexpr int kZeroFilterIndex1 = 3; +constexpr int kZeroFilterIndex2 = 9; + +const float kDctModulation[ThreeBandFilterBank::kNumNonZeroFilters][kDctSize] = + {{2.f, 2.f, 2.f}, + {1.73205077f, 0.f, -1.73205077f}, + {1.f, -2.f, 1.f}, + {-1.f, 2.f, -1.f}, + {-1.73205077f, 0.f, 1.73205077f}, + {-2.f, -2.f, -2.f}, + {-1.73205077f, 0.f, 1.73205077f}, + {-1.f, 2.f, -1.f}, + {1.f, -2.f, 1.f}, + {1.73205077f, 0.f, -1.73205077f}}; + +// Filters the input signal `in` with the filter `filter` using a shift by +// `in_shift`, taking into account the previous state. +void FilterCore(ArrayView filter, + ArrayView in, + const int in_shift, + ArrayView out, + ArrayView state) { + constexpr int kMaxInShift = (kStride - 1); + RTC_DCHECK_GE(in_shift, 0); + RTC_DCHECK_LE(in_shift, kMaxInShift); + std::fill(out.begin(), out.end(), 0.f); + + for (int k = 0; k < in_shift; ++k) { + for (int i = 0, j = kMemorySize + k - in_shift; i < kFilterSize; + ++i, j -= kStride) { + out[k] += state[j] * filter[i]; + } + } + + for (int k = in_shift, shift = 0; k < kFilterSize * kStride; ++k, ++shift) { + RTC_DCHECK_GE(shift, 0); + const int loop_limit = std::min(kFilterSize, 1 + (shift >> kStrideLog2)); + for (int i = 0, j = shift; i < loop_limit; ++i, j -= kStride) { + out[k] += in[j] * filter[i]; + } + for (int i = loop_limit, j = kMemorySize + shift - loop_limit * kStride; + i < kFilterSize; ++i, j -= kStride) { + out[k] += state[j] * filter[i]; + } + } + + for (int k = kFilterSize * kStride, shift = kFilterSize * kStride - in_shift; + k < ThreeBandFilterBank::kSplitBandSize; ++k, ++shift) { + for (int i = 0, j = shift; i < kFilterSize; ++i, j -= kStride) { + out[k] += in[j] * filter[i]; + } + } + + // Update current state. + std::copy(in.begin() + ThreeBandFilterBank::kSplitBandSize - kMemorySize, + in.end(), state.begin()); +} + +} // namespace + +// Because the low-pass filter prototype has half bandwidth it is possible to +// use a DCT to shift it in both directions at the same time, to the center +// frequencies [1 / 12, 3 / 12, 5 / 12]. +ThreeBandFilterBank::ThreeBandFilterBank() { + RTC_DCHECK_EQ(state_analysis_.size(), kNumNonZeroFilters); + RTC_DCHECK_EQ(state_synthesis_.size(), kNumNonZeroFilters); + for (int k = 0; k < kNumNonZeroFilters; ++k) { + RTC_DCHECK_EQ(state_analysis_[k].size(), kMemorySize); + RTC_DCHECK_EQ(state_synthesis_[k].size(), kMemorySize); + + state_analysis_[k].fill(0.f); + state_synthesis_[k].fill(0.f); + } +} + +ThreeBandFilterBank::~ThreeBandFilterBank() = default; + +// The analysis can be separated in these steps: +// 1. Serial to parallel downsampling by a factor of `kNumBands`. +// 2. Filtering of `kSparsity` different delayed signals with polyphase +// decomposition of the low-pass prototype filter and upsampled by a factor +// of `kSparsity`. +// 3. Modulating with cosines and accumulating to get the desired band. +void ThreeBandFilterBank::Analysis( + ArrayView in, + ArrayView, ThreeBandFilterBank::kNumBands> out) { + // Initialize the output to zero. + for (int band = 0; band < ThreeBandFilterBank::kNumBands; ++band) { + RTC_DCHECK_EQ(out[band].size(), kSplitBandSize); + std::fill(out[band].begin(), out[band].end(), 0); + } + + for (int downsampling_index = 0; downsampling_index < kSubSampling; + ++downsampling_index) { + // Downsample to form the filter input. + std::array in_subsampled; + for (int k = 0; k < kSplitBandSize; ++k) { + in_subsampled[k] = + in[(kSubSampling - 1) - downsampling_index + kSubSampling * k]; + } + + for (int in_shift = 0; in_shift < kStride; ++in_shift) { + // Choose filter, skip zero filters. + const int index = downsampling_index + in_shift * kSubSampling; + if (index == kZeroFilterIndex1 || index == kZeroFilterIndex2) { + continue; + } + const int filter_index = + index < kZeroFilterIndex1 + ? index + : (index < kZeroFilterIndex2 ? index - 1 : index - 2); + + ArrayView filter(kFilterCoeffs[filter_index]); + ArrayView dct_modulation( + kDctModulation[filter_index]); + ArrayView state(state_analysis_[filter_index]); + + // Filter. + std::array out_subsampled; + FilterCore(filter, in_subsampled, in_shift, out_subsampled, state); + + // Band and modulate the output. + for (int band = 0; band < ThreeBandFilterBank::kNumBands; ++band) { + float* out_band = out[band].data(); + for (int n = 0; n < kSplitBandSize; ++n) { + out_band[n] += dct_modulation[band] * out_subsampled[n]; + } + } + } + } +} + +// The synthesis can be separated in these steps: +// 1. Modulating with cosines. +// 2. Filtering each one with a polyphase decomposition of the low-pass +// prototype filter upsampled by a factor of `kSparsity` and accumulating +// `kSparsity` signals with different delays. +// 3. Parallel to serial upsampling by a factor of `kNumBands`. +void ThreeBandFilterBank::Synthesis( + ArrayView, ThreeBandFilterBank::kNumBands> in, + ArrayView out) { + std::fill(out.begin(), out.end(), 0); + for (int upsampling_index = 0; upsampling_index < kSubSampling; + ++upsampling_index) { + for (int in_shift = 0; in_shift < kStride; ++in_shift) { + // Choose filter, skip zero filters. + const int index = upsampling_index + in_shift * kSubSampling; + if (index == kZeroFilterIndex1 || index == kZeroFilterIndex2) { + continue; + } + const int filter_index = + index < kZeroFilterIndex1 + ? index + : (index < kZeroFilterIndex2 ? index - 1 : index - 2); + + ArrayView filter(kFilterCoeffs[filter_index]); + ArrayView dct_modulation( + kDctModulation[filter_index]); + ArrayView state(state_synthesis_[filter_index]); + + // Prepare filter input by modulating the banded input. + std::array in_subsampled; + std::fill(in_subsampled.begin(), in_subsampled.end(), 0.f); + for (int band = 0; band < ThreeBandFilterBank::kNumBands; ++band) { + RTC_DCHECK_EQ(in[band].size(), kSplitBandSize); + const float* in_band = in[band].data(); + for (int n = 0; n < kSplitBandSize; ++n) { + in_subsampled[n] += dct_modulation[band] * in_band[n]; + } + } + + // Filter. + std::array out_subsampled; + FilterCore(filter, in_subsampled, in_shift, out_subsampled, state); + + // Upsample. + constexpr float kUpsamplingScaling = kSubSampling; + for (int k = 0; k < kSplitBandSize; ++k) { + out[upsampling_index + kSubSampling * k] += + kUpsamplingScaling * out_subsampled[k]; + } + } + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/three_band_filter_bank.h b/pkg/apm/webrtc/modules/audio_processing/three_band_filter_bank.h new file mode 100644 index 00000000..5c7dc1c8 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/three_band_filter_bank.h @@ -0,0 +1,77 @@ +/* + * 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 MODULES_AUDIO_PROCESSING_THREE_BAND_FILTER_BANK_H_ +#define MODULES_AUDIO_PROCESSING_THREE_BAND_FILTER_BANK_H_ + +#include +#include +#include +#include + +#include "api/array_view.h" + +namespace webrtc { + +constexpr int kSparsity = 4; +constexpr int kStrideLog2 = 2; +constexpr int kStride = 1 << kStrideLog2; +constexpr int kNumZeroFilters = 2; +constexpr int kFilterSize = 4; +constexpr int kMemorySize = kFilterSize * kStride - 1; +static_assert(kMemorySize == 15, + "The memory size must be sufficient to provide memory for the " + "shifted filters"); + +// An implementation of a 3-band FIR filter-bank with DCT modulation, similar to +// the proposed in "Multirate Signal Processing for Communication Systems" by +// Fredric J Harris. +// The low-pass filter prototype has these characteristics: +// * Pass-band ripple = 0.3dB +// * Pass-band frequency = 0.147 (7kHz at 48kHz) +// * Stop-band attenuation = 40dB +// * Stop-band frequency = 0.192 (9.2kHz at 48kHz) +// * Delay = 24 samples (500us at 48kHz) +// * Linear phase +// This filter bank does not satisfy perfect reconstruction. The SNR after +// analysis and synthesis (with no processing in between) is approximately 9.5dB +// depending on the input signal after compensating for the delay. +class ThreeBandFilterBank final { + public: + static const int kNumBands = 3; + static const int kFullBandSize = 480; + static const int kSplitBandSize = + ThreeBandFilterBank::kFullBandSize / ThreeBandFilterBank::kNumBands; + static const int kNumNonZeroFilters = + kSparsity * ThreeBandFilterBank::kNumBands - kNumZeroFilters; + + ThreeBandFilterBank(); + ~ThreeBandFilterBank(); + + // Splits `in` of size kFullBandSize into 3 downsampled frequency bands in + // `out`, each of size 160. + void Analysis(ArrayView in, + ArrayView, kNumBands> out); + + // Merges the 3 downsampled frequency bands in `in`, each of size 160, into + // `out`, which is of size kFullBandSize. + void Synthesis(ArrayView, kNumBands> in, + ArrayView out); + + private: + std::array, kNumNonZeroFilters> + state_analysis_; + std::array, kNumNonZeroFilters> + state_synthesis_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_THREE_BAND_FILTER_BANK_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc b/pkg/apm/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc new file mode 100644 index 00000000..19faadfc --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/audio_processing/utility/cascaded_biquad_filter.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +CascadedBiQuadFilter::BiQuadParam::BiQuadParam(std::complex zero, + std::complex pole, + float gain, + bool mirror_zero_along_i_axis) + : zero(zero), + pole(pole), + gain(gain), + mirror_zero_along_i_axis(mirror_zero_along_i_axis) {} + +CascadedBiQuadFilter::BiQuadParam::BiQuadParam(const BiQuadParam&) = default; + +CascadedBiQuadFilter::BiQuad::BiQuad( + const CascadedBiQuadFilter::BiQuadParam& param) + : x(), y() { + float z_r = std::real(param.zero); + float z_i = std::imag(param.zero); + float p_r = std::real(param.pole); + float p_i = std::imag(param.pole); + float gain = param.gain; + + if (param.mirror_zero_along_i_axis) { + // Assuming zeroes at z_r and -z_r. + RTC_DCHECK(z_i == 0.f); + coefficients.b[0] = gain * 1.f; + coefficients.b[1] = 0.f; + coefficients.b[2] = gain * -(z_r * z_r); + } else { + // Assuming zeros at (z_r + z_i*i) and (z_r - z_i*i). + coefficients.b[0] = gain * 1.f; + coefficients.b[1] = gain * -2.f * z_r; + coefficients.b[2] = gain * (z_r * z_r + z_i * z_i); + } + + // Assuming poles at (p_r + p_i*i) and (p_r - p_i*i). + coefficients.a[0] = -2.f * p_r; + coefficients.a[1] = p_r * p_r + p_i * p_i; +} + +void CascadedBiQuadFilter::BiQuad::BiQuad::Reset() { + x[0] = x[1] = y[0] = y[1] = 0.f; +} + +CascadedBiQuadFilter::CascadedBiQuadFilter( + const CascadedBiQuadFilter::BiQuadCoefficients& coefficients, + size_t num_biquads) + : biquads_(num_biquads, BiQuad(coefficients)) {} + +CascadedBiQuadFilter::CascadedBiQuadFilter( + const std::vector& biquad_params) { + for (const auto& param : biquad_params) { + biquads_.push_back(BiQuad(param)); + } +} + +CascadedBiQuadFilter::~CascadedBiQuadFilter() = default; + +void CascadedBiQuadFilter::Process(ArrayView x, + ArrayView y) { + if (biquads_.size() > 0) { + ApplyBiQuad(x, y, &biquads_[0]); + for (size_t k = 1; k < biquads_.size(); ++k) { + ApplyBiQuad(y, y, &biquads_[k]); + } + } else { + std::copy(x.begin(), x.end(), y.begin()); + } +} + +void CascadedBiQuadFilter::Process(ArrayView y) { + for (auto& biquad : biquads_) { + ApplyBiQuad(y, y, &biquad); + } +} + +void CascadedBiQuadFilter::Reset() { + for (auto& biquad : biquads_) { + biquad.Reset(); + } +} + +void CascadedBiQuadFilter::ApplyBiQuad(ArrayView x, + ArrayView y, + CascadedBiQuadFilter::BiQuad* biquad) { + RTC_DCHECK_EQ(x.size(), y.size()); + const float c_a_0 = biquad->coefficients.a[0]; + const float c_a_1 = biquad->coefficients.a[1]; + const float c_b_0 = biquad->coefficients.b[0]; + const float c_b_1 = biquad->coefficients.b[1]; + const float c_b_2 = biquad->coefficients.b[2]; + float m_x_0 = biquad->x[0]; + float m_x_1 = biquad->x[1]; + float m_y_0 = biquad->y[0]; + float m_y_1 = biquad->y[1]; + for (size_t k = 0; k < x.size(); ++k) { + const float tmp = x[k]; + y[k] = c_b_0 * tmp + c_b_1 * m_x_0 + c_b_2 * m_x_1 - c_a_0 * m_y_0 - + c_a_1 * m_y_1; + m_x_1 = m_x_0; + m_x_0 = tmp; + m_y_1 = m_y_0; + m_y_0 = y[k]; + } + biquad->x[0] = m_x_0; + biquad->x[1] = m_x_1; + biquad->y[0] = m_y_0; + biquad->y[1] = m_y_1; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.h b/pkg/apm/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.h new file mode 100644 index 00000000..799b4394 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_CASCADED_BIQUAD_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_CASCADED_BIQUAD_FILTER_H_ + +#include + +#include +#include + +#include "api/array_view.h" + +namespace webrtc { + +// Applies a number of biquads in a cascaded manner. The filter implementation +// is direct form 1. +class CascadedBiQuadFilter { + public: + struct BiQuadParam { + BiQuadParam(std::complex zero, + std::complex pole, + float gain, + bool mirror_zero_along_i_axis = false); + explicit BiQuadParam(const BiQuadParam&); + std::complex zero; + std::complex pole; + float gain; + bool mirror_zero_along_i_axis; + }; + + struct BiQuadCoefficients { + float b[3]; + float a[2]; + }; + + struct BiQuad { + explicit BiQuad(const BiQuadCoefficients& coefficients) + : coefficients(coefficients), x(), y() {} + explicit BiQuad(const CascadedBiQuadFilter::BiQuadParam& param); + void Reset(); + BiQuadCoefficients coefficients; + float x[2]; + float y[2]; + }; + + CascadedBiQuadFilter( + const CascadedBiQuadFilter::BiQuadCoefficients& coefficients, + size_t num_biquads); + explicit CascadedBiQuadFilter( + const std::vector& biquad_params); + ~CascadedBiQuadFilter(); + CascadedBiQuadFilter(const CascadedBiQuadFilter&) = delete; + CascadedBiQuadFilter& operator=(const CascadedBiQuadFilter&) = delete; + + // Applies the biquads on the values in x in order to form the output in y. + void Process(ArrayView x, ArrayView y); + // Applies the biquads on the values in y in an in-place manner. + void Process(ArrayView y); + // Resets the filter to its initial state. + void Reset(); + + private: + void ApplyBiQuad(ArrayView x, + ArrayView y, + CascadedBiQuadFilter::BiQuad* biquad); + + std::vector biquads_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_CASCADED_BIQUAD_FILTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator.cc b/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator.cc new file mode 100644 index 00000000..6868392f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator.cc @@ -0,0 +1,708 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/utility/delay_estimator.h" + +#include +#include + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Number of right shifts for scaling is linearly depending on number of bits in +// the far-end binary spectrum. +static const int kShiftsAtZero = 13; // Right shifts at zero binary spectrum. +static const int kShiftsLinearSlope = 3; + +static const int32_t kProbabilityOffset = 1024; // 2 in Q9. +static const int32_t kProbabilityLowerLimit = 8704; // 17 in Q9. +static const int32_t kProbabilityMinSpread = 2816; // 5.5 in Q9. + +// Robust validation settings +static const float kHistogramMax = 3000.f; +static const float kLastHistogramMax = 250.f; +static const float kMinHistogramThreshold = 1.5f; +static const int kMinRequiredHits = 10; +static const int kMaxHitsWhenPossiblyNonCausal = 10; +static const int kMaxHitsWhenPossiblyCausal = 1000; +static const float kQ14Scaling = 1.f / (1 << 14); // Scaling by 2^14 to get Q0. +static const float kFractionSlope = 0.05f; +static const float kMinFractionWhenPossiblyCausal = 0.5f; +static const float kMinFractionWhenPossiblyNonCausal = 0.25f; + +} // namespace + +// Counts and returns number of bits of a 32-bit word. +static int BitCount(uint32_t u32) { + uint32_t tmp = + u32 - ((u32 >> 1) & 033333333333) - ((u32 >> 2) & 011111111111); + tmp = ((tmp + (tmp >> 3)) & 030707070707); + tmp = (tmp + (tmp >> 6)); + tmp = (tmp + (tmp >> 12) + (tmp >> 24)) & 077; + + return ((int)tmp); +} + +// Compares the `binary_vector` with all rows of the `binary_matrix` and counts +// per row the number of times they have the same value. +// +// Inputs: +// - binary_vector : binary "vector" stored in a long +// - binary_matrix : binary "matrix" stored as a vector of long +// - matrix_size : size of binary "matrix" +// +// Output: +// - bit_counts : "Vector" stored as a long, containing for each +// row the number of times the matrix row and the +// input vector have the same value +// +static void BitCountComparison(uint32_t binary_vector, + const uint32_t* binary_matrix, + int matrix_size, + int32_t* bit_counts) { + int n = 0; + + // Compare `binary_vector` with all rows of the `binary_matrix` + for (; n < matrix_size; n++) { + bit_counts[n] = (int32_t)BitCount(binary_vector ^ binary_matrix[n]); + } +} + +// Collects necessary statistics for the HistogramBasedValidation(). This +// function has to be called prior to calling HistogramBasedValidation(). The +// statistics updated and used by the HistogramBasedValidation() are: +// 1. the number of `candidate_hits`, which states for how long we have had the +// same `candidate_delay` +// 2. the `histogram` of candidate delays over time. This histogram is +// weighted with respect to a reliability measure and time-varying to cope +// with possible delay shifts. +// For further description see commented code. +// +// Inputs: +// - candidate_delay : The delay to validate. +// - valley_depth_q14 : The cost function has a valley/minimum at the +// `candidate_delay` location. `valley_depth_q14` is the +// cost function difference between the minimum and +// maximum locations. The value is in the Q14 domain. +// - valley_level_q14 : Is the cost function value at the minimum, in Q14. +static void UpdateRobustValidationStatistics(BinaryDelayEstimator* self, + int candidate_delay, + int32_t valley_depth_q14, + int32_t valley_level_q14) { + const float valley_depth = valley_depth_q14 * kQ14Scaling; + float decrease_in_last_set = valley_depth; + const int max_hits_for_slow_change = (candidate_delay < self->last_delay) + ? kMaxHitsWhenPossiblyNonCausal + : kMaxHitsWhenPossiblyCausal; + int i = 0; + + RTC_DCHECK_EQ(self->history_size, self->farend->history_size); + // Reset `candidate_hits` if we have a new candidate. + if (candidate_delay != self->last_candidate_delay) { + self->candidate_hits = 0; + self->last_candidate_delay = candidate_delay; + } + self->candidate_hits++; + + // The `histogram` is updated differently across the bins. + // 1. The `candidate_delay` histogram bin is increased with the + // `valley_depth`, which is a simple measure of how reliable the + // `candidate_delay` is. The histogram is not increased above + // `kHistogramMax`. + self->histogram[candidate_delay] += valley_depth; + if (self->histogram[candidate_delay] > kHistogramMax) { + self->histogram[candidate_delay] = kHistogramMax; + } + // 2. The histogram bins in the neighborhood of `candidate_delay` are + // unaffected. The neighborhood is defined as x + {-2, -1, 0, 1}. + // 3. The histogram bins in the neighborhood of `last_delay` are decreased + // with `decrease_in_last_set`. This value equals the difference between + // the cost function values at the locations `candidate_delay` and + // `last_delay` until we reach `max_hits_for_slow_change` consecutive hits + // at the `candidate_delay`. If we exceed this amount of hits the + // `candidate_delay` is a "potential" candidate and we start decreasing + // these histogram bins more rapidly with `valley_depth`. + if (self->candidate_hits < max_hits_for_slow_change) { + decrease_in_last_set = + (self->mean_bit_counts[self->compare_delay] - valley_level_q14) * + kQ14Scaling; + } + // 4. All other bins are decreased with `valley_depth`. + // TODO(bjornv): Investigate how to make this loop more efficient. Split up + // the loop? Remove parts that doesn't add too much. + for (i = 0; i < self->history_size; ++i) { + int is_in_last_set = (i >= self->last_delay - 2) && + (i <= self->last_delay + 1) && (i != candidate_delay); + int is_in_candidate_set = + (i >= candidate_delay - 2) && (i <= candidate_delay + 1); + self->histogram[i] -= + decrease_in_last_set * is_in_last_set + + valley_depth * (!is_in_last_set && !is_in_candidate_set); + // 5. No histogram bin can go below 0. + if (self->histogram[i] < 0) { + self->histogram[i] = 0; + } + } +} + +// Validates the `candidate_delay`, estimated in WebRtc_ProcessBinarySpectrum(), +// based on a mix of counting concurring hits with a modified histogram +// of recent delay estimates. In brief a candidate is valid (returns 1) if it +// is the most likely according to the histogram. There are a couple of +// exceptions that are worth mentioning: +// 1. If the `candidate_delay` < `last_delay` it can be that we are in a +// non-causal state, breaking a possible echo control algorithm. Hence, we +// open up for a quicker change by allowing the change even if the +// `candidate_delay` is not the most likely one according to the histogram. +// 2. There's a minimum number of hits (kMinRequiredHits) and the histogram +// value has to reached a minimum (kMinHistogramThreshold) to be valid. +// 3. The action is also depending on the filter length used for echo control. +// If the delay difference is larger than what the filter can capture, we +// also move quicker towards a change. +// For further description see commented code. +// +// Input: +// - candidate_delay : The delay to validate. +// +// Return value: +// - is_histogram_valid : 1 - The `candidate_delay` is valid. +// 0 - Otherwise. +static int HistogramBasedValidation(const BinaryDelayEstimator* self, + int candidate_delay) { + float fraction = 1.f; + float histogram_threshold = self->histogram[self->compare_delay]; + const int delay_difference = candidate_delay - self->last_delay; + int is_histogram_valid = 0; + + // The histogram based validation of `candidate_delay` is done by comparing + // the `histogram` at bin `candidate_delay` with a `histogram_threshold`. + // This `histogram_threshold` equals a `fraction` of the `histogram` at bin + // `last_delay`. The `fraction` is a piecewise linear function of the + // `delay_difference` between the `candidate_delay` and the `last_delay` + // allowing for a quicker move if + // i) a potential echo control filter can not handle these large differences. + // ii) keeping `last_delay` instead of updating to `candidate_delay` could + // force an echo control into a non-causal state. + // We further require the histogram to have reached a minimum value of + // `kMinHistogramThreshold`. In addition, we also require the number of + // `candidate_hits` to be more than `kMinRequiredHits` to remove spurious + // values. + + // Calculate a comparison histogram value (`histogram_threshold`) that is + // depending on the distance between the `candidate_delay` and `last_delay`. + // TODO(bjornv): How much can we gain by turning the fraction calculation + // into tables? + if (delay_difference > self->allowed_offset) { + fraction = 1.f - kFractionSlope * (delay_difference - self->allowed_offset); + fraction = (fraction > kMinFractionWhenPossiblyCausal + ? fraction + : kMinFractionWhenPossiblyCausal); + } else if (delay_difference < 0) { + fraction = + kMinFractionWhenPossiblyNonCausal - kFractionSlope * delay_difference; + fraction = (fraction > 1.f ? 1.f : fraction); + } + histogram_threshold *= fraction; + histogram_threshold = + (histogram_threshold > kMinHistogramThreshold ? histogram_threshold + : kMinHistogramThreshold); + + is_histogram_valid = + (self->histogram[candidate_delay] >= histogram_threshold) && + (self->candidate_hits > kMinRequiredHits); + + return is_histogram_valid; +} + +// Performs a robust validation of the `candidate_delay` estimated in +// WebRtc_ProcessBinarySpectrum(). The algorithm takes the +// `is_instantaneous_valid` and the `is_histogram_valid` and combines them +// into a robust validation. The HistogramBasedValidation() has to be called +// prior to this call. +// For further description on how the combination is done, see commented code. +// +// Inputs: +// - candidate_delay : The delay to validate. +// - is_instantaneous_valid : The instantaneous validation performed in +// WebRtc_ProcessBinarySpectrum(). +// - is_histogram_valid : The histogram based validation. +// +// Return value: +// - is_robust : 1 - The candidate_delay is valid according to a +// combination of the two inputs. +// : 0 - Otherwise. +static int RobustValidation(const BinaryDelayEstimator* self, + int candidate_delay, + int is_instantaneous_valid, + int is_histogram_valid) { + int is_robust = 0; + + // The final robust validation is based on the two algorithms; 1) the + // `is_instantaneous_valid` and 2) the histogram based with result stored in + // `is_histogram_valid`. + // i) Before we actually have a valid estimate (`last_delay` == -2), we say + // a candidate is valid if either algorithm states so + // (`is_instantaneous_valid` OR `is_histogram_valid`). + is_robust = + (self->last_delay < 0) && (is_instantaneous_valid || is_histogram_valid); + // ii) Otherwise, we need both algorithms to be certain + // (`is_instantaneous_valid` AND `is_histogram_valid`) + is_robust |= is_instantaneous_valid && is_histogram_valid; + // iii) With one exception, i.e., the histogram based algorithm can overrule + // the instantaneous one if `is_histogram_valid` = 1 and the histogram + // is significantly strong. + is_robust |= is_histogram_valid && + (self->histogram[candidate_delay] > self->last_delay_histogram); + + return is_robust; +} + +void WebRtc_FreeBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self) { + if (self == NULL) { + return; + } + + free(self->binary_far_history); + self->binary_far_history = NULL; + + free(self->far_bit_counts); + self->far_bit_counts = NULL; + + free(self); +} + +BinaryDelayEstimatorFarend* WebRtc_CreateBinaryDelayEstimatorFarend( + int history_size) { + BinaryDelayEstimatorFarend* self = NULL; + + if (history_size > 1) { + // Sanity conditions fulfilled. + self = static_cast( + malloc(sizeof(BinaryDelayEstimatorFarend))); + } + if (self == NULL) { + return NULL; + } + + self->history_size = 0; + self->binary_far_history = NULL; + self->far_bit_counts = NULL; + if (WebRtc_AllocateFarendBufferMemory(self, history_size) == 0) { + WebRtc_FreeBinaryDelayEstimatorFarend(self); + self = NULL; + } + return self; +} + +int WebRtc_AllocateFarendBufferMemory(BinaryDelayEstimatorFarend* self, + int history_size) { + RTC_DCHECK(self); + // (Re-)Allocate memory for history buffers. + self->binary_far_history = static_cast( + realloc(self->binary_far_history, + history_size * sizeof(*self->binary_far_history))); + self->far_bit_counts = static_cast(realloc( + self->far_bit_counts, history_size * sizeof(*self->far_bit_counts))); + if ((self->binary_far_history == NULL) || (self->far_bit_counts == NULL)) { + history_size = 0; + } + // Fill with zeros if we have expanded the buffers. + if (history_size > self->history_size) { + int size_diff = history_size - self->history_size; + memset(&self->binary_far_history[self->history_size], 0, + sizeof(*self->binary_far_history) * size_diff); + memset(&self->far_bit_counts[self->history_size], 0, + sizeof(*self->far_bit_counts) * size_diff); + } + self->history_size = history_size; + + return self->history_size; +} + +void WebRtc_InitBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self) { + RTC_DCHECK(self); + memset(self->binary_far_history, 0, sizeof(uint32_t) * self->history_size); + memset(self->far_bit_counts, 0, sizeof(int) * self->history_size); +} + +void WebRtc_SoftResetBinaryDelayEstimatorFarend( + BinaryDelayEstimatorFarend* self, + int delay_shift) { + int abs_shift = abs(delay_shift); + int shift_size = 0; + int dest_index = 0; + int src_index = 0; + int padding_index = 0; + + RTC_DCHECK(self); + shift_size = self->history_size - abs_shift; + RTC_DCHECK_GT(shift_size, 0); + if (delay_shift == 0) { + return; + } else if (delay_shift > 0) { + dest_index = abs_shift; + } else if (delay_shift < 0) { + src_index = abs_shift; + padding_index = shift_size; + } + + // Shift and zero pad buffers. + memmove(&self->binary_far_history[dest_index], + &self->binary_far_history[src_index], + sizeof(*self->binary_far_history) * shift_size); + memset(&self->binary_far_history[padding_index], 0, + sizeof(*self->binary_far_history) * abs_shift); + memmove(&self->far_bit_counts[dest_index], &self->far_bit_counts[src_index], + sizeof(*self->far_bit_counts) * shift_size); + memset(&self->far_bit_counts[padding_index], 0, + sizeof(*self->far_bit_counts) * abs_shift); +} + +void WebRtc_AddBinaryFarSpectrum(BinaryDelayEstimatorFarend* handle, + uint32_t binary_far_spectrum) { + RTC_DCHECK(handle); + // Shift binary spectrum history and insert current `binary_far_spectrum`. + memmove(&(handle->binary_far_history[1]), &(handle->binary_far_history[0]), + (handle->history_size - 1) * sizeof(uint32_t)); + handle->binary_far_history[0] = binary_far_spectrum; + + // Shift history of far-end binary spectrum bit counts and insert bit count + // of current `binary_far_spectrum`. + memmove(&(handle->far_bit_counts[1]), &(handle->far_bit_counts[0]), + (handle->history_size - 1) * sizeof(int)); + handle->far_bit_counts[0] = BitCount(binary_far_spectrum); +} + +void WebRtc_FreeBinaryDelayEstimator(BinaryDelayEstimator* self) { + if (self == NULL) { + return; + } + + free(self->mean_bit_counts); + self->mean_bit_counts = NULL; + + free(self->bit_counts); + self->bit_counts = NULL; + + free(self->binary_near_history); + self->binary_near_history = NULL; + + free(self->histogram); + self->histogram = NULL; + + // BinaryDelayEstimator does not have ownership of `farend`, hence we do not + // free the memory here. That should be handled separately by the user. + self->farend = NULL; + + free(self); +} + +BinaryDelayEstimator* WebRtc_CreateBinaryDelayEstimator( + BinaryDelayEstimatorFarend* farend, + int max_lookahead) { + BinaryDelayEstimator* self = NULL; + + if ((farend != NULL) && (max_lookahead >= 0)) { + // Sanity conditions fulfilled. + self = static_cast( + malloc(sizeof(BinaryDelayEstimator))); + } + if (self == NULL) { + return NULL; + } + + self->farend = farend; + self->near_history_size = max_lookahead + 1; + self->history_size = 0; + self->robust_validation_enabled = 0; // Disabled by default. + self->allowed_offset = 0; + + self->lookahead = max_lookahead; + + // Allocate memory for spectrum and history buffers. + self->mean_bit_counts = NULL; + self->bit_counts = NULL; + self->histogram = NULL; + self->binary_near_history = static_cast( + malloc((max_lookahead + 1) * sizeof(*self->binary_near_history))); + if (self->binary_near_history == NULL || + WebRtc_AllocateHistoryBufferMemory(self, farend->history_size) == 0) { + WebRtc_FreeBinaryDelayEstimator(self); + self = NULL; + } + + return self; +} + +int WebRtc_AllocateHistoryBufferMemory(BinaryDelayEstimator* self, + int history_size) { + BinaryDelayEstimatorFarend* far = self->farend; + // (Re-)Allocate memory for spectrum and history buffers. + if (history_size != far->history_size) { + // Only update far-end buffers if we need. + history_size = WebRtc_AllocateFarendBufferMemory(far, history_size); + } + // The extra array element in `mean_bit_counts` and `histogram` is a dummy + // element only used while `last_delay` == -2, i.e., before we have a valid + // estimate. + self->mean_bit_counts = static_cast( + realloc(self->mean_bit_counts, + (history_size + 1) * sizeof(*self->mean_bit_counts))); + self->bit_counts = static_cast( + realloc(self->bit_counts, history_size * sizeof(*self->bit_counts))); + self->histogram = static_cast( + realloc(self->histogram, (history_size + 1) * sizeof(*self->histogram))); + + if ((self->mean_bit_counts == NULL) || (self->bit_counts == NULL) || + (self->histogram == NULL)) { + history_size = 0; + } + // Fill with zeros if we have expanded the buffers. + if (history_size > self->history_size) { + int size_diff = history_size - self->history_size; + memset(&self->mean_bit_counts[self->history_size], 0, + sizeof(*self->mean_bit_counts) * size_diff); + memset(&self->bit_counts[self->history_size], 0, + sizeof(*self->bit_counts) * size_diff); + memset(&self->histogram[self->history_size], 0, + sizeof(*self->histogram) * size_diff); + } + self->history_size = history_size; + + return self->history_size; +} + +void WebRtc_InitBinaryDelayEstimator(BinaryDelayEstimator* self) { + int i = 0; + RTC_DCHECK(self); + + memset(self->bit_counts, 0, sizeof(int32_t) * self->history_size); + memset(self->binary_near_history, 0, + sizeof(uint32_t) * self->near_history_size); + for (i = 0; i <= self->history_size; ++i) { + self->mean_bit_counts[i] = (20 << 9); // 20 in Q9. + self->histogram[i] = 0.f; + } + self->minimum_probability = kMaxBitCountsQ9; // 32 in Q9. + self->last_delay_probability = (int)kMaxBitCountsQ9; // 32 in Q9. + + // Default return value if we're unable to estimate. -1 is used for errors. + self->last_delay = -2; + + self->last_candidate_delay = -2; + self->compare_delay = self->history_size; + self->candidate_hits = 0; + self->last_delay_histogram = 0.f; +} + +int WebRtc_SoftResetBinaryDelayEstimator(BinaryDelayEstimator* self, + int delay_shift) { + int lookahead = 0; + RTC_DCHECK(self); + lookahead = self->lookahead; + self->lookahead -= delay_shift; + if (self->lookahead < 0) { + self->lookahead = 0; + } + if (self->lookahead > self->near_history_size - 1) { + self->lookahead = self->near_history_size - 1; + } + return lookahead - self->lookahead; +} + +int WebRtc_ProcessBinarySpectrum(BinaryDelayEstimator* self, + uint32_t binary_near_spectrum) { + int i = 0; + int candidate_delay = -1; + int valid_candidate = 0; + + int32_t value_best_candidate = kMaxBitCountsQ9; + int32_t value_worst_candidate = 0; + int32_t valley_depth = 0; + + RTC_DCHECK(self); + if (self->farend->history_size != self->history_size) { + // Non matching history sizes. + return -1; + } + if (self->near_history_size > 1) { + // If we apply lookahead, shift near-end binary spectrum history. Insert + // current `binary_near_spectrum` and pull out the delayed one. + memmove(&(self->binary_near_history[1]), &(self->binary_near_history[0]), + (self->near_history_size - 1) * sizeof(uint32_t)); + self->binary_near_history[0] = binary_near_spectrum; + binary_near_spectrum = self->binary_near_history[self->lookahead]; + } + + // Compare with delayed spectra and store the `bit_counts` for each delay. + BitCountComparison(binary_near_spectrum, self->farend->binary_far_history, + self->history_size, self->bit_counts); + + // Update `mean_bit_counts`, which is the smoothed version of `bit_counts`. + for (i = 0; i < self->history_size; i++) { + // `bit_counts` is constrained to [0, 32], meaning we can smooth with a + // factor up to 2^26. We use Q9. + int32_t bit_count = (self->bit_counts[i] << 9); // Q9. + + // Update `mean_bit_counts` only when far-end signal has something to + // contribute. If `far_bit_counts` is zero the far-end signal is weak and + // we likely have a poor echo condition, hence don't update. + if (self->farend->far_bit_counts[i] > 0) { + // Make number of right shifts piecewise linear w.r.t. `far_bit_counts`. + int shifts = kShiftsAtZero; + shifts -= (kShiftsLinearSlope * self->farend->far_bit_counts[i]) >> 4; + WebRtc_MeanEstimatorFix(bit_count, shifts, &(self->mean_bit_counts[i])); + } + } + + // Find `candidate_delay`, `value_best_candidate` and `value_worst_candidate` + // of `mean_bit_counts`. + for (i = 0; i < self->history_size; i++) { + if (self->mean_bit_counts[i] < value_best_candidate) { + value_best_candidate = self->mean_bit_counts[i]; + candidate_delay = i; + } + if (self->mean_bit_counts[i] > value_worst_candidate) { + value_worst_candidate = self->mean_bit_counts[i]; + } + } + valley_depth = value_worst_candidate - value_best_candidate; + + // The `value_best_candidate` is a good indicator on the probability of + // `candidate_delay` being an accurate delay (a small `value_best_candidate` + // means a good binary match). In the following sections we make a decision + // whether to update `last_delay` or not. + // 1) If the difference bit counts between the best and the worst delay + // candidates is too small we consider the situation to be unreliable and + // don't update `last_delay`. + // 2) If the situation is reliable we update `last_delay` if the value of the + // best candidate delay has a value less than + // i) an adaptive threshold `minimum_probability`, or + // ii) this corresponding value `last_delay_probability`, but updated at + // this time instant. + + // Update `minimum_probability`. + if ((self->minimum_probability > kProbabilityLowerLimit) && + (valley_depth > kProbabilityMinSpread)) { + // The "hard" threshold can't be lower than 17 (in Q9). + // The valley in the curve also has to be distinct, i.e., the + // difference between `value_worst_candidate` and `value_best_candidate` has + // to be large enough. + int32_t threshold = value_best_candidate + kProbabilityOffset; + if (threshold < kProbabilityLowerLimit) { + threshold = kProbabilityLowerLimit; + } + if (self->minimum_probability > threshold) { + self->minimum_probability = threshold; + } + } + // Update `last_delay_probability`. + // We use a Markov type model, i.e., a slowly increasing level over time. + self->last_delay_probability++; + // Validate `candidate_delay`. We have a reliable instantaneous delay + // estimate if + // 1) The valley is distinct enough (`valley_depth` > `kProbabilityOffset`) + // and + // 2) The depth of the valley is deep enough + // (`value_best_candidate` < `minimum_probability`) + // and deeper than the best estimate so far + // (`value_best_candidate` < `last_delay_probability`) + valid_candidate = ((valley_depth > kProbabilityOffset) && + ((value_best_candidate < self->minimum_probability) || + (value_best_candidate < self->last_delay_probability))); + + // Check for nonstationary farend signal. + const bool non_stationary_farend = + std::any_of(self->farend->far_bit_counts, + self->farend->far_bit_counts + self->history_size, + [](int a) { return a > 0; }); + + if (non_stationary_farend) { + // Only update the validation statistics when the farend is nonstationary + // as the underlying estimates are otherwise frozen. + UpdateRobustValidationStatistics(self, candidate_delay, valley_depth, + value_best_candidate); + } + + if (self->robust_validation_enabled) { + int is_histogram_valid = HistogramBasedValidation(self, candidate_delay); + valid_candidate = RobustValidation(self, candidate_delay, valid_candidate, + is_histogram_valid); + } + + // Only update the delay estimate when the farend is nonstationary and when + // a valid delay candidate is available. + if (non_stationary_farend && valid_candidate) { + if (candidate_delay != self->last_delay) { + self->last_delay_histogram = + (self->histogram[candidate_delay] > kLastHistogramMax + ? kLastHistogramMax + : self->histogram[candidate_delay]); + // Adjust the histogram if we made a change to `last_delay`, though it was + // not the most likely one according to the histogram. + if (self->histogram[candidate_delay] < + self->histogram[self->compare_delay]) { + self->histogram[self->compare_delay] = self->histogram[candidate_delay]; + } + } + self->last_delay = candidate_delay; + if (value_best_candidate < self->last_delay_probability) { + self->last_delay_probability = value_best_candidate; + } + self->compare_delay = self->last_delay; + } + + return self->last_delay; +} + +int WebRtc_binary_last_delay(BinaryDelayEstimator* self) { + RTC_DCHECK(self); + return self->last_delay; +} + +float WebRtc_binary_last_delay_quality(BinaryDelayEstimator* self) { + float quality = 0; + RTC_DCHECK(self); + + if (self->robust_validation_enabled) { + // Simply a linear function of the histogram height at delay estimate. + quality = self->histogram[self->compare_delay] / kHistogramMax; + } else { + // Note that `last_delay_probability` states how deep the minimum of the + // cost function is, so it is rather an error probability. + quality = (float)(kMaxBitCountsQ9 - self->last_delay_probability) / + kMaxBitCountsQ9; + if (quality < 0) { + quality = 0; + } + } + return quality; +} + +void WebRtc_MeanEstimatorFix(int32_t new_value, + int factor, + int32_t* mean_value) { + int32_t diff = new_value - *mean_value; + + // mean_new = mean_value + ((new_value - mean_value) >> factor); + if (diff < 0) { + diff = -((-diff) >> factor); + } else { + diff = (diff >> factor); + } + *mean_value += diff; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator.h b/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator.h new file mode 100644 index 00000000..b6fc36a7 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator.h @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Performs delay estimation on binary converted spectra. +// The return value is 0 - OK and -1 - Error, unless otherwise stated. + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_ + +#include + +namespace webrtc { + +static const int32_t kMaxBitCountsQ9 = (32 << 9); // 32 matching bits in Q9. + +typedef struct { + // Pointer to bit counts. + int* far_bit_counts; + // Binary history variables. + uint32_t* binary_far_history; + int history_size; +} BinaryDelayEstimatorFarend; + +typedef struct { + // Pointer to bit counts. + int32_t* mean_bit_counts; + // Array only used locally in ProcessBinarySpectrum() but whose size is + // determined at run-time. + int32_t* bit_counts; + + // Binary history variables. + uint32_t* binary_near_history; + int near_history_size; + int history_size; + + // Delay estimation variables. + int32_t minimum_probability; + int last_delay_probability; + + // Delay memory. + int last_delay; + + // Robust validation + int robust_validation_enabled; + int allowed_offset; + int last_candidate_delay; + int compare_delay; + int candidate_hits; + float* histogram; + float last_delay_histogram; + + // For dynamically changing the lookahead when using SoftReset...(). + int lookahead; + + // Far-end binary spectrum history buffer etc. + BinaryDelayEstimatorFarend* farend; +} BinaryDelayEstimator; + +// Releases the memory allocated by +// WebRtc_CreateBinaryDelayEstimatorFarend(...). +// Input: +// - self : Pointer to the binary delay estimation far-end +// instance which is the return value of +// WebRtc_CreateBinaryDelayEstimatorFarend(). +// +void WebRtc_FreeBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self); + +// Allocates the memory needed by the far-end part of the binary delay +// estimation. The memory needs to be initialized separately through +// WebRtc_InitBinaryDelayEstimatorFarend(...). +// +// Inputs: +// - history_size : Size of the far-end binary spectrum history. +// +// Return value: +// - BinaryDelayEstimatorFarend* +// : Created `handle`. If the memory can't be allocated +// or if any of the input parameters are invalid NULL +// is returned. +// +BinaryDelayEstimatorFarend* WebRtc_CreateBinaryDelayEstimatorFarend( + int history_size); + +// Re-allocates the buffers. +// +// Inputs: +// - self : Pointer to the binary estimation far-end instance +// which is the return value of +// WebRtc_CreateBinaryDelayEstimatorFarend(). +// - history_size : Size of the far-end binary spectrum history. +// +// Return value: +// - history_size : The history size allocated. +int WebRtc_AllocateFarendBufferMemory(BinaryDelayEstimatorFarend* self, + int history_size); + +// Initializes the delay estimation far-end instance created with +// WebRtc_CreateBinaryDelayEstimatorFarend(...). +// +// Input: +// - self : Pointer to the delay estimation far-end instance. +// +// Output: +// - self : Initialized far-end instance. +// +void WebRtc_InitBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self); + +// Soft resets the delay estimation far-end instance created with +// WebRtc_CreateBinaryDelayEstimatorFarend(...). +// +// Input: +// - delay_shift : The amount of blocks to shift history buffers. +// +void WebRtc_SoftResetBinaryDelayEstimatorFarend( + BinaryDelayEstimatorFarend* self, + int delay_shift); + +// Adds the binary far-end spectrum to the internal far-end history buffer. This +// spectrum is used as reference when calculating the delay using +// WebRtc_ProcessBinarySpectrum(). +// +// Inputs: +// - self : Pointer to the delay estimation far-end +// instance. +// - binary_far_spectrum : Far-end binary spectrum. +// +// Output: +// - self : Updated far-end instance. +// +void WebRtc_AddBinaryFarSpectrum(BinaryDelayEstimatorFarend* self, + uint32_t binary_far_spectrum); + +// Releases the memory allocated by WebRtc_CreateBinaryDelayEstimator(...). +// +// Note that BinaryDelayEstimator utilizes BinaryDelayEstimatorFarend, but does +// not take ownership of it, hence the BinaryDelayEstimator has to be torn down +// before the far-end. +// +// Input: +// - self : Pointer to the binary delay estimation instance +// which is the return value of +// WebRtc_CreateBinaryDelayEstimator(). +// +void WebRtc_FreeBinaryDelayEstimator(BinaryDelayEstimator* self); + +// Allocates the memory needed by the binary delay estimation. The memory needs +// to be initialized separately through WebRtc_InitBinaryDelayEstimator(...). +// +// See WebRtc_CreateDelayEstimator(..) in delay_estimator_wrapper.c for detailed +// description. +BinaryDelayEstimator* WebRtc_CreateBinaryDelayEstimator( + BinaryDelayEstimatorFarend* farend, + int max_lookahead); + +// Re-allocates `history_size` dependent buffers. The far-end buffers will be +// updated at the same time if needed. +// +// Input: +// - self : Pointer to the binary estimation instance which is +// the return value of +// WebRtc_CreateBinaryDelayEstimator(). +// - history_size : Size of the history buffers. +// +// Return value: +// - history_size : The history size allocated. +int WebRtc_AllocateHistoryBufferMemory(BinaryDelayEstimator* self, + int history_size); + +// Initializes the delay estimation instance created with +// WebRtc_CreateBinaryDelayEstimator(...). +// +// Input: +// - self : Pointer to the delay estimation instance. +// +// Output: +// - self : Initialized instance. +// +void WebRtc_InitBinaryDelayEstimator(BinaryDelayEstimator* self); + +// Soft resets the delay estimation instance created with +// WebRtc_CreateBinaryDelayEstimator(...). +// +// Input: +// - delay_shift : The amount of blocks to shift history buffers. +// +// Return value: +// - actual_shifts : The actual number of shifts performed. +// +int WebRtc_SoftResetBinaryDelayEstimator(BinaryDelayEstimator* self, + int delay_shift); + +// Estimates and returns the delay between the binary far-end and binary near- +// end spectra. It is assumed the binary far-end spectrum has been added using +// WebRtc_AddBinaryFarSpectrum() prior to this call. The value will be offset by +// the lookahead (i.e. the lookahead should be subtracted from the returned +// value). +// +// Inputs: +// - self : Pointer to the delay estimation instance. +// - binary_near_spectrum : Near-end binary spectrum of the current block. +// +// Output: +// - self : Updated instance. +// +// Return value: +// - delay : >= 0 - Calculated delay value. +// -2 - Insufficient data for estimation. +// +int WebRtc_ProcessBinarySpectrum(BinaryDelayEstimator* self, + uint32_t binary_near_spectrum); + +// Returns the last calculated delay updated by the function +// WebRtc_ProcessBinarySpectrum(...). +// +// Input: +// - self : Pointer to the delay estimation instance. +// +// Return value: +// - delay : >= 0 - Last calculated delay value +// -2 - Insufficient data for estimation. +// +int WebRtc_binary_last_delay(BinaryDelayEstimator* self); + +// Returns the estimation quality of the last calculated delay updated by the +// function WebRtc_ProcessBinarySpectrum(...). The estimation quality is a value +// in the interval [0, 1]. The higher the value, the better the quality. +// +// Return value: +// - delay_quality : >= 0 - Estimation quality of last calculated +// delay value. +float WebRtc_binary_last_delay_quality(BinaryDelayEstimator* self); + +// Updates the `mean_value` recursively with a step size of 2^-`factor`. This +// function is used internally in the Binary Delay Estimator as well as the +// Fixed point wrapper. +// +// Inputs: +// - new_value : The new value the mean should be updated with. +// - factor : The step size, in number of right shifts. +// +// Input/Output: +// - mean_value : Pointer to the mean value. +// +void WebRtc_MeanEstimatorFix(int32_t new_value, + int factor, + int32_t* mean_value); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_internal.h b/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_internal.h new file mode 100644 index 00000000..891e2002 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_internal.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +// Header file including the delay estimator handle used for testing. + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_ + +#include "modules/audio_processing/utility/delay_estimator.h" + +namespace webrtc { + +typedef union { + float float_; + int32_t int32_; +} SpectrumType; + +typedef struct { + // Pointers to mean values of spectrum. + SpectrumType* mean_far_spectrum; + // `mean_far_spectrum` initialization indicator. + int far_spectrum_initialized; + + int spectrum_size; + + // Far-end part of binary spectrum based delay estimation. + BinaryDelayEstimatorFarend* binary_farend; +} DelayEstimatorFarend; + +typedef struct { + // Pointers to mean values of spectrum. + SpectrumType* mean_near_spectrum; + // `mean_near_spectrum` initialization indicator. + int near_spectrum_initialized; + + int spectrum_size; + + // Binary spectrum based delay estimator + BinaryDelayEstimator* binary_handle; +} DelayEstimator; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc b/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc new file mode 100644 index 00000000..3b1409cc --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/utility/delay_estimator_wrapper.h" + +#include +#include + +#include "modules/audio_processing/utility/delay_estimator.h" +#include "modules/audio_processing/utility/delay_estimator_internal.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Only bit `kBandFirst` through bit `kBandLast` are processed and +// `kBandFirst` - `kBandLast` must be < 32. +constexpr int kBandFirst = 12; +constexpr int kBandLast = 43; + +static __inline uint32_t SetBit(uint32_t in, int pos) { + uint32_t mask = (1 << pos); + uint32_t out = (in | mask); + + return out; +} + +// Calculates the mean recursively. Same version as WebRtc_MeanEstimatorFix(), +// but for float. +// +// Inputs: +// - new_value : New additional value. +// - scale : Scale for smoothing (should be less than 1.0). +// +// Input/Output: +// - mean_value : Pointer to the mean value for updating. +// +static void MeanEstimatorFloat(float new_value, + float scale, + float* mean_value) { + RTC_DCHECK_LT(scale, 1.0f); + *mean_value += (new_value - *mean_value) * scale; +} + +// Computes the binary spectrum by comparing the input `spectrum` with a +// `threshold_spectrum`. Float and fixed point versions. +// +// Inputs: +// - spectrum : Spectrum of which the binary spectrum should be +// calculated. +// - threshold_spectrum : Threshold spectrum with which the input +// spectrum is compared. +// Return: +// - out : Binary spectrum. +// +static uint32_t BinarySpectrumFix(const uint16_t* spectrum, + SpectrumType* threshold_spectrum, + int q_domain, + int* threshold_initialized) { + int i = kBandFirst; + uint32_t out = 0; + + RTC_DCHECK_LT(q_domain, 16); + + if (!(*threshold_initialized)) { + // Set the `threshold_spectrum` to half the input `spectrum` as starting + // value. This speeds up the convergence. + for (i = kBandFirst; i <= kBandLast; i++) { + if (spectrum[i] > 0) { + // Convert input spectrum from Q(`q_domain`) to Q15. + int32_t spectrum_q15 = ((int32_t)spectrum[i]) << (15 - q_domain); + threshold_spectrum[i].int32_ = (spectrum_q15 >> 1); + *threshold_initialized = 1; + } + } + } + for (i = kBandFirst; i <= kBandLast; i++) { + // Convert input spectrum from Q(`q_domain`) to Q15. + int32_t spectrum_q15 = ((int32_t)spectrum[i]) << (15 - q_domain); + // Update the `threshold_spectrum`. + WebRtc_MeanEstimatorFix(spectrum_q15, 6, &(threshold_spectrum[i].int32_)); + // Convert `spectrum` at current frequency bin to a binary value. + if (spectrum_q15 > threshold_spectrum[i].int32_) { + out = SetBit(out, i - kBandFirst); + } + } + + return out; +} + +static uint32_t BinarySpectrumFloat(const float* spectrum, + SpectrumType* threshold_spectrum, + int* threshold_initialized) { + int i = kBandFirst; + uint32_t out = 0; + const float kScale = 1 / 64.0; + + if (!(*threshold_initialized)) { + // Set the `threshold_spectrum` to half the input `spectrum` as starting + // value. This speeds up the convergence. + for (i = kBandFirst; i <= kBandLast; i++) { + if (spectrum[i] > 0.0f) { + threshold_spectrum[i].float_ = (spectrum[i] / 2); + *threshold_initialized = 1; + } + } + } + + for (i = kBandFirst; i <= kBandLast; i++) { + // Update the `threshold_spectrum`. + MeanEstimatorFloat(spectrum[i], kScale, &(threshold_spectrum[i].float_)); + // Convert `spectrum` at current frequency bin to a binary value. + if (spectrum[i] > threshold_spectrum[i].float_) { + out = SetBit(out, i - kBandFirst); + } + } + + return out; +} + +void WebRtc_FreeDelayEstimatorFarend(void* handle) { + DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle; + + if (handle == NULL) { + return; + } + + free(self->mean_far_spectrum); + self->mean_far_spectrum = NULL; + + WebRtc_FreeBinaryDelayEstimatorFarend(self->binary_farend); + self->binary_farend = NULL; + + free(self); +} + +void* WebRtc_CreateDelayEstimatorFarend(int spectrum_size, int history_size) { + DelayEstimatorFarend* self = NULL; + + // Check if the sub band used in the delay estimation is small enough to fit + // the binary spectra in a uint32_t. + static_assert(kBandLast - kBandFirst < 32, ""); + + if (spectrum_size >= kBandLast) { + self = static_cast( + malloc(sizeof(DelayEstimatorFarend))); + } + + if (self != NULL) { + int memory_fail = 0; + + // Allocate memory for the binary far-end spectrum handling. + self->binary_farend = WebRtc_CreateBinaryDelayEstimatorFarend(history_size); + memory_fail |= (self->binary_farend == NULL); + + // Allocate memory for spectrum buffers. + self->mean_far_spectrum = static_cast( + malloc(spectrum_size * sizeof(SpectrumType))); + memory_fail |= (self->mean_far_spectrum == NULL); + + self->spectrum_size = spectrum_size; + + if (memory_fail) { + WebRtc_FreeDelayEstimatorFarend(self); + self = NULL; + } + } + + return self; +} + +int WebRtc_InitDelayEstimatorFarend(void* handle) { + DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle; + + if (self == NULL) { + return -1; + } + + // Initialize far-end part of binary delay estimator. + WebRtc_InitBinaryDelayEstimatorFarend(self->binary_farend); + + // Set averaged far and near end spectra to zero. + memset(self->mean_far_spectrum, 0, + sizeof(SpectrumType) * self->spectrum_size); + // Reset initialization indicators. + self->far_spectrum_initialized = 0; + + return 0; +} + +void WebRtc_SoftResetDelayEstimatorFarend(void* handle, int delay_shift) { + DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle; + RTC_DCHECK(self); + WebRtc_SoftResetBinaryDelayEstimatorFarend(self->binary_farend, delay_shift); +} + +int WebRtc_AddFarSpectrumFix(void* handle, + const uint16_t* far_spectrum, + int spectrum_size, + int far_q) { + DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle; + uint32_t binary_spectrum = 0; + + if (self == NULL) { + return -1; + } + if (far_spectrum == NULL) { + // Empty far end spectrum. + return -1; + } + if (spectrum_size != self->spectrum_size) { + // Data sizes don't match. + return -1; + } + if (far_q > 15) { + // If `far_q` is larger than 15 we cannot guarantee no wrap around. + return -1; + } + + // Get binary spectrum. + binary_spectrum = BinarySpectrumFix(far_spectrum, self->mean_far_spectrum, + far_q, &(self->far_spectrum_initialized)); + WebRtc_AddBinaryFarSpectrum(self->binary_farend, binary_spectrum); + + return 0; +} + +int WebRtc_AddFarSpectrumFloat(void* handle, + const float* far_spectrum, + int spectrum_size) { + DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle; + uint32_t binary_spectrum = 0; + + if (self == NULL) { + return -1; + } + if (far_spectrum == NULL) { + // Empty far end spectrum. + return -1; + } + if (spectrum_size != self->spectrum_size) { + // Data sizes don't match. + return -1; + } + + // Get binary spectrum. + binary_spectrum = BinarySpectrumFloat(far_spectrum, self->mean_far_spectrum, + &(self->far_spectrum_initialized)); + WebRtc_AddBinaryFarSpectrum(self->binary_farend, binary_spectrum); + + return 0; +} + +void WebRtc_FreeDelayEstimator(void* handle) { + DelayEstimator* self = (DelayEstimator*)handle; + + if (handle == NULL) { + return; + } + + free(self->mean_near_spectrum); + self->mean_near_spectrum = NULL; + + WebRtc_FreeBinaryDelayEstimator(self->binary_handle); + self->binary_handle = NULL; + + free(self); +} + +void* WebRtc_CreateDelayEstimator(void* farend_handle, int max_lookahead) { + DelayEstimator* self = NULL; + DelayEstimatorFarend* farend = (DelayEstimatorFarend*)farend_handle; + + if (farend_handle != NULL) { + self = static_cast(malloc(sizeof(DelayEstimator))); + } + + if (self != NULL) { + int memory_fail = 0; + + // Allocate memory for the farend spectrum handling. + self->binary_handle = + WebRtc_CreateBinaryDelayEstimator(farend->binary_farend, max_lookahead); + memory_fail |= (self->binary_handle == NULL); + + // Allocate memory for spectrum buffers. + self->mean_near_spectrum = static_cast( + malloc(farend->spectrum_size * sizeof(SpectrumType))); + memory_fail |= (self->mean_near_spectrum == NULL); + + self->spectrum_size = farend->spectrum_size; + + if (memory_fail) { + WebRtc_FreeDelayEstimator(self); + self = NULL; + } + } + + return self; +} + +int WebRtc_InitDelayEstimator(void* handle) { + DelayEstimator* self = (DelayEstimator*)handle; + + if (self == NULL) { + return -1; + } + + // Initialize binary delay estimator. + WebRtc_InitBinaryDelayEstimator(self->binary_handle); + + // Set averaged far and near end spectra to zero. + memset(self->mean_near_spectrum, 0, + sizeof(SpectrumType) * self->spectrum_size); + // Reset initialization indicators. + self->near_spectrum_initialized = 0; + + return 0; +} + +int WebRtc_SoftResetDelayEstimator(void* handle, int delay_shift) { + DelayEstimator* self = (DelayEstimator*)handle; + RTC_DCHECK(self); + return WebRtc_SoftResetBinaryDelayEstimator(self->binary_handle, delay_shift); +} + +int WebRtc_set_history_size(void* handle, int history_size) { + DelayEstimator* self = static_cast(handle); + + if ((self == NULL) || (history_size <= 1)) { + return -1; + } + return WebRtc_AllocateHistoryBufferMemory(self->binary_handle, history_size); +} + +int WebRtc_history_size(const void* handle) { + const DelayEstimator* self = static_cast(handle); + + if (self == NULL) { + return -1; + } + if (self->binary_handle->farend->history_size != + self->binary_handle->history_size) { + // Non matching history sizes. + return -1; + } + return self->binary_handle->history_size; +} + +int WebRtc_set_lookahead(void* handle, int lookahead) { + DelayEstimator* self = (DelayEstimator*)handle; + RTC_DCHECK(self); + RTC_DCHECK(self->binary_handle); + if ((lookahead > self->binary_handle->near_history_size - 1) || + (lookahead < 0)) { + return -1; + } + self->binary_handle->lookahead = lookahead; + return self->binary_handle->lookahead; +} + +int WebRtc_lookahead(void* handle) { + DelayEstimator* self = (DelayEstimator*)handle; + RTC_DCHECK(self); + RTC_DCHECK(self->binary_handle); + return self->binary_handle->lookahead; +} + +int WebRtc_set_allowed_offset(void* handle, int allowed_offset) { + DelayEstimator* self = (DelayEstimator*)handle; + + if ((self == NULL) || (allowed_offset < 0)) { + return -1; + } + self->binary_handle->allowed_offset = allowed_offset; + return 0; +} + +int WebRtc_get_allowed_offset(const void* handle) { + const DelayEstimator* self = (const DelayEstimator*)handle; + + if (self == NULL) { + return -1; + } + return self->binary_handle->allowed_offset; +} + +int WebRtc_enable_robust_validation(void* handle, int enable) { + DelayEstimator* self = (DelayEstimator*)handle; + + if (self == NULL) { + return -1; + } + if ((enable < 0) || (enable > 1)) { + return -1; + } + RTC_DCHECK(self->binary_handle); + self->binary_handle->robust_validation_enabled = enable; + return 0; +} + +int WebRtc_is_robust_validation_enabled(const void* handle) { + const DelayEstimator* self = (const DelayEstimator*)handle; + + if (self == NULL) { + return -1; + } + return self->binary_handle->robust_validation_enabled; +} + +int WebRtc_DelayEstimatorProcessFix(void* handle, + const uint16_t* near_spectrum, + int spectrum_size, + int near_q) { + DelayEstimator* self = (DelayEstimator*)handle; + uint32_t binary_spectrum = 0; + + if (self == NULL) { + return -1; + } + if (near_spectrum == NULL) { + // Empty near end spectrum. + return -1; + } + if (spectrum_size != self->spectrum_size) { + // Data sizes don't match. + return -1; + } + if (near_q > 15) { + // If `near_q` is larger than 15 we cannot guarantee no wrap around. + return -1; + } + + // Get binary spectra. + binary_spectrum = + BinarySpectrumFix(near_spectrum, self->mean_near_spectrum, near_q, + &(self->near_spectrum_initialized)); + + return WebRtc_ProcessBinarySpectrum(self->binary_handle, binary_spectrum); +} + +int WebRtc_DelayEstimatorProcessFloat(void* handle, + const float* near_spectrum, + int spectrum_size) { + DelayEstimator* self = (DelayEstimator*)handle; + uint32_t binary_spectrum = 0; + + if (self == NULL) { + return -1; + } + if (near_spectrum == NULL) { + // Empty near end spectrum. + return -1; + } + if (spectrum_size != self->spectrum_size) { + // Data sizes don't match. + return -1; + } + + // Get binary spectrum. + binary_spectrum = BinarySpectrumFloat(near_spectrum, self->mean_near_spectrum, + &(self->near_spectrum_initialized)); + + return WebRtc_ProcessBinarySpectrum(self->binary_handle, binary_spectrum); +} + +int WebRtc_last_delay(void* handle) { + DelayEstimator* self = (DelayEstimator*)handle; + + if (self == NULL) { + return -1; + } + + return WebRtc_binary_last_delay(self->binary_handle); +} + +float WebRtc_last_delay_quality(void* handle) { + DelayEstimator* self = (DelayEstimator*)handle; + RTC_DCHECK(self); + return WebRtc_binary_last_delay_quality(self->binary_handle); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h b/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h new file mode 100644 index 00000000..a90cbe31 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Performs delay estimation on block by block basis. +// The return value is 0 - OK and -1 - Error, unless otherwise stated. + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_ + +#include + +namespace webrtc { + +// Releases the memory allocated by WebRtc_CreateDelayEstimatorFarend(...) +void WebRtc_FreeDelayEstimatorFarend(void* handle); + +// Allocates the memory needed by the far-end part of the delay estimation. The +// memory needs to be initialized separately through +// WebRtc_InitDelayEstimatorFarend(...). +// +// Inputs: +// - spectrum_size : Size of the spectrum used both in far-end and +// near-end. Used to allocate memory for spectrum +// specific buffers. +// - history_size : The far-end history buffer size. A change in buffer +// size can be forced with WebRtc_set_history_size(). +// Note that the maximum delay which can be estimated is +// determined together with WebRtc_set_lookahead(). +// +// Return value: +// - void* : Created `handle`. If the memory can't be allocated or +// if any of the input parameters are invalid NULL is +// returned. +void* WebRtc_CreateDelayEstimatorFarend(int spectrum_size, int history_size); + +// Initializes the far-end part of the delay estimation instance returned by +// WebRtc_CreateDelayEstimatorFarend(...) +int WebRtc_InitDelayEstimatorFarend(void* handle); + +// Soft resets the far-end part of the delay estimation instance returned by +// WebRtc_CreateDelayEstimatorFarend(...). +// Input: +// - delay_shift : The amount of blocks to shift history buffers. +void WebRtc_SoftResetDelayEstimatorFarend(void* handle, int delay_shift); + +// Adds the far-end spectrum to the far-end history buffer. This spectrum is +// used as reference when calculating the delay using +// WebRtc_ProcessSpectrum(). +// +// Inputs: +// - far_spectrum : Far-end spectrum. +// - spectrum_size : The size of the data arrays (same for both far- and +// near-end). +// - far_q : The Q-domain of the far-end data. +// +// Output: +// - handle : Updated far-end instance. +// +int WebRtc_AddFarSpectrumFix(void* handle, + const uint16_t* far_spectrum, + int spectrum_size, + int far_q); + +// See WebRtc_AddFarSpectrumFix() for description. +int WebRtc_AddFarSpectrumFloat(void* handle, + const float* far_spectrum, + int spectrum_size); + +// Releases the memory allocated by WebRtc_CreateDelayEstimator(...) +void WebRtc_FreeDelayEstimator(void* handle); + +// Allocates the memory needed by the delay estimation. The memory needs to be +// initialized separately through WebRtc_InitDelayEstimator(...). +// +// Inputs: +// - farend_handle : Pointer to the far-end part of the delay estimation +// instance created prior to this call using +// WebRtc_CreateDelayEstimatorFarend(). +// +// Note that WebRtc_CreateDelayEstimator does not take +// ownership of `farend_handle`, which has to be torn +// down properly after this instance. +// +// - max_lookahead : Maximum amount of non-causal lookahead allowed. The +// actual amount of lookahead used can be controlled by +// WebRtc_set_lookahead(...). The default `lookahead` is +// set to `max_lookahead` at create time. Use +// WebRtc_set_lookahead(...) before start if a different +// value is desired. +// +// Using lookahead can detect cases in which a near-end +// signal occurs before the corresponding far-end signal. +// It will delay the estimate for the current block by an +// equal amount, and the returned values will be offset +// by it. +// +// A value of zero is the typical no-lookahead case. +// This also represents the minimum delay which can be +// estimated. +// +// Note that the effective range of delay estimates is +// [-`lookahead`,... ,`history_size`-`lookahead`) +// where `history_size` is set through +// WebRtc_set_history_size(). +// +// Return value: +// - void* : Created `handle`. If the memory can't be allocated or +// if any of the input parameters are invalid NULL is +// returned. +void* WebRtc_CreateDelayEstimator(void* farend_handle, int max_lookahead); + +// Initializes the delay estimation instance returned by +// WebRtc_CreateDelayEstimator(...) +int WebRtc_InitDelayEstimator(void* handle); + +// Soft resets the delay estimation instance returned by +// WebRtc_CreateDelayEstimator(...) +// Input: +// - delay_shift : The amount of blocks to shift history buffers. +// +// Return value: +// - actual_shifts : The actual number of shifts performed. +int WebRtc_SoftResetDelayEstimator(void* handle, int delay_shift); + +// Sets the effective `history_size` used. Valid values from 2. We simply need +// at least two delays to compare to perform an estimate. If `history_size` is +// changed, buffers are reallocated filling in with zeros if necessary. +// Note that changing the `history_size` affects both buffers in far-end and +// near-end. Hence it is important to change all DelayEstimators that use the +// same reference far-end, to the same `history_size` value. +// Inputs: +// - handle : Pointer to the delay estimation instance. +// - history_size : Effective history size to be used. +// Return value: +// - new_history_size : The new history size used. If the memory was not able +// to be allocated 0 is returned. +int WebRtc_set_history_size(void* handle, int history_size); + +// Returns the history_size currently used. +// Input: +// - handle : Pointer to the delay estimation instance. +int WebRtc_history_size(const void* handle); + +// Sets the amount of `lookahead` to use. Valid values are [0, max_lookahead] +// where `max_lookahead` was set at create time through +// WebRtc_CreateDelayEstimator(...). +// +// Input: +// - handle : Pointer to the delay estimation instance. +// - lookahead : The amount of lookahead to be used. +// +// Return value: +// - new_lookahead : The actual amount of lookahead set, unless `handle` is +// a NULL pointer or `lookahead` is invalid, for which an +// error is returned. +int WebRtc_set_lookahead(void* handle, int lookahead); + +// Returns the amount of lookahead we currently use. +// Input: +// - handle : Pointer to the delay estimation instance. +int WebRtc_lookahead(void* handle); + +// Sets the `allowed_offset` used in the robust validation scheme. If the +// delay estimator is used in an echo control component, this parameter is +// related to the filter length. In principle `allowed_offset` should be set to +// the echo control filter length minus the expected echo duration, i.e., the +// delay offset the echo control can handle without quality regression. The +// default value, used if not set manually, is zero. Note that `allowed_offset` +// has to be non-negative. +// Inputs: +// - handle : Pointer to the delay estimation instance. +// - allowed_offset : The amount of delay offset, measured in partitions, +// the echo control filter can handle. +int WebRtc_set_allowed_offset(void* handle, int allowed_offset); + +// Returns the `allowed_offset` in number of partitions. +int WebRtc_get_allowed_offset(const void* handle); + +// Enables/Disables a robust validation functionality in the delay estimation. +// This is by default set to disabled at create time. The state is preserved +// over a reset. +// Inputs: +// - handle : Pointer to the delay estimation instance. +// - enable : Enable (1) or disable (0) this feature. +int WebRtc_enable_robust_validation(void* handle, int enable); + +// Returns 1 if robust validation is enabled and 0 if disabled. +int WebRtc_is_robust_validation_enabled(const void* handle); + +// Estimates and returns the delay between the far-end and near-end blocks. The +// value will be offset by the lookahead (i.e. the lookahead should be +// subtracted from the returned value). +// Inputs: +// - handle : Pointer to the delay estimation instance. +// - near_spectrum : Pointer to the near-end spectrum data of the current +// block. +// - spectrum_size : The size of the data arrays (same for both far- and +// near-end). +// - near_q : The Q-domain of the near-end data. +// +// Output: +// - handle : Updated instance. +// +// Return value: +// - delay : >= 0 - Calculated delay value. +// -1 - Error. +// -2 - Insufficient data for estimation. +int WebRtc_DelayEstimatorProcessFix(void* handle, + const uint16_t* near_spectrum, + int spectrum_size, + int near_q); + +// See WebRtc_DelayEstimatorProcessFix() for description. +int WebRtc_DelayEstimatorProcessFloat(void* handle, + const float* near_spectrum, + int spectrum_size); + +// Returns the last calculated delay updated by the function +// WebRtc_DelayEstimatorProcess(...). +// +// Input: +// - handle : Pointer to the delay estimation instance. +// +// Return value: +// - delay : >= 0 - Last calculated delay value. +// -1 - Error. +// -2 - Insufficient data for estimation. +int WebRtc_last_delay(void* handle); + +// Returns the estimation quality/probability of the last calculated delay +// updated by the function WebRtc_DelayEstimatorProcess(...). The estimation +// quality is a value in the interval [0, 1]. The higher the value, the better +// the quality. +// +// Return value: +// - delay_quality : >= 0 - Estimation quality of last calculated delay. +float WebRtc_last_delay_quality(void* handle); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/utility/pffft_wrapper.cc b/pkg/apm/webrtc/modules/audio_processing/utility/pffft_wrapper.cc new file mode 100644 index 00000000..3f65941c --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/utility/pffft_wrapper.cc @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/utility/pffft_wrapper.h" + +#include "rtc_base/checks.h" +#include "third_party/pffft/src/pffft.h" + +namespace webrtc { +namespace { + +size_t GetBufferSize(size_t fft_size, Pffft::FftType fft_type) { + return fft_size * (fft_type == Pffft::FftType::kReal ? 1 : 2); +} + +float* AllocatePffftBuffer(size_t size) { + return static_cast(pffft_aligned_malloc(size * sizeof(float))); +} + +} // namespace + +Pffft::FloatBuffer::FloatBuffer(size_t fft_size, FftType fft_type) + : size_(GetBufferSize(fft_size, fft_type)), + data_(AllocatePffftBuffer(size_)) {} + +Pffft::FloatBuffer::~FloatBuffer() { + pffft_aligned_free(data_); +} + +ArrayView Pffft::FloatBuffer::GetConstView() const { + return {data_, size_}; +} + +ArrayView Pffft::FloatBuffer::GetView() { + return {data_, size_}; +} + +Pffft::Pffft(size_t fft_size, FftType fft_type) + : fft_size_(fft_size), + fft_type_(fft_type), + pffft_status_(pffft_new_setup( + fft_size_, + fft_type == Pffft::FftType::kReal ? PFFFT_REAL : PFFFT_COMPLEX)), + scratch_buffer_( + AllocatePffftBuffer(GetBufferSize(fft_size_, fft_type_))) { + RTC_DCHECK(pffft_status_); + RTC_DCHECK(scratch_buffer_); +} + +Pffft::~Pffft() { + pffft_destroy_setup(pffft_status_); + pffft_aligned_free(scratch_buffer_); +} + +bool Pffft::IsValidFftSize(size_t fft_size, FftType fft_type) { + if (fft_size == 0) { + return false; + } + // PFFFT only supports transforms for inputs of length N of the form + // N = (2^a)*(3^b)*(5^c) where b >=0 and c >= 0 and a >= 5 for the real FFT + // and a >= 4 for the complex FFT. + constexpr int kFactors[] = {2, 3, 5}; + int factorization[] = {0, 0, 0}; + int n = static_cast(fft_size); + for (int i = 0; i < 3; ++i) { + while (n % kFactors[i] == 0) { + n = n / kFactors[i]; + factorization[i]++; + } + } + int a_min = (fft_type == Pffft::FftType::kReal) ? 5 : 4; + return factorization[0] >= a_min && n == 1; +} + +bool Pffft::IsSimdEnabled() { + return pffft_simd_size() > 1; +} + +std::unique_ptr Pffft::CreateBuffer() const { + // Cannot use make_unique from absl because Pffft is the only friend of + // Pffft::FloatBuffer. + std::unique_ptr buffer( + new Pffft::FloatBuffer(fft_size_, fft_type_)); + return buffer; +} + +void Pffft::ForwardTransform(const FloatBuffer& in, + FloatBuffer* out, + bool ordered) { + RTC_DCHECK_EQ(in.size(), GetBufferSize(fft_size_, fft_type_)); + RTC_DCHECK_EQ(in.size(), out->size()); + RTC_DCHECK(scratch_buffer_); + if (ordered) { + pffft_transform_ordered(pffft_status_, in.const_data(), out->data(), + scratch_buffer_, PFFFT_FORWARD); + } else { + pffft_transform(pffft_status_, in.const_data(), out->data(), + scratch_buffer_, PFFFT_FORWARD); + } +} + +void Pffft::BackwardTransform(const FloatBuffer& in, + FloatBuffer* out, + bool ordered) { + RTC_DCHECK_EQ(in.size(), GetBufferSize(fft_size_, fft_type_)); + RTC_DCHECK_EQ(in.size(), out->size()); + RTC_DCHECK(scratch_buffer_); + if (ordered) { + pffft_transform_ordered(pffft_status_, in.const_data(), out->data(), + scratch_buffer_, PFFFT_BACKWARD); + } else { + pffft_transform(pffft_status_, in.const_data(), out->data(), + scratch_buffer_, PFFFT_BACKWARD); + } +} + +void Pffft::FrequencyDomainConvolve(const FloatBuffer& fft_x, + const FloatBuffer& fft_y, + FloatBuffer* out, + float scaling) { + RTC_DCHECK_EQ(fft_x.size(), GetBufferSize(fft_size_, fft_type_)); + RTC_DCHECK_EQ(fft_x.size(), fft_y.size()); + RTC_DCHECK_EQ(fft_x.size(), out->size()); + pffft_zconvolve_accumulate(pffft_status_, fft_x.const_data(), + fft_y.const_data(), out->data(), scaling); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/utility/pffft_wrapper.h b/pkg/apm/webrtc/modules/audio_processing/utility/pffft_wrapper.h new file mode 100644 index 00000000..b555b4b1 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/utility/pffft_wrapper.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_PFFFT_WRAPPER_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_PFFFT_WRAPPER_H_ + +#include + +#include "api/array_view.h" + +// Forward declaration. +struct PFFFT_Setup; + +namespace webrtc { + +// Pretty-Fast Fast Fourier Transform (PFFFT) wrapper class. +// Not thread safe. +class Pffft { + public: + enum class FftType { kReal, kComplex }; + + // 1D floating point buffer used as input/output data type for the FFT ops. + // It must be constructed using Pffft::CreateBuffer(). + class FloatBuffer { + public: + FloatBuffer(const FloatBuffer&) = delete; + FloatBuffer& operator=(const FloatBuffer&) = delete; + ~FloatBuffer(); + + ArrayView GetConstView() const; + ArrayView GetView(); + + private: + friend class Pffft; + FloatBuffer(size_t fft_size, FftType fft_type); + const float* const_data() const { return data_; } + float* data() { return data_; } + size_t size() const { return size_; } + + const size_t size_; + float* const data_; + }; + + // TODO(https://crbug.com/webrtc/9577): Consider adding a factory and making + // the ctor private. + // static std::unique_ptr Create(size_t fft_size, + // FftType fft_type); Ctor. `fft_size` must be a supported size (see + // Pffft::IsValidFftSize()). If not supported, the code will crash. + Pffft(size_t fft_size, FftType fft_type); + Pffft(const Pffft&) = delete; + Pffft& operator=(const Pffft&) = delete; + ~Pffft(); + + // Returns true if the FFT size is supported. + static bool IsValidFftSize(size_t fft_size, FftType fft_type); + + // Returns true if SIMD code optimizations are being used. + static bool IsSimdEnabled(); + + // Creates a buffer of the right size. + std::unique_ptr CreateBuffer() const; + + // TODO(https://crbug.com/webrtc/9577): Overload with webrtc::ArrayView args. + // Computes the forward fast Fourier transform. + void ForwardTransform(const FloatBuffer& in, FloatBuffer* out, bool ordered); + // Computes the backward fast Fourier transform. + void BackwardTransform(const FloatBuffer& in, FloatBuffer* out, bool ordered); + + // Multiplies the frequency components of `fft_x` and `fft_y` and accumulates + // them into `out`. The arrays must have been obtained with + // ForwardTransform(..., /*ordered=*/false) - i.e., `fft_x` and `fft_y` must + // not be ordered. + void FrequencyDomainConvolve(const FloatBuffer& fft_x, + const FloatBuffer& fft_y, + FloatBuffer* out, + float scaling = 1.f); + + private: + const size_t fft_size_; + const FftType fft_type_; + PFFFT_Setup* pffft_status_; + float* const scratch_buffer_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_PFFFT_WRAPPER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/utility/utility.go b/pkg/apm/webrtc/modules/audio_processing/utility/utility.go new file mode 100644 index 00000000..ee7ef97e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/utility/utility.go @@ -0,0 +1,10 @@ +//go:build console + +package utility + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/common.h b/pkg/apm/webrtc/modules/audio_processing/vad/common.h new file mode 100644 index 00000000..b5a5fb38 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/common.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_VAD_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_VAD_COMMON_H_ + +#include + +static const int kSampleRateHz = 16000; +static const size_t kLength10Ms = kSampleRateHz / 100; +static const size_t kMaxNumFrames = 4; + +struct AudioFeatures { + double log_pitch_gain[kMaxNumFrames]; + double pitch_lag_hz[kMaxNumFrames]; + double spectral_peak[kMaxNumFrames]; + double rms[kMaxNumFrames]; + size_t num_frames; + bool silence; +}; + +#endif // MODULES_AUDIO_PROCESSING_VAD_COMMON_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/gmm.cc b/pkg/apm/webrtc/modules/audio_processing/vad/gmm.cc new file mode 100644 index 00000000..3b8764c4 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/gmm.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/vad/gmm.h" + +#include + +namespace webrtc { + +static const int kMaxDimension = 10; + +static void RemoveMean(const double* in, + const double* mean_vec, + int dimension, + double* out) { + for (int n = 0; n < dimension; ++n) + out[n] = in[n] - mean_vec[n]; +} + +static double ComputeExponent(const double* in, + const double* covar_inv, + int dimension) { + double q = 0; + for (int i = 0; i < dimension; ++i) { + double v = 0; + for (int j = 0; j < dimension; j++) + v += (*covar_inv++) * in[j]; + q += v * in[i]; + } + q *= -0.5; + return q; +} + +double EvaluateGmm(const double* x, const GmmParameters& gmm_parameters) { + if (gmm_parameters.dimension > kMaxDimension) { + return -1; // This is invalid pdf so the caller can check this. + } + double f = 0; + double v[kMaxDimension]; + const double* mean_vec = gmm_parameters.mean; + const double* covar_inv = gmm_parameters.covar_inverse; + + for (int n = 0; n < gmm_parameters.num_mixtures; n++) { + RemoveMean(x, mean_vec, gmm_parameters.dimension, v); + double q = ComputeExponent(v, covar_inv, gmm_parameters.dimension) + + gmm_parameters.weight[n]; + f += exp(q); + mean_vec += gmm_parameters.dimension; + covar_inv += gmm_parameters.dimension * gmm_parameters.dimension; + } + return f; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/gmm.h b/pkg/apm/webrtc/modules/audio_processing/vad/gmm.h new file mode 100644 index 00000000..d9d68ecf --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/gmm.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_VAD_GMM_H_ +#define MODULES_AUDIO_PROCESSING_VAD_GMM_H_ + +namespace webrtc { + +// A structure that specifies a GMM. +// A GMM is formulated as +// f(x) = w[0] * mixture[0] + w[1] * mixture[1] + ... + +// w[num_mixtures - 1] * mixture[num_mixtures - 1]; +// Where a 'mixture' is a Gaussian density. + +struct GmmParameters { + // weight[n] = log(w[n]) - `dimension`/2 * log(2*pi) - 1/2 * log(det(cov[n])); + // where cov[n] is the covariance matrix of mixture n; + const double* weight; + // pointer to the first element of a `num_mixtures`x`dimension` matrix + // where kth row is the mean of the kth mixture. + const double* mean; + // pointer to the first element of a `num_mixtures`x`dimension`x`dimension` + // 3D-matrix, where the kth 2D-matrix is the inverse of the covariance + // matrix of the kth mixture. + const double* covar_inverse; + // Dimensionality of the mixtures. + int dimension; + // number of the mixtures. + int num_mixtures; +}; + +// Evaluate the given GMM, according to `gmm_parameters`, at the given point +// `x`. If the dimensionality of the given GMM is larger that the maximum +// acceptable dimension by the following function -1 is returned. +double EvaluateGmm(const double* x, const GmmParameters& gmm_parameters); + +} // namespace webrtc +#endif // MODULES_AUDIO_PROCESSING_VAD_GMM_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/noise_gmm_tables.h b/pkg/apm/webrtc/modules/audio_processing/vad/noise_gmm_tables.h new file mode 100644 index 00000000..944a5401 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/noise_gmm_tables.h @@ -0,0 +1,82 @@ +/* + * 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. + */ + +// GMM tables for inactive segments. Generated by MakeGmmTables.m. + +#ifndef MODULES_AUDIO_PROCESSING_VAD_NOISE_GMM_TABLES_H_ +#define MODULES_AUDIO_PROCESSING_VAD_NOISE_GMM_TABLES_H_ + +namespace webrtc { + +static const int kNoiseGmmNumMixtures = 12; +static const int kNoiseGmmDim = 3; + +static const double + kNoiseGmmCovarInverse[kNoiseGmmNumMixtures][kNoiseGmmDim][kNoiseGmmDim] = { + {{7.36219567592941e+00, 4.83060785179861e-03, 1.23335151497610e-02}, + {4.83060785179861e-03, 1.65289507047817e-04, -2.41490588169997e-04}, + {1.23335151497610e-02, -2.41490588169997e-04, 6.59472060689382e-03}}, + {{8.70265239309140e+00, -5.30636201431086e-04, 5.44014966585347e-03}, + {-5.30636201431086e-04, 3.11095453521008e-04, -1.86287206836035e-04}, + {5.44014966585347e-03, -1.86287206836035e-04, 6.29493388790744e-04}}, + {{4.53467851955055e+00, -3.92977536695197e-03, -2.46521420693317e-03}, + {-3.92977536695197e-03, 4.94650752632750e-05, -1.08587438501826e-05}, + {-2.46521420693317e-03, -1.08587438501826e-05, 9.28793975422261e-05}}, + {{9.26817997114275e-01, -4.03976069276753e-04, -3.56441427392165e-03}, + {-4.03976069276753e-04, 2.51976251631430e-06, 1.46914206734572e-07}, + {-3.56441427392165e-03, 1.46914206734572e-07, 8.19914567685373e-05}}, + {{7.61715986787441e+00, -1.54889041216888e-04, 2.41756280071656e-02}, + {-1.54889041216888e-04, 3.50282550461672e-07, -6.27251196972490e-06}, + {2.41756280071656e-02, -6.27251196972490e-06, 1.45061847649872e-02}}, + {{8.31193642663158e+00, -3.84070508164323e-04, -3.09750630821876e-02}, + {-3.84070508164323e-04, 3.80433432277336e-07, -1.14321142836636e-06}, + {-3.09750630821876e-02, -1.14321142836636e-06, 8.35091486289997e-04}}, + {{9.67283151270894e-01, 5.82465812445039e-05, -3.18350798617053e-03}, + {5.82465812445039e-05, 2.23762672000318e-07, -7.74196587408623e-07}, + {-3.18350798617053e-03, -7.74196587408623e-07, 3.85120938338325e-04}}, + {{8.28066236985388e+00, 5.87634508319763e-05, 6.99303090891743e-03}, + {5.87634508319763e-05, 2.93746018618058e-07, 3.40843332882272e-07}, + {6.99303090891743e-03, 3.40843332882272e-07, 1.99379171190344e-04}}, + {{6.07488998675646e+00, -1.11494526618473e-02, 5.10013111123381e-03}, + {-1.11494526618473e-02, 6.99238879921751e-04, 5.36718550370870e-05}, + {5.10013111123381e-03, 5.36718550370870e-05, 5.26909853276753e-04}}, + {{6.90492021419175e+00, 4.20639355257863e-04, -2.38612752336481e-03}, + {4.20639355257863e-04, 3.31246767338153e-06, -2.42052288150859e-08}, + {-2.38612752336481e-03, -2.42052288150859e-08, 4.46608368363412e-04}}, + {{1.31069150869715e+01, -1.73718583865670e-04, -1.97591814508578e-02}, + {-1.73718583865670e-04, 2.80451716300124e-07, 9.96570755379865e-07}, + {-1.97591814508578e-02, 9.96570755379865e-07, 2.41361900868847e-03}}, + {{4.69566344239814e+00, -2.61077567563690e-04, 5.26359000761433e-03}, + {-2.61077567563690e-04, 1.82420859823767e-06, -7.83645887541601e-07}, + {5.26359000761433e-03, -7.83645887541601e-07, 1.33586288288802e-02}}}; + +static const double kNoiseGmmMean[kNoiseGmmNumMixtures][kNoiseGmmDim] = { + {-2.01386094766163e+00, 1.69702162045397e+02, 7.41715804872181e+01}, + {-1.94684591777290e+00, 1.42398396732668e+02, 1.64186321157831e+02}, + {-2.29319297562437e+00, 3.86415425589868e+02, 2.13452215267125e+02}, + {-3.25487177070268e+00, 1.08668712553616e+03, 2.33119949467419e+02}, + {-2.13159632447467e+00, 4.83821702557717e+03, 6.86786166673740e+01}, + {-2.26171410780526e+00, 4.79420193982422e+03, 1.53222513286450e+02}, + {-3.32166740703185e+00, 4.35161135834358e+03, 1.33206448431316e+02}, + {-2.19290322814343e+00, 3.98325506609408e+03, 2.13249167359934e+02}, + {-2.02898459255404e+00, 7.37039893155007e+03, 1.12518527491926e+02}, + {-2.26150236399500e+00, 1.54896745196145e+03, 1.49717357868579e+02}, + {-2.00417668301790e+00, 3.82434760310304e+03, 1.07438913004312e+02}, + {-2.30193040814533e+00, 1.43953696546439e+03, 7.04085275122649e+01}}; + +static const double kNoiseGmmWeights[kNoiseGmmNumMixtures] = { + -1.09422832086193e+01, -1.10847897513425e+01, -1.36767587732187e+01, + -1.79789356118641e+01, -1.42830169160894e+01, -1.56500228061379e+01, + -1.83124990950113e+01, -1.69979436177477e+01, -1.12329424387828e+01, + -1.41311785780639e+01, -1.47171861448585e+01, -1.35963362781839e+01}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_VAD_NOISE_GMM_TABLES_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/pitch_based_vad.cc b/pkg/apm/webrtc/modules/audio_processing/vad/pitch_based_vad.cc new file mode 100644 index 00000000..68e60dc6 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/pitch_based_vad.cc @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/vad/pitch_based_vad.h" + +#include + +#include "modules/audio_processing/vad/common.h" +#include "modules/audio_processing/vad/noise_gmm_tables.h" +#include "modules/audio_processing/vad/vad_circular_buffer.h" +#include "modules/audio_processing/vad/voice_gmm_tables.h" + +namespace webrtc { + +static_assert(kNoiseGmmDim == kVoiceGmmDim, + "noise and voice gmm dimension not equal"); + +// These values should match MATLAB counterparts for unit-tests to pass. +static const int kPosteriorHistorySize = 500; // 5 sec of 10 ms frames. +static const double kInitialPriorProbability = 0.3; +static const int kTransientWidthThreshold = 7; +static const double kLowProbabilityThreshold = 0.2; + +static double LimitProbability(double p) { + const double kLimHigh = 0.99; + const double kLimLow = 0.01; + + if (p > kLimHigh) + p = kLimHigh; + else if (p < kLimLow) + p = kLimLow; + return p; +} + +PitchBasedVad::PitchBasedVad() + : p_prior_(kInitialPriorProbability), + circular_buffer_(VadCircularBuffer::Create(kPosteriorHistorySize)) { + // Setup noise GMM. + noise_gmm_.dimension = kNoiseGmmDim; + noise_gmm_.num_mixtures = kNoiseGmmNumMixtures; + noise_gmm_.weight = kNoiseGmmWeights; + noise_gmm_.mean = &kNoiseGmmMean[0][0]; + noise_gmm_.covar_inverse = &kNoiseGmmCovarInverse[0][0][0]; + + // Setup voice GMM. + voice_gmm_.dimension = kVoiceGmmDim; + voice_gmm_.num_mixtures = kVoiceGmmNumMixtures; + voice_gmm_.weight = kVoiceGmmWeights; + voice_gmm_.mean = &kVoiceGmmMean[0][0]; + voice_gmm_.covar_inverse = &kVoiceGmmCovarInverse[0][0][0]; +} + +PitchBasedVad::~PitchBasedVad() {} + +int PitchBasedVad::VoicingProbability(const AudioFeatures& features, + double* p_combined) { + double p; + double gmm_features[3]; + double pdf_features_given_voice; + double pdf_features_given_noise; + // These limits are the same in matlab implementation 'VoicingProbGMM().' + const double kLimLowLogPitchGain = -2.0; + const double kLimHighLogPitchGain = -0.9; + const double kLimLowSpectralPeak = 200; + const double kLimHighSpectralPeak = 2000; + const double kEps = 1e-12; + for (size_t n = 0; n < features.num_frames; n++) { + gmm_features[0] = features.log_pitch_gain[n]; + gmm_features[1] = features.spectral_peak[n]; + gmm_features[2] = features.pitch_lag_hz[n]; + + pdf_features_given_voice = EvaluateGmm(gmm_features, voice_gmm_); + pdf_features_given_noise = EvaluateGmm(gmm_features, noise_gmm_); + + if (features.spectral_peak[n] < kLimLowSpectralPeak || + features.spectral_peak[n] > kLimHighSpectralPeak || + features.log_pitch_gain[n] < kLimLowLogPitchGain) { + pdf_features_given_voice = kEps * pdf_features_given_noise; + } else if (features.log_pitch_gain[n] > kLimHighLogPitchGain) { + pdf_features_given_noise = kEps * pdf_features_given_voice; + } + + p = p_prior_ * pdf_features_given_voice / + (pdf_features_given_voice * p_prior_ + + pdf_features_given_noise * (1 - p_prior_)); + + p = LimitProbability(p); + + // Combine pitch-based probability with standalone probability, before + // updating prior probabilities. + double prod_active = p * p_combined[n]; + double prod_inactive = (1 - p) * (1 - p_combined[n]); + p_combined[n] = prod_active / (prod_active + prod_inactive); + + if (UpdatePrior(p_combined[n]) < 0) + return -1; + // Limit prior probability. With a zero prior probability the posterior + // probability is always zero. + p_prior_ = LimitProbability(p_prior_); + } + return 0; +} + +int PitchBasedVad::UpdatePrior(double p) { + circular_buffer_->Insert(p); + if (circular_buffer_->RemoveTransient(kTransientWidthThreshold, + kLowProbabilityThreshold) < 0) + return -1; + p_prior_ = circular_buffer_->Mean(); + return 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/pitch_based_vad.h b/pkg/apm/webrtc/modules/audio_processing/vad/pitch_based_vad.h new file mode 100644 index 00000000..fa3abc2d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/pitch_based_vad.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_VAD_PITCH_BASED_VAD_H_ +#define MODULES_AUDIO_PROCESSING_VAD_PITCH_BASED_VAD_H_ + +#include + +#include "modules/audio_processing/vad/common.h" +#include "modules/audio_processing/vad/gmm.h" + +namespace webrtc { + +class VadCircularBuffer; + +// Computes the probability of the input audio frame to be active given +// the corresponding pitch-gain and lag of the frame. +class PitchBasedVad { + public: + PitchBasedVad(); + ~PitchBasedVad(); + + // Compute pitch-based voicing probability, given the features. + // features: a structure containing features required for computing voicing + // probabilities. + // + // p_combined: an array which contains the combined activity probabilities + // computed prior to the call of this function. The method, + // then, computes the voicing probabilities and combine them + // with the given values. The result are returned in `p`. + int VoicingProbability(const AudioFeatures& features, double* p_combined); + + private: + int UpdatePrior(double p); + + // TODO(turajs): maybe defining this at a higher level (maybe enum) so that + // all the code recognize it as "no-error." + static const int kNoError = 0; + + GmmParameters noise_gmm_; + GmmParameters voice_gmm_; + + double p_prior_; + + std::unique_ptr circular_buffer_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_VAD_PITCH_BASED_VAD_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/pitch_internal.cc b/pkg/apm/webrtc/modules/audio_processing/vad/pitch_internal.cc new file mode 100644 index 00000000..8f869186 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/pitch_internal.cc @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/vad/pitch_internal.h" + +#include + +namespace webrtc { + +// A 4-to-3 linear interpolation. +// The interpolation constants are derived as following: +// Input pitch parameters are updated every 7.5 ms. Within a 30-ms interval +// we are interested in pitch parameters of 0-5 ms, 10-15ms and 20-25ms. This is +// like interpolating 4-to-6 and keep the odd samples. +// The reason behind this is that LPC coefficients are computed for the first +// half of each 10ms interval. +static void PitchInterpolation(double old_val, const double* in, double* out) { + out[0] = 1. / 6. * old_val + 5. / 6. * in[0]; + out[1] = 5. / 6. * in[1] + 1. / 6. * in[2]; + out[2] = 0.5 * in[2] + 0.5 * in[3]; +} + +void GetSubframesPitchParameters(int sampling_rate_hz, + double* gains, + double* lags, + int num_in_frames, + int num_out_frames, + double* log_old_gain, + double* old_lag, + double* log_pitch_gain, + double* pitch_lag_hz) { + // Gain interpolation is in log-domain, also returned in log-domain. + for (int n = 0; n < num_in_frames; n++) + gains[n] = log(gains[n] + 1e-12); + + // Interpolate lags and gains. + PitchInterpolation(*log_old_gain, gains, log_pitch_gain); + *log_old_gain = gains[num_in_frames - 1]; + PitchInterpolation(*old_lag, lags, pitch_lag_hz); + *old_lag = lags[num_in_frames - 1]; + + // Convert pitch-lags to Hertz. + for (int n = 0; n < num_out_frames; n++) { + pitch_lag_hz[n] = (sampling_rate_hz) / (pitch_lag_hz[n]); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/pitch_internal.h b/pkg/apm/webrtc/modules/audio_processing/vad/pitch_internal.h new file mode 100644 index 00000000..e382c1fb --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/pitch_internal.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_VAD_PITCH_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_VAD_PITCH_INTERNAL_H_ + +namespace webrtc { + +// TODO(turajs): Write a description of this function. Also be consistent with +// usage of `sampling_rate_hz` vs `kSamplingFreqHz`. +void GetSubframesPitchParameters(int sampling_rate_hz, + double* gains, + double* lags, + int num_in_frames, + int num_out_frames, + double* log_old_gain, + double* old_lag, + double* log_pitch_gain, + double* pitch_lag_hz); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_VAD_PITCH_INTERNAL_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/pole_zero_filter.cc b/pkg/apm/webrtc/modules/audio_processing/vad/pole_zero_filter.cc new file mode 100644 index 00000000..e7a61130 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/pole_zero_filter.cc @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/vad/pole_zero_filter.h" + +#include + +#include + +namespace webrtc { + +PoleZeroFilter* PoleZeroFilter::Create(const float* numerator_coefficients, + size_t order_numerator, + const float* denominator_coefficients, + size_t order_denominator) { + if (order_numerator > kMaxFilterOrder || + order_denominator > kMaxFilterOrder || denominator_coefficients[0] == 0 || + numerator_coefficients == NULL || denominator_coefficients == NULL) + return NULL; + return new PoleZeroFilter(numerator_coefficients, order_numerator, + denominator_coefficients, order_denominator); +} + +PoleZeroFilter::PoleZeroFilter(const float* numerator_coefficients, + size_t order_numerator, + const float* denominator_coefficients, + size_t order_denominator) + : past_input_(), + past_output_(), + numerator_coefficients_(), + denominator_coefficients_(), + order_numerator_(order_numerator), + order_denominator_(order_denominator), + highest_order_(std::max(order_denominator, order_numerator)) { + memcpy(numerator_coefficients_, numerator_coefficients, + sizeof(numerator_coefficients_[0]) * (order_numerator_ + 1)); + memcpy(denominator_coefficients_, denominator_coefficients, + sizeof(denominator_coefficients_[0]) * (order_denominator_ + 1)); + + if (denominator_coefficients_[0] != 1) { + for (size_t n = 0; n <= order_numerator_; n++) + numerator_coefficients_[n] /= denominator_coefficients_[0]; + for (size_t n = 0; n <= order_denominator_; n++) + denominator_coefficients_[n] /= denominator_coefficients_[0]; + } +} + +template +static float FilterArPast(const T* past, + size_t order, + const float* coefficients) { + float sum = 0.0f; + size_t past_index = order - 1; + for (size_t k = 1; k <= order; k++, past_index--) + sum += coefficients[k] * past[past_index]; + return sum; +} + +int PoleZeroFilter::Filter(const int16_t* in, + size_t num_input_samples, + float* output) { + if (in == NULL || output == NULL) + return -1; + // This is the typical case, just a memcpy. + const size_t k = std::min(num_input_samples, highest_order_); + size_t n; + for (n = 0; n < k; n++) { + output[n] = in[n] * numerator_coefficients_[0]; + output[n] += FilterArPast(&past_input_[n], order_numerator_, + numerator_coefficients_); + output[n] -= FilterArPast(&past_output_[n], order_denominator_, + denominator_coefficients_); + + past_input_[n + order_numerator_] = in[n]; + past_output_[n + order_denominator_] = output[n]; + } + if (highest_order_ < num_input_samples) { + for (size_t m = 0; n < num_input_samples; n++, m++) { + output[n] = in[n] * numerator_coefficients_[0]; + output[n] += + FilterArPast(&in[m], order_numerator_, numerator_coefficients_); + output[n] -= FilterArPast(&output[m], order_denominator_, + denominator_coefficients_); + } + // Record into the past signal. + memcpy(past_input_, &in[num_input_samples - order_numerator_], + sizeof(in[0]) * order_numerator_); + memcpy(past_output_, &output[num_input_samples - order_denominator_], + sizeof(output[0]) * order_denominator_); + } else { + // Odd case that the length of the input is shorter that filter order. + memmove(past_input_, &past_input_[num_input_samples], + order_numerator_ * sizeof(past_input_[0])); + memmove(past_output_, &past_output_[num_input_samples], + order_denominator_ * sizeof(past_output_[0])); + } + return 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/pole_zero_filter.h b/pkg/apm/webrtc/modules/audio_processing/vad/pole_zero_filter.h new file mode 100644 index 00000000..11a05114 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/pole_zero_filter.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_VAD_POLE_ZERO_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_VAD_POLE_ZERO_FILTER_H_ + +#include +#include + +namespace webrtc { + +class PoleZeroFilter { + public: + ~PoleZeroFilter() {} + + static PoleZeroFilter* Create(const float* numerator_coefficients, + size_t order_numerator, + const float* denominator_coefficients, + size_t order_denominator); + + int Filter(const int16_t* in, size_t num_input_samples, float* output); + + private: + PoleZeroFilter(const float* numerator_coefficients, + size_t order_numerator, + const float* denominator_coefficients, + size_t order_denominator); + + static const int kMaxFilterOrder = 24; + + int16_t past_input_[kMaxFilterOrder * 2]; + float past_output_[kMaxFilterOrder * 2]; + + float numerator_coefficients_[kMaxFilterOrder + 1]; + float denominator_coefficients_[kMaxFilterOrder + 1]; + + size_t order_numerator_; + size_t order_denominator_; + size_t highest_order_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_VAD_POLE_ZERO_FILTER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/standalone_vad.cc b/pkg/apm/webrtc/modules/audio_processing/vad/standalone_vad.cc new file mode 100644 index 00000000..1397668e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/standalone_vad.cc @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/vad/standalone_vad.h" + +#include + +#include "common_audio/vad/include/webrtc_vad.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +static const int kDefaultStandaloneVadMode = 3; + +StandaloneVad::StandaloneVad(VadInst* vad) + : vad_(vad), buffer_(), index_(0), mode_(kDefaultStandaloneVadMode) {} + +StandaloneVad::~StandaloneVad() { + WebRtcVad_Free(vad_); +} + +StandaloneVad* StandaloneVad::Create() { + VadInst* vad = WebRtcVad_Create(); + if (!vad) + return nullptr; + + int err = WebRtcVad_Init(vad); + err |= WebRtcVad_set_mode(vad, kDefaultStandaloneVadMode); + if (err != 0) { + WebRtcVad_Free(vad); + return nullptr; + } + return new StandaloneVad(vad); +} + +int StandaloneVad::AddAudio(const int16_t* data, size_t length) { + if (length != kLength10Ms) + return -1; + + if (index_ + length > kLength10Ms * kMaxNum10msFrames) + // Reset the buffer if it's full. + // TODO(ajm): Instead, consider just processing every 10 ms frame. Then we + // can forgo the buffering. + index_ = 0; + + memcpy(&buffer_[index_], data, sizeof(int16_t) * length); + index_ += length; + return 0; +} + +int StandaloneVad::GetActivity(double* p, size_t length_p) { + if (index_ == 0) + return -1; + + const size_t num_frames = index_ / kLength10Ms; + if (num_frames > length_p) + return -1; + RTC_DCHECK_EQ(0, WebRtcVad_ValidRateAndFrameLength(kSampleRateHz, index_)); + + int activity = WebRtcVad_Process(vad_, kSampleRateHz, buffer_, index_); + if (activity < 0) + return -1; + else if (activity == 0) + p[0] = 0.01; // Arbitrary but small and non-zero. + else + p[0] = 0.5; // 0.5 is neutral values when combinned by other probabilities. + for (size_t n = 1; n < num_frames; n++) + p[n] = p[0]; + // Reset the buffer to start from the beginning. + index_ = 0; + return activity; +} + +int StandaloneVad::set_mode(int mode) { + if (mode < 0 || mode > 3) + return -1; + if (WebRtcVad_set_mode(vad_, mode) != 0) + return -1; + + mode_ = mode; + return 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/standalone_vad.h b/pkg/apm/webrtc/modules/audio_processing/vad/standalone_vad.h new file mode 100644 index 00000000..b0846337 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/standalone_vad.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC_STANDALONE_VAD_H_ +#define MODULES_AUDIO_PROCESSING_AGC_STANDALONE_VAD_H_ + +#include +#include + +#include "common_audio/vad/include/webrtc_vad.h" +#include "modules/audio_processing/vad/common.h" + +namespace webrtc { + +class StandaloneVad { + public: + static StandaloneVad* Create(); + ~StandaloneVad(); + + // Outputs + // p: a buffer where probabilities are written to. + // length_p: number of elements of `p`. + // + // return value: + // -1: if no audio is stored or VAD returns error. + // 0: in success. + // In case of error the content of `activity` is unchanged. + // + // Note that due to a high false-positive (VAD decision is active while the + // processed audio is just background noise) rate, stand-alone VAD is used as + // a one-sided indicator. The activity probability is 0.5 if the frame is + // classified as active, and the probability is 0.01 if the audio is + // classified as passive. In this way, when probabilities are combined, the + // effect of the stand-alone VAD is neutral if the input is classified as + // active. + int GetActivity(double* p, size_t length_p); + + // Expecting 10 ms of 16 kHz audio to be pushed in. + int AddAudio(const int16_t* data, size_t length); + + // Set aggressiveness of VAD, 0 is the least aggressive and 3 is the most + // aggressive mode. Returns -1 if the input is less than 0 or larger than 3, + // otherwise 0 is returned. + int set_mode(int mode); + // Get the agressiveness of the current VAD. + int mode() const { return mode_; } + + private: + explicit StandaloneVad(VadInst* vad); + + static const size_t kMaxNum10msFrames = 3; + + // TODO(turajs): Is there a way to use scoped-pointer here? + VadInst* vad_; + int16_t buffer_[kMaxNum10msFrames * kLength10Ms]; + size_t index_; + int mode_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_STANDALONE_VAD_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/vad.go b/pkg/apm/webrtc/modules/audio_processing/vad/vad.go new file mode 100644 index 00000000..20e53da2 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/vad.go @@ -0,0 +1,10 @@ +//go:build console + +package vad + +// #cgo CXXFLAGS: -I${SRCDIR}/../../.. -I${SRCDIR}/../../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc.cc b/pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc.cc new file mode 100644 index 00000000..aaf8214d --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc.cc @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/vad/vad_audio_proc.h" + +#include +#include +#include + +#include "common_audio/third_party/ooura/fft_size_256/fft4g.h" +#include "modules/audio_processing/vad/pitch_internal.h" +#include "modules/audio_processing/vad/pole_zero_filter.h" +#include "modules/audio_processing/vad/vad_audio_proc_internal.h" +#include "rtc_base/checks.h" +extern "C" { +#include "modules/audio_coding/codecs/isac/main/source/filter_functions.h" +#include "modules/audio_coding/codecs/isac/main/source/isac_vad.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" +} + +namespace webrtc { + +// The following structures are declared anonymous in iSAC's structs.h. To +// forward declare them, we use this derived class trick. +struct VadAudioProc::PitchAnalysisStruct : public ::PitchAnalysisStruct {}; +struct VadAudioProc::PreFiltBankstr : public ::PreFiltBankstr {}; + +static constexpr float kFrequencyResolution = + kSampleRateHz / static_cast(VadAudioProc::kDftSize); +static constexpr int kSilenceRms = 5; + +// TODO(turajs): Make a Create or Init for VadAudioProc. +VadAudioProc::VadAudioProc() + : audio_buffer_(), + num_buffer_samples_(kNumPastSignalSamples), + log_old_gain_(-2), + old_lag_(50), // Arbitrary but valid as pitch-lag (in samples). + pitch_analysis_handle_(new PitchAnalysisStruct), + pre_filter_handle_(new PreFiltBankstr), + high_pass_filter_(PoleZeroFilter::Create(kCoeffNumerator, + kFilterOrder, + kCoeffDenominator, + kFilterOrder)) { + static_assert(kNumPastSignalSamples + kNumSubframeSamples == + sizeof(kLpcAnalWin) / sizeof(kLpcAnalWin[0]), + "lpc analysis window incorrect size"); + static_assert(kLpcOrder + 1 == sizeof(kCorrWeight) / sizeof(kCorrWeight[0]), + "correlation weight incorrect size"); + + // TODO(turajs): Are we doing too much in the constructor? + float data[kDftSize]; + // Make FFT to initialize. + ip_[0] = 0; + WebRtc_rdft(kDftSize, 1, data, ip_, w_fft_); + // TODO(turajs): Need to initialize high-pass filter. + + // Initialize iSAC components. + WebRtcIsac_InitPreFilterbank(pre_filter_handle_.get()); + WebRtcIsac_InitPitchAnalysis(pitch_analysis_handle_.get()); +} + +VadAudioProc::~VadAudioProc() {} + +void VadAudioProc::ResetBuffer() { + memcpy(audio_buffer_, &audio_buffer_[kNumSamplesToProcess], + sizeof(audio_buffer_[0]) * kNumPastSignalSamples); + num_buffer_samples_ = kNumPastSignalSamples; +} + +int VadAudioProc::ExtractFeatures(const int16_t* frame, + size_t length, + AudioFeatures* features) { + features->num_frames = 0; + if (length != kNumSubframeSamples) { + return -1; + } + + // High-pass filter to remove the DC component and very low frequency content. + // We have experienced that this high-pass filtering improves voice/non-voiced + // classification. + if (high_pass_filter_->Filter(frame, kNumSubframeSamples, + &audio_buffer_[num_buffer_samples_]) != 0) { + return -1; + } + + num_buffer_samples_ += kNumSubframeSamples; + if (num_buffer_samples_ < kBufferLength) { + return 0; + } + RTC_DCHECK_EQ(num_buffer_samples_, kBufferLength); + features->num_frames = kNum10msSubframes; + features->silence = false; + + Rms(features->rms, kMaxNumFrames); + for (size_t i = 0; i < kNum10msSubframes; ++i) { + if (features->rms[i] < kSilenceRms) { + // PitchAnalysis can cause NaNs in the pitch gain if it's fed silence. + // Bail out here instead. + features->silence = true; + ResetBuffer(); + return 0; + } + } + + PitchAnalysis(features->log_pitch_gain, features->pitch_lag_hz, + kMaxNumFrames); + FindFirstSpectralPeaks(features->spectral_peak, kMaxNumFrames); + ResetBuffer(); + return 0; +} + +// Computes |kLpcOrder + 1| correlation coefficients. +void VadAudioProc::SubframeCorrelation(double* corr, + size_t length_corr, + size_t subframe_index) { + RTC_DCHECK_GE(length_corr, kLpcOrder + 1); + double windowed_audio[kNumSubframeSamples + kNumPastSignalSamples]; + size_t buffer_index = subframe_index * kNumSubframeSamples; + + for (size_t n = 0; n < kNumSubframeSamples + kNumPastSignalSamples; n++) + windowed_audio[n] = audio_buffer_[buffer_index++] * kLpcAnalWin[n]; + + WebRtcIsac_AutoCorr(corr, windowed_audio, + kNumSubframeSamples + kNumPastSignalSamples, kLpcOrder); +} + +// Compute `kNum10msSubframes` sets of LPC coefficients, one per 10 ms input. +// The analysis window is 15 ms long and it is centered on the first half of +// each 10ms sub-frame. This is equivalent to computing LPC coefficients for the +// first half of each 10 ms subframe. +void VadAudioProc::GetLpcPolynomials(double* lpc, size_t length_lpc) { + RTC_DCHECK_GE(length_lpc, kNum10msSubframes * (kLpcOrder + 1)); + double corr[kLpcOrder + 1]; + double reflec_coeff[kLpcOrder]; + for (size_t i = 0, offset_lpc = 0; i < kNum10msSubframes; + i++, offset_lpc += kLpcOrder + 1) { + SubframeCorrelation(corr, kLpcOrder + 1, i); + corr[0] *= 1.0001; + // This makes Lev-Durb a bit more stable. + for (size_t k = 0; k < kLpcOrder + 1; k++) { + corr[k] *= kCorrWeight[k]; + } + WebRtcIsac_LevDurb(&lpc[offset_lpc], reflec_coeff, corr, kLpcOrder); + } +} + +// Fit a second order curve to these 3 points and find the location of the +// extremum. The points are inverted before curve fitting. +static float QuadraticInterpolation(float prev_val, + float curr_val, + float next_val) { + // Doing the interpolation in |1 / A(z)|^2. + float fractional_index = 0; + next_val = 1.0f / next_val; + prev_val = 1.0f / prev_val; + curr_val = 1.0f / curr_val; + + fractional_index = + -(next_val - prev_val) * 0.5f / (next_val + prev_val - 2.f * curr_val); + RTC_DCHECK_LT(fabs(fractional_index), 1); + return fractional_index; +} + +// 1 / A(z), where A(z) is defined by `lpc` is a model of the spectral envelope +// of the input signal. The local maximum of the spectral envelope corresponds +// with the local minimum of A(z). It saves complexity, as we save one +// inversion. Furthermore, we find the first local maximum of magnitude squared, +// to save on one square root. +void VadAudioProc::FindFirstSpectralPeaks(double* f_peak, + size_t length_f_peak) { + RTC_DCHECK_GE(length_f_peak, kNum10msSubframes); + double lpc[kNum10msSubframes * (kLpcOrder + 1)]; + // For all sub-frames. + GetLpcPolynomials(lpc, kNum10msSubframes * (kLpcOrder + 1)); + + const size_t kNumDftCoefficients = kDftSize / 2 + 1; + float data[kDftSize]; + + for (size_t i = 0; i < kNum10msSubframes; i++) { + // Convert to float with zero pad. + memset(data, 0, sizeof(data)); + for (size_t n = 0; n < kLpcOrder + 1; n++) { + data[n] = static_cast(lpc[i * (kLpcOrder + 1) + n]); + } + // Transform to frequency domain. + WebRtc_rdft(kDftSize, 1, data, ip_, w_fft_); + + size_t index_peak = 0; + float prev_magn_sqr = data[0] * data[0]; + float curr_magn_sqr = data[2] * data[2] + data[3] * data[3]; + float next_magn_sqr; + bool found_peak = false; + for (size_t n = 2; n < kNumDftCoefficients - 1; n++) { + next_magn_sqr = + data[2 * n] * data[2 * n] + data[2 * n + 1] * data[2 * n + 1]; + if (curr_magn_sqr < prev_magn_sqr && curr_magn_sqr < next_magn_sqr) { + found_peak = true; + index_peak = n - 1; + break; + } + prev_magn_sqr = curr_magn_sqr; + curr_magn_sqr = next_magn_sqr; + } + float fractional_index = 0; + if (!found_peak) { + // Checking if |kNumDftCoefficients - 1| is the local minimum. + next_magn_sqr = data[1] * data[1]; + if (curr_magn_sqr < prev_magn_sqr && curr_magn_sqr < next_magn_sqr) { + index_peak = kNumDftCoefficients - 1; + } + } else { + // A peak is found, do a simple quadratic interpolation to get a more + // accurate estimate of the peak location. + fractional_index = + QuadraticInterpolation(prev_magn_sqr, curr_magn_sqr, next_magn_sqr); + } + f_peak[i] = (index_peak + fractional_index) * kFrequencyResolution; + } +} + +// Using iSAC functions to estimate pitch gains & lags. +void VadAudioProc::PitchAnalysis(double* log_pitch_gains, + double* pitch_lags_hz, + size_t length) { + // TODO(turajs): This can be "imported" from iSAC & and the next two + // constants. + RTC_DCHECK_GE(length, kNum10msSubframes); + const int kNumPitchSubframes = 4; + double gains[kNumPitchSubframes]; + double lags[kNumPitchSubframes]; + + const int kNumSubbandFrameSamples = 240; + const int kNumLookaheadSamples = 24; + + float lower[kNumSubbandFrameSamples]; + float upper[kNumSubbandFrameSamples]; + double lower_lookahead[kNumSubbandFrameSamples]; + double upper_lookahead[kNumSubbandFrameSamples]; + double lower_lookahead_pre_filter[kNumSubbandFrameSamples + + kNumLookaheadSamples]; + + // Split signal to lower and upper bands + WebRtcIsac_SplitAndFilterFloat(&audio_buffer_[kNumPastSignalSamples], lower, + upper, lower_lookahead, upper_lookahead, + pre_filter_handle_.get()); + WebRtcIsac_PitchAnalysis(lower_lookahead, lower_lookahead_pre_filter, + pitch_analysis_handle_.get(), lags, gains); + + // Lags are computed on lower-band signal with sampling rate half of the + // input signal. + GetSubframesPitchParameters( + kSampleRateHz / 2, gains, lags, kNumPitchSubframes, kNum10msSubframes, + &log_old_gain_, &old_lag_, log_pitch_gains, pitch_lags_hz); +} + +void VadAudioProc::Rms(double* rms, size_t length_rms) { + RTC_DCHECK_GE(length_rms, kNum10msSubframes); + size_t offset = kNumPastSignalSamples; + for (size_t i = 0; i < kNum10msSubframes; i++) { + rms[i] = 0; + for (size_t n = 0; n < kNumSubframeSamples; n++, offset++) + rms[i] += audio_buffer_[offset] * audio_buffer_[offset]; + rms[i] = sqrt(rms[i] / kNumSubframeSamples); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc.h b/pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc.h new file mode 100644 index 00000000..905c687e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_H_ +#define MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_H_ + +#include +#include + +#include + +#include "modules/audio_processing/vad/common.h" // AudioFeatures, kSampleR... + +namespace webrtc { + +class PoleZeroFilter; + +class VadAudioProc { + public: + // Forward declare iSAC structs. + struct PitchAnalysisStruct; + struct PreFiltBankstr; + + VadAudioProc(); + ~VadAudioProc(); + + int ExtractFeatures(const int16_t* audio_frame, + size_t length, + AudioFeatures* audio_features); + + static constexpr size_t kDftSize = 512; + + private: + void PitchAnalysis(double* pitch_gains, double* pitch_lags_hz, size_t length); + void SubframeCorrelation(double* corr, + size_t length_corr, + size_t subframe_index); + void GetLpcPolynomials(double* lpc, size_t length_lpc); + void FindFirstSpectralPeaks(double* f_peak, size_t length_f_peak); + void Rms(double* rms, size_t length_rms); + void ResetBuffer(); + + // To compute spectral peak we perform LPC analysis to get spectral envelope. + // For every 30 ms we compute 3 spectral peak there for 3 LPC analysis. + // LPC is computed over 15 ms of windowed audio. For every 10 ms sub-frame + // we need 5 ms of past signal to create the input of LPC analysis. + static constexpr size_t kNumPastSignalSamples = size_t{kSampleRateHz / 200}; + + // TODO(turajs): maybe defining this at a higher level (maybe enum) so that + // all the code recognize it as "no-error." + static constexpr int kNoError = 0; + + static constexpr size_t kNum10msSubframes = 3; + static constexpr size_t kNumSubframeSamples = size_t{kSampleRateHz / 100}; + // Samples in 30 ms @ given sampling rate. + static constexpr size_t kNumSamplesToProcess = + kNum10msSubframes * kNumSubframeSamples; + static constexpr size_t kBufferLength = + kNumPastSignalSamples + kNumSamplesToProcess; + static constexpr size_t kIpLength = kDftSize >> 1; + static constexpr size_t kWLength = kDftSize >> 1; + static constexpr size_t kLpcOrder = 16; + + size_t ip_[kIpLength]; + float w_fft_[kWLength]; + + // A buffer of 5 ms (past audio) + 30 ms (one iSAC frame ). + float audio_buffer_[kBufferLength]; + size_t num_buffer_samples_; + + double log_old_gain_; + double old_lag_; + + std::unique_ptr pitch_analysis_handle_; + std::unique_ptr pre_filter_handle_; + std::unique_ptr high_pass_filter_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc_internal.h b/pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc_internal.h new file mode 100644 index 00000000..93589aff --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/vad_audio_proc_internal.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_INTERNAL_H_ + +#include + +namespace webrtc { + +// These values should match MATLAB counterparts for unit-tests to pass. +static const double kCorrWeight[] = { + 1.000000, 0.985000, 0.970225, 0.955672, 0.941337, 0.927217, + 0.913308, 0.899609, 0.886115, 0.872823, 0.859730, 0.846834, + 0.834132, 0.821620, 0.809296, 0.797156, 0.785199}; + +static const double kLpcAnalWin[] = { + 0.00000000, 0.01314436, 0.02628645, 0.03942400, 0.05255473, 0.06567639, + 0.07878670, 0.09188339, 0.10496421, 0.11802689, 0.13106918, 0.14408883, + 0.15708358, 0.17005118, 0.18298941, 0.19589602, 0.20876878, 0.22160547, + 0.23440387, 0.24716177, 0.25987696, 0.27254725, 0.28517045, 0.29774438, + 0.31026687, 0.32273574, 0.33514885, 0.34750406, 0.35979922, 0.37203222, + 0.38420093, 0.39630327, 0.40833713, 0.42030043, 0.43219112, 0.44400713, + 0.45574642, 0.46740697, 0.47898676, 0.49048379, 0.50189608, 0.51322164, + 0.52445853, 0.53560481, 0.54665854, 0.55761782, 0.56848075, 0.57924546, + 0.58991008, 0.60047278, 0.61093173, 0.62128512, 0.63153117, 0.64166810, + 0.65169416, 0.66160761, 0.67140676, 0.68108990, 0.69065536, 0.70010148, + 0.70942664, 0.71862923, 0.72770765, 0.73666033, 0.74548573, 0.75418233, + 0.76274862, 0.77118312, 0.77948437, 0.78765094, 0.79568142, 0.80357442, + 0.81132858, 0.81894256, 0.82641504, 0.83374472, 0.84093036, 0.84797069, + 0.85486451, 0.86161063, 0.86820787, 0.87465511, 0.88095122, 0.88709512, + 0.89308574, 0.89892206, 0.90460306, 0.91012776, 0.91549520, 0.92070447, + 0.92575465, 0.93064488, 0.93537432, 0.93994213, 0.94434755, 0.94858979, + 0.95266814, 0.95658189, 0.96033035, 0.96391289, 0.96732888, 0.97057773, + 0.97365889, 0.97657181, 0.97931600, 0.98189099, 0.98429632, 0.98653158, + 0.98859639, 0.99049038, 0.99221324, 0.99376466, 0.99514438, 0.99635215, + 0.99738778, 0.99825107, 0.99894188, 0.99946010, 0.99980562, 0.99997840, + 0.99997840, 0.99980562, 0.99946010, 0.99894188, 0.99825107, 0.99738778, + 0.99635215, 0.99514438, 0.99376466, 0.99221324, 0.99049038, 0.98859639, + 0.98653158, 0.98429632, 0.98189099, 0.97931600, 0.97657181, 0.97365889, + 0.97057773, 0.96732888, 0.96391289, 0.96033035, 0.95658189, 0.95266814, + 0.94858979, 0.94434755, 0.93994213, 0.93537432, 0.93064488, 0.92575465, + 0.92070447, 0.91549520, 0.91012776, 0.90460306, 0.89892206, 0.89308574, + 0.88709512, 0.88095122, 0.87465511, 0.86820787, 0.86161063, 0.85486451, + 0.84797069, 0.84093036, 0.83374472, 0.82641504, 0.81894256, 0.81132858, + 0.80357442, 0.79568142, 0.78765094, 0.77948437, 0.77118312, 0.76274862, + 0.75418233, 0.74548573, 0.73666033, 0.72770765, 0.71862923, 0.70942664, + 0.70010148, 0.69065536, 0.68108990, 0.67140676, 0.66160761, 0.65169416, + 0.64166810, 0.63153117, 0.62128512, 0.61093173, 0.60047278, 0.58991008, + 0.57924546, 0.56848075, 0.55761782, 0.54665854, 0.53560481, 0.52445853, + 0.51322164, 0.50189608, 0.49048379, 0.47898676, 0.46740697, 0.45574642, + 0.44400713, 0.43219112, 0.42030043, 0.40833713, 0.39630327, 0.38420093, + 0.37203222, 0.35979922, 0.34750406, 0.33514885, 0.32273574, 0.31026687, + 0.29774438, 0.28517045, 0.27254725, 0.25987696, 0.24716177, 0.23440387, + 0.22160547, 0.20876878, 0.19589602, 0.18298941, 0.17005118, 0.15708358, + 0.14408883, 0.13106918, 0.11802689, 0.10496421, 0.09188339, 0.07878670, + 0.06567639, 0.05255473, 0.03942400, 0.02628645, 0.01314436, 0.00000000}; + +static const size_t kFilterOrder = 2; +static const float kCoeffNumerator[kFilterOrder + 1] = {0.974827f, -1.949650f, + 0.974827f}; +static const float kCoeffDenominator[kFilterOrder + 1] = {1.0f, -1.971999f, + 0.972457f}; + +static_assert(kFilterOrder + 1 == + sizeof(kCoeffNumerator) / sizeof(kCoeffNumerator[0]), + "numerator coefficients incorrect size"); +static_assert(kFilterOrder + 1 == + sizeof(kCoeffDenominator) / sizeof(kCoeffDenominator[0]), + "denominator coefficients incorrect size"); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROCESSING_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/vad_circular_buffer.cc b/pkg/apm/webrtc/modules/audio_processing/vad/vad_circular_buffer.cc new file mode 100644 index 00000000..31f14d7f --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/vad_circular_buffer.cc @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/vad/vad_circular_buffer.h" + +#include + +namespace webrtc { + +VadCircularBuffer::VadCircularBuffer(int buffer_size) + : buffer_(new double[buffer_size]), + is_full_(false), + index_(0), + buffer_size_(buffer_size), + sum_(0) {} + +VadCircularBuffer::~VadCircularBuffer() {} + +void VadCircularBuffer::Reset() { + is_full_ = false; + index_ = 0; + sum_ = 0; +} + +VadCircularBuffer* VadCircularBuffer::Create(int buffer_size) { + if (buffer_size <= 0) + return NULL; + return new VadCircularBuffer(buffer_size); +} + +double VadCircularBuffer::Oldest() const { + if (!is_full_) + return buffer_[0]; + else + return buffer_[index_]; +} + +double VadCircularBuffer::Mean() { + double m; + if (is_full_) { + m = sum_ / buffer_size_; + } else { + if (index_ > 0) + m = sum_ / index_; + else + m = 0; + } + return m; +} + +void VadCircularBuffer::Insert(double value) { + if (is_full_) { + sum_ -= buffer_[index_]; + } + sum_ += value; + buffer_[index_] = value; + index_++; + if (index_ >= buffer_size_) { + is_full_ = true; + index_ = 0; + } +} +int VadCircularBuffer::BufferLevel() { + if (is_full_) + return buffer_size_; + return index_; +} + +int VadCircularBuffer::Get(int index, double* value) const { + int err = ConvertToLinearIndex(&index); + if (err < 0) + return -1; + *value = buffer_[index]; + return 0; +} + +int VadCircularBuffer::Set(int index, double value) { + int err = ConvertToLinearIndex(&index); + if (err < 0) + return -1; + + sum_ -= buffer_[index]; + buffer_[index] = value; + sum_ += value; + return 0; +} + +int VadCircularBuffer::ConvertToLinearIndex(int* index) const { + if (*index < 0 || *index >= buffer_size_) + return -1; + + if (!is_full_ && *index >= index_) + return -1; + + *index = index_ - 1 - *index; + if (*index < 0) + *index += buffer_size_; + return 0; +} + +int VadCircularBuffer::RemoveTransient(int width_threshold, + double val_threshold) { + if (!is_full_ && index_ < width_threshold + 2) + return 0; + + int index_1 = 0; + int index_2 = width_threshold + 1; + double v = 0; + if (Get(index_1, &v) < 0) + return -1; + if (v < val_threshold) { + Set(index_1, 0); + int index; + for (index = index_2; index > index_1; index--) { + if (Get(index, &v) < 0) + return -1; + if (v < val_threshold) + break; + } + for (; index > index_1; index--) { + if (Set(index, 0.0) < 0) + return -1; + } + } + return 0; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/vad_circular_buffer.h b/pkg/apm/webrtc/modules/audio_processing/vad/vad_circular_buffer.h new file mode 100644 index 00000000..c1806f9e --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/vad_circular_buffer.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_VAD_VAD_CIRCULAR_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_VAD_VAD_CIRCULAR_BUFFER_H_ + +#include + +namespace webrtc { + +// A circular buffer tailored to the need of this project. It stores last +// K samples of the input, and keeps track of the mean of the last samples. +// +// It is used in class "PitchBasedActivity" to keep track of posterior +// probabilities in the past few seconds. The posterior probabilities are used +// to recursively update prior probabilities. +class VadCircularBuffer { + public: + static VadCircularBuffer* Create(int buffer_size); + ~VadCircularBuffer(); + + // If buffer is wrapped around. + bool is_full() const { return is_full_; } + // Get the oldest entry in the buffer. + double Oldest() const; + // Insert new value into the buffer. + void Insert(double value); + // Reset buffer, forget the past, start fresh. + void Reset(); + + // The mean value of the elements in the buffer. The return value is zero if + // buffer is empty, i.e. no value is inserted. + double Mean(); + // Remove transients. If the values exceed `val_threshold` for a period + // shorter then or equal to `width_threshold`, then that period is considered + // transient and set to zero. + int RemoveTransient(int width_threshold, double val_threshold); + + private: + explicit VadCircularBuffer(int buffer_size); + // Get previous values. |index = 0| corresponds to the most recent + // insertion. |index = 1| is the one before the most recent insertion, and + // so on. + int Get(int index, double* value) const; + // Set a given position to `value`. `index` is interpreted as above. + int Set(int index, double value); + // Return the number of valid elements in the buffer. + int BufferLevel(); + + // Convert an index with the interpretation as get() method to the + // corresponding linear index. + int ConvertToLinearIndex(int* index) const; + + std::unique_ptr buffer_; + bool is_full_; + int index_; + int buffer_size_; + double sum_; +}; + +} // namespace webrtc +#endif // MODULES_AUDIO_PROCESSING_VAD_VAD_CIRCULAR_BUFFER_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/voice_activity_detector.cc b/pkg/apm/webrtc/modules/audio_processing/vad/voice_activity_detector.cc new file mode 100644 index 00000000..02023d6a --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/voice_activity_detector.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/vad/voice_activity_detector.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +const size_t kNumChannels = 1; + +const double kDefaultVoiceValue = 1.0; +const double kNeutralProbability = 0.5; +const double kLowProbability = 0.01; + +} // namespace + +VoiceActivityDetector::VoiceActivityDetector() + : last_voice_probability_(kDefaultVoiceValue), + standalone_vad_(StandaloneVad::Create()) {} + +VoiceActivityDetector::~VoiceActivityDetector() = default; + +// Because ISAC has a different chunk length, it updates +// `chunkwise_voice_probabilities_` and `chunkwise_rms_` when there is new data. +// Otherwise it clears them. +void VoiceActivityDetector::ProcessChunk(const int16_t* audio, + size_t length, + int sample_rate_hz) { + RTC_DCHECK_EQ(length, sample_rate_hz / 100); + // TODO(bugs.webrtc.org/7494): Remove resampling and force 16 kHz audio. + // Resample to the required rate. + const int16_t* resampled_ptr = audio; + if (sample_rate_hz != kSampleRateHz) { + RTC_CHECK_EQ( + resampler_.ResetIfNeeded(sample_rate_hz, kSampleRateHz, kNumChannels), + 0); + resampler_.Push(audio, length, resampled_, kLength10Ms, length); + resampled_ptr = resampled_; + } + RTC_DCHECK_EQ(length, kLength10Ms); + + // Each chunk needs to be passed into `standalone_vad_`, because internally it + // buffers the audio and processes it all at once when GetActivity() is + // called. + RTC_CHECK_EQ(standalone_vad_->AddAudio(resampled_ptr, length), 0); + + audio_processing_.ExtractFeatures(resampled_ptr, length, &features_); + + chunkwise_voice_probabilities_.resize(features_.num_frames); + chunkwise_rms_.resize(features_.num_frames); + std::copy(features_.rms, features_.rms + chunkwise_rms_.size(), + chunkwise_rms_.begin()); + if (features_.num_frames > 0) { + if (features_.silence) { + // The other features are invalid, so set the voice probabilities to an + // arbitrary low value. + std::fill(chunkwise_voice_probabilities_.begin(), + chunkwise_voice_probabilities_.end(), kLowProbability); + } else { + std::fill(chunkwise_voice_probabilities_.begin(), + chunkwise_voice_probabilities_.end(), kNeutralProbability); + RTC_CHECK_GE( + standalone_vad_->GetActivity(&chunkwise_voice_probabilities_[0], + chunkwise_voice_probabilities_.size()), + 0); + RTC_CHECK_GE(pitch_based_vad_.VoicingProbability( + features_, &chunkwise_voice_probabilities_[0]), + 0); + } + last_voice_probability_ = chunkwise_voice_probabilities_.back(); + } +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/voice_activity_detector.h b/pkg/apm/webrtc/modules/audio_processing/vad/voice_activity_detector.h new file mode 100644 index 00000000..401acca6 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/voice_activity_detector.h @@ -0,0 +1,74 @@ +/* + * 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 MODULES_AUDIO_PROCESSING_VAD_VOICE_ACTIVITY_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_VAD_VOICE_ACTIVITY_DETECTOR_H_ + +#include +#include + +#include +#include + +#include "common_audio/resampler/include/resampler.h" +#include "modules/audio_processing/vad/common.h" +#include "modules/audio_processing/vad/pitch_based_vad.h" +#include "modules/audio_processing/vad/standalone_vad.h" +#include "modules/audio_processing/vad/vad_audio_proc.h" + +namespace webrtc { + +// A Voice Activity Detector (VAD) that combines the voice probability from the +// StandaloneVad and PitchBasedVad to get a more robust estimation. +class VoiceActivityDetector { + public: + VoiceActivityDetector(); + ~VoiceActivityDetector(); + + // Processes each audio chunk and estimates the voice probability. + // TODO(bugs.webrtc.org/7494): Switch to webrtc::ArrayView and remove + // `sample_rate_hz`. + void ProcessChunk(const int16_t* audio, size_t length, int sample_rate_hz); + + // Returns a vector of voice probabilities for each chunk. It can be empty for + // some chunks, but it catches up afterwards returning multiple values at + // once. + const std::vector& chunkwise_voice_probabilities() const { + return chunkwise_voice_probabilities_; + } + + // Returns a vector of RMS values for each chunk. It has the same length as + // chunkwise_voice_probabilities(). + const std::vector& chunkwise_rms() const { return chunkwise_rms_; } + + // Returns the last voice probability, regardless of the internal + // implementation, although it has a few chunks of delay. + float last_voice_probability() const { return last_voice_probability_; } + + private: + // TODO(aluebs): Change these to float. + std::vector chunkwise_voice_probabilities_; + std::vector chunkwise_rms_; + + float last_voice_probability_; + + Resampler resampler_; + VadAudioProc audio_processing_; + + std::unique_ptr standalone_vad_; + PitchBasedVad pitch_based_vad_; + + int16_t resampled_[kLength10Ms]; + AudioFeatures features_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_VAD_VOICE_ACTIVITY_DETECTOR_H_ diff --git a/pkg/apm/webrtc/modules/audio_processing/vad/voice_gmm_tables.h b/pkg/apm/webrtc/modules/audio_processing/vad/voice_gmm_tables.h new file mode 100644 index 00000000..ef4ad7e2 --- /dev/null +++ b/pkg/apm/webrtc/modules/audio_processing/vad/voice_gmm_tables.h @@ -0,0 +1,77 @@ +/* + * 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. + */ + +// GMM tables for active segments. Generated by MakeGmmTables.m. + +#ifndef MODULES_AUDIO_PROCESSING_VAD_VOICE_GMM_TABLES_H_ +#define MODULES_AUDIO_PROCESSING_VAD_VOICE_GMM_TABLES_H_ + +static const int kVoiceGmmNumMixtures = 12; +static const int kVoiceGmmDim = 3; + +static const double + kVoiceGmmCovarInverse[kVoiceGmmNumMixtures][kVoiceGmmDim][kVoiceGmmDim] = { + {{1.83673825579513e+00, -8.09791637570095e-04, 4.60106414365986e-03}, + {-8.09791637570095e-04, 8.89351738394608e-04, -9.80188953277734e-04}, + {4.60106414365986e-03, -9.80188953277734e-04, 1.38706060206582e-03}}, + {{6.76228912850703e+01, -1.98893120119660e-02, -3.53548357253551e-03}, + {-1.98893120119660e-02, 3.96216858500530e-05, -4.08492938394097e-05}, + {-3.53548357253551e-03, -4.08492938394097e-05, 9.31864352856416e-04}}, + {{9.98612435944558e+00, -5.27880954316893e-03, -6.30342541619017e-03}, + {-5.27880954316893e-03, 4.54359480225226e-05, 6.30804591626044e-05}, + {-6.30342541619017e-03, 6.30804591626044e-05, 5.36466441382942e-04}}, + {{3.39917474216349e+01, -1.56213579433191e-03, -4.01459014990225e-02}, + {-1.56213579433191e-03, 6.40415424897724e-05, 6.20076342427833e-05}, + {-4.01459014990225e-02, 6.20076342427833e-05, 3.51199070103063e-03}}, + {{1.34545062271428e+01, -7.94513610147144e-03, -5.34401019341728e-02}, + {-7.94513610147144e-03, 1.16511820098649e-04, 4.66063702069293e-05}, + {-5.34401019341728e-02, 4.66063702069293e-05, 2.72354323774163e-03}}, + {{1.08557844314806e+02, -1.54885805673668e-02, -1.88029692674851e-02}, + {-1.54885805673668e-02, 1.16404042786406e-04, 6.45579292702802e-06}, + {-1.88029692674851e-02, 6.45579292702802e-06, 4.32330478391416e-04}}, + {{8.22940066541450e+01, -1.15903110231303e-02, -4.92166764865343e-02}, + {-1.15903110231303e-02, 7.42510742165261e-05, 3.73007314191290e-06}, + {-4.92166764865343e-02, 3.73007314191290e-06, 3.64005221593244e-03}}, + {{2.31133605685660e+00, -7.83261568950254e-04, 7.45744012346313e-04}, + {-7.83261568950254e-04, 1.29460648214142e-05, -2.22774455093730e-06}, + {7.45744012346313e-04, -2.22774455093730e-06, 1.05117294093010e-04}}, + {{3.78767849189611e+02, 1.57759761011568e-03, -2.08551217988774e-02}, + {1.57759761011568e-03, 4.76066236886865e-05, -2.33977412299324e-05}, + {-2.08551217988774e-02, -2.33977412299324e-05, 5.24261005371196e-04}}, + {{6.98580096506135e-01, -5.13850255217378e-04, -4.01124551717056e-04}, + {-5.13850255217378e-04, 1.40501021984840e-06, -2.09496928716569e-06}, + {-4.01124551717056e-04, -2.09496928716569e-06, 2.82879357740037e-04}}, + {{2.62770945162399e+00, -2.31825753241430e-03, -5.30447217466318e-03}, + {-2.31825753241430e-03, 4.59108572227649e-05, 7.67631886355405e-05}, + {-5.30447217466318e-03, 7.67631886355405e-05, 2.28521601674098e-03}}, + {{1.89940391362152e+02, -4.23280856852379e-03, -2.70608873541399e-02}, + {-4.23280856852379e-03, 6.77547582742563e-05, 2.69154203800467e-05}, + {-2.70608873541399e-02, 2.69154203800467e-05, 3.88574543373470e-03}}}; + +static const double kVoiceGmmMean[kVoiceGmmNumMixtures][kVoiceGmmDim] = { + {-2.15020241646536e+00, 4.97079062999877e+02, 4.77078119504505e+02}, + {-8.92097680029190e-01, 5.92064964199921e+02, 1.81045145941059e+02}, + {-1.29435784144398e+00, 4.98450293410611e+02, 1.71991263804064e+02}, + {-1.03925228397884e+00, 4.99511274321571e+02, 1.05838336539105e+02}, + {-1.29229047206129e+00, 4.15026762566707e+02, 1.12861119017125e+02}, + {-7.88748114599810e-01, 4.48739336688113e+02, 1.89784216956337e+02}, + {-8.77777402332642e-01, 4.86620285054533e+02, 1.13477708016491e+02}, + {-2.06465957063057e+00, 6.33385049870607e+02, 2.32758546796149e+02}, + {-6.98893789231685e-01, 5.93622051503385e+02, 1.92536982473203e+02}, + {-2.55901217508894e+00, 1.55914919756205e+03, 1.39769980835570e+02}, + {-1.92070024165837e+00, 4.87983940444185e+02, 1.02745468128289e+02}, + {-7.29187507662854e-01, 5.22717685022855e+02, 1.16377942283991e+02}}; + +static const double kVoiceGmmWeights[kVoiceGmmNumMixtures] = { + -1.39789694361035e+01, -1.19527720202104e+01, -1.32396317929055e+01, + -1.09436815209238e+01, -1.13440027478149e+01, -1.12200721834504e+01, + -1.02537324043693e+01, -1.60789861938302e+01, -1.03394494048344e+01, + -1.83207938586818e+01, -1.31186044948288e+01, -9.52479998673554e+00}; +#endif // MODULES_AUDIO_PROCESSING_VAD_VOICE_GMM_TABLES_H_ diff --git a/pkg/apm/webrtc/modules/third_party/fft/fft.h b/pkg/apm/webrtc/modules/third_party/fft/fft.h new file mode 100644 index 00000000..f8f8b6f1 --- /dev/null +++ b/pkg/apm/webrtc/modules/third_party/fft/fft.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the ../../../LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/*--------------------------------*-C-*---------------------------------* + * File: + * fftn.h + * ---------------------------------------------------------------------* + * Re[]: real value array + * Im[]: imaginary value array + * nTotal: total number of complex values + * nPass: number of elements involved in this pass of transform + * nSpan: nspan/nPass = number of bytes to increment pointer + * in Re[] and Im[] + * isign: exponent: +1 = forward -1 = reverse + * scaling: normalizing constant by which the final result is *divided* + * scaling == -1, normalize by total dimension of the transform + * scaling < -1, normalize by the square-root of the total dimension + * + * ---------------------------------------------------------------------- + * See the comments in the code for correct usage! + */ + +#ifndef MODULES_THIRD_PARTY_FFT_FFT_H_ +#define MODULES_THIRD_PARTY_FFT_FFT_H_ + +#define FFT_MAXFFTSIZE 2048 +#define FFT_NFACTOR 11 + +typedef struct { + unsigned int SpaceAlloced; + unsigned int MaxPermAlloced; + double Tmp0[FFT_MAXFFTSIZE]; + double Tmp1[FFT_MAXFFTSIZE]; + double Tmp2[FFT_MAXFFTSIZE]; + double Tmp3[FFT_MAXFFTSIZE]; + int Perm[FFT_MAXFFTSIZE]; + int factor[FFT_NFACTOR]; + +} FFTstr; + +/* double precision routine */ + +int WebRtcIsac_Fftns(unsigned int ndim, + const int dims[], + double Re[], + double Im[], + int isign, + double scaling, + FFTstr* fftstate); + +#endif /* MODULES_THIRD_PARTY_FFT_FFT_H_ */ diff --git a/pkg/apm/webrtc/rtc_base/arraysize.h b/pkg/apm/webrtc/rtc_base/arraysize.h new file mode 100644 index 00000000..bf8e6d88 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/arraysize.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_ARRAYSIZE_H_ +#define RTC_BASE_ARRAYSIZE_H_ + +#include + +// This file defines the arraysize() macro and is derived from Chromium's +// base/macros.h. + +// The arraysize(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. If you use arraysize on +// a pointer by mistake, you will get a compile-time error. + +// This template function declaration is used in defining arraysize. +// Note that the function doesn't need an implementation, as we only +// use its type. +template +char (&ArraySizeHelper(T (&array)[N]))[N]; + +#define arraysize(array) (sizeof(ArraySizeHelper(array))) + +#endif // RTC_BASE_ARRAYSIZE_H_ diff --git a/pkg/apm/webrtc/rtc_base/async_dns_resolver.h b/pkg/apm/webrtc/rtc_base/async_dns_resolver.h new file mode 100644 index 00000000..674e1408 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/async_dns_resolver.h @@ -0,0 +1,63 @@ +/* + * 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 RTC_BASE_ASYNC_DNS_RESOLVER_H_ +#define RTC_BASE_ASYNC_DNS_RESOLVER_H_ + +#include + +#include "api/async_dns_resolver.h" +#include "api/sequence_checker.h" +#include "api/task_queue/pending_task_safety_flag.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { +// This file contains a default implementation of +// webrtc::AsyncDnsResolverInterface, for use when there is no need for special +// treatment. + +class AsyncDnsResolverResultImpl : public AsyncDnsResolverResult { + public: + bool GetResolvedAddress(int family, SocketAddress* addr) const override; + // Returns error from resolver. + int GetError() const override; + + private: + friend class AsyncDnsResolver; + RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_; + SocketAddress addr_ RTC_GUARDED_BY(sequence_checker_); + std::vector addresses_ RTC_GUARDED_BY(sequence_checker_); + int error_ RTC_GUARDED_BY(sequence_checker_); +}; + +class RTC_EXPORT AsyncDnsResolver : public AsyncDnsResolverInterface { + public: + AsyncDnsResolver(); + ~AsyncDnsResolver(); + // Start address resolution of the hostname in `addr`. + void Start(const SocketAddress& addr, + absl::AnyInvocable callback) override; + // Start address resolution of the hostname in `addr` matching `family`. + void Start(const SocketAddress& addr, + int family, + absl::AnyInvocable callback) override; + const AsyncDnsResolverResult& result() const override; + + private: + class State; + ScopedTaskSafety safety_; // To check for client going away + scoped_refptr state_; // To check for "this" going away + AsyncDnsResolverResultImpl result_; + absl::AnyInvocable callback_; +}; + +} // namespace webrtc +#endif // RTC_BASE_ASYNC_DNS_RESOLVER_H_ diff --git a/pkg/apm/webrtc/rtc_base/async_packet_socket.h b/pkg/apm/webrtc/rtc_base/async_packet_socket.h new file mode 100644 index 00000000..e06de43d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/async_packet_socket.h @@ -0,0 +1,217 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_ASYNC_PACKET_SOCKET_H_ +#define RTC_BASE_ASYNC_PACKET_SOCKET_H_ + +#include +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "api/sequence_checker.h" +#include "rtc_base/callback_list.h" +#include "rtc_base/checks.h" +#include "rtc_base/dscp.h" +#include "rtc_base/network/received_packet.h" +#include "rtc_base/network/sent_packet.h" +#include "rtc_base/socket.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +// This structure holds the info needed to update the packet send time header +// extension, including the information needed to update the authentication tag +// after changing the value. +struct PacketTimeUpdateParams { + PacketTimeUpdateParams(); + PacketTimeUpdateParams(const PacketTimeUpdateParams& other); + ~PacketTimeUpdateParams(); + + int rtp_sendtime_extension_id = -1; // extension header id present in packet. + std::vector srtp_auth_key; // Authentication key. + int srtp_auth_tag_len = -1; // Authentication tag length. + int64_t srtp_packet_index = -1; // Required for Rtp Packet authentication. +}; + +// This structure holds meta information for the packet which is about to send +// over network. +struct RTC_EXPORT AsyncSocketPacketOptions { + AsyncSocketPacketOptions(); + explicit AsyncSocketPacketOptions(DiffServCodePoint dscp); + AsyncSocketPacketOptions(const AsyncSocketPacketOptions& other); + ~AsyncSocketPacketOptions(); + + DiffServCodePoint dscp = DSCP_NO_CHANGE; + + // Packet will be sent with ECN(1), RFC-3168, Section 5. + // Intended to be used with L4S + // https://www.rfc-editor.org/rfc/rfc9331.html + bool ecn_1 = false; + + // When used with RTP packets (for example, webrtc::PacketOptions), the value + // should be 16 bits. A value of -1 represents "not set". + int64_t packet_id = -1; + webrtc::PacketTimeUpdateParams packet_time_params; + // PacketInfo is passed to SentPacket when signaling this packet is sent. + PacketInfo info_signaled_after_sent; + // True if this is a batchable packet. Batchable packets are collected at low + // levels and sent first when their AsyncPacketSocket receives a + // OnSendBatchComplete call. + bool batchable = false; + // True if this is the last packet of a batch. + bool last_packet_in_batch = false; +}; + +// Provides the ability to receive packets asynchronously. Sends are not +// buffered since it is acceptable to drop packets under high load. +class RTC_EXPORT AsyncPacketSocket : public sigslot::has_slots<> { + public: + enum State { + STATE_CLOSED, + STATE_BINDING, + STATE_BOUND, + STATE_CONNECTING, + STATE_CONNECTED + }; + + AsyncPacketSocket() = default; + ~AsyncPacketSocket() override; + + AsyncPacketSocket(const AsyncPacketSocket&) = delete; + AsyncPacketSocket& operator=(const AsyncPacketSocket&) = delete; + + // Returns current local address. Address may be set to null if the + // socket is not bound yet (GetState() returns STATE_BINDING). + virtual SocketAddress GetLocalAddress() const = 0; + + // Returns remote address. Returns zeroes if this is not a client TCP socket. + virtual SocketAddress GetRemoteAddress() const = 0; + + // Send a packet. + virtual int Send(const void* pv, + size_t cb, + const AsyncSocketPacketOptions& options) = 0; + virtual int SendTo(const void* pv, + size_t cb, + const SocketAddress& addr, + const AsyncSocketPacketOptions& options) = 0; + + // Close the socket. + virtual int Close() = 0; + + // Returns current state of the socket. + virtual State GetState() const = 0; + + // Get/set options. + virtual int GetOption(Socket::Option opt, int* value) = 0; + virtual int SetOption(Socket::Option opt, int value) = 0; + + // Get/Set current error. + // TODO: Remove SetError(). + virtual int GetError() const = 0; + virtual void SetError(int error) = 0; + + // Register a callback to be called when the socket is closed. + void SubscribeCloseEvent( + const void* removal_tag, + std::function callback); + void UnsubscribeCloseEvent(const void* removal_tag); + + void RegisterReceivedPacketCallback( + absl::AnyInvocable + received_packet_callback); + void DeregisterReceivedPacketCallback(); + + // Emitted each time a packet is sent. + sigslot::signal2 SignalSentPacket; + + // Emitted when the socket is currently able to send. + sigslot::signal1 SignalReadyToSend; + + // Emitted after address for the socket is allocated, i.e. binding + // is finished. State of the socket is changed from BINDING to BOUND + // (for UDP sockets). + sigslot::signal2 SignalAddressReady; + + // Emitted for client TCP sockets when state is changed from + // CONNECTING to CONNECTED. + sigslot::signal1 SignalConnect; + + void NotifyClosedForTest(int err) { NotifyClosed(err); } + + protected: + // TODO(bugs.webrtc.org/11943): Remove after updating downstream code. + void SignalClose(AsyncPacketSocket* s, int err) { + RTC_DCHECK_EQ(s, this); + NotifyClosed(err); + } + + void NotifyClosed(int err) { + RTC_DCHECK_RUN_ON(&network_checker_); + on_close_.Send(this, err); + } + + void NotifyPacketReceived(const ReceivedIpPacket& packet); + + RTC_NO_UNIQUE_ADDRESS SequenceChecker network_checker_{ + SequenceChecker::kDetached}; + + private: + CallbackList on_close_ + RTC_GUARDED_BY(&network_checker_); + absl::AnyInvocable + received_packet_callback_ RTC_GUARDED_BY(&network_checker_); +}; + +// Listen socket, producing an AsyncPacketSocket when a peer connects. +class RTC_EXPORT AsyncListenSocket : public sigslot::has_slots<> { + public: + enum class State { + kClosed, + kBound, + }; + + // Returns current state of the socket. + virtual State GetState() const = 0; + + // Returns current local address. Address may be set to null if the + // socket is not bound yet (GetState() returns kBinding). + virtual SocketAddress GetLocalAddress() const = 0; + + sigslot::signal2 SignalNewConnection; +}; + +void CopySocketInformationToPacketInfo(size_t packet_size_bytes, + const AsyncPacketSocket& socket_from, + PacketInfo* info); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::AsyncListenSocket; +using ::webrtc::AsyncPacketSocket; +using ::webrtc::CopySocketInformationToPacketInfo; +using ::webrtc::PacketTimeUpdateParams; +using PacketOptions = ::webrtc::AsyncSocketPacketOptions; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_ASYNC_PACKET_SOCKET_H_ diff --git a/pkg/apm/webrtc/rtc_base/async_socket.h b/pkg/apm/webrtc/rtc_base/async_socket.h new file mode 100644 index 00000000..efd0ced7 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/async_socket.h @@ -0,0 +1,73 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_ASYNC_SOCKET_H_ +#define RTC_BASE_ASYNC_SOCKET_H_ + +#include +#include + +#include + +#include "rtc_base/socket.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/third_party/sigslot/sigslot.h" + +namespace webrtc { + +class AsyncSocketAdapter : public Socket, public sigslot::has_slots<> { + public: + // Takes ownership of the passed in socket. + // TODO(bugs.webrtc.org/6424): Change to unique_ptr here and in callers. + explicit AsyncSocketAdapter(Socket* socket); + + SocketAddress GetLocalAddress() const override; + SocketAddress GetRemoteAddress() const override; + int Bind(const SocketAddress& addr) override; + int Connect(const SocketAddress& addr) override; + int Send(const void* pv, size_t cb) override; + int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override; + int Recv(void* pv, size_t cb, int64_t* timestamp) override; + int RecvFrom(void* pv, + size_t cb, + SocketAddress* paddr, + int64_t* timestamp) override; + int Listen(int backlog) override; + Socket* Accept(SocketAddress* paddr) override; + int Close() override; + int GetError() const override; + void SetError(int error) override; + ConnState GetState() const override; + int GetOption(Option opt, int* value) override; + int SetOption(Option opt, int value) override; + + protected: + virtual void OnConnectEvent(Socket* socket); + virtual void OnReadEvent(Socket* socket); + virtual void OnWriteEvent(Socket* socket); + virtual void OnCloseEvent(Socket* socket, int err); + + Socket* GetSocket() const { return socket_.get(); } + + private: + const std::unique_ptr socket_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::AsyncSocketAdapter; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_ASYNC_SOCKET_H_ diff --git a/pkg/apm/webrtc/rtc_base/async_tcp_socket.h b/pkg/apm/webrtc/rtc_base/async_tcp_socket.h new file mode 100644 index 00000000..daf890cd --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/async_tcp_socket.h @@ -0,0 +1,136 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_ASYNC_TCP_SOCKET_H_ +#define RTC_BASE_ASYNC_TCP_SOCKET_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "rtc_base/async_packet_socket.h" +#include "rtc_base/buffer.h" +#include "rtc_base/socket.h" +#include "rtc_base/socket_address.h" + +namespace webrtc { + +// Simulates UDP semantics over TCP. Send and Recv packet sizes +// are preserved, and drops packets silently on Send, rather than +// buffer them in user space. +class AsyncTCPSocketBase : public AsyncPacketSocket { + public: + AsyncTCPSocketBase(Socket* socket, size_t max_packet_size); + ~AsyncTCPSocketBase() override; + + AsyncTCPSocketBase(const AsyncTCPSocketBase&) = delete; + AsyncTCPSocketBase& operator=(const AsyncTCPSocketBase&) = delete; + + // Pure virtual methods to send and recv data. + int Send(const void* pv, + size_t cb, + const AsyncSocketPacketOptions& options) override = 0; + // Must return the number of bytes processed. + virtual size_t ProcessInput(ArrayView data) = 0; + + SocketAddress GetLocalAddress() const override; + SocketAddress GetRemoteAddress() const override; + int SendTo(const void* pv, + size_t cb, + const SocketAddress& addr, + const AsyncSocketPacketOptions& options) override; + int Close() override; + + State GetState() const override; + int GetOption(Socket::Option opt, int* value) override; + int SetOption(Socket::Option opt, int value) override; + int GetError() const override; + void SetError(int error) override; + + protected: + // Binds and connects `socket` and creates AsyncTCPSocket for + // it. Takes ownership of `socket`. Returns null if bind() or + // connect() fail (`socket` is destroyed in that case). + static Socket* ConnectSocket(Socket* socket, + const SocketAddress& bind_address, + const SocketAddress& remote_address); + int FlushOutBuffer(); + // Add data to `outbuf_`. + void AppendToOutBuffer(const void* pv, size_t cb); + + // Helper methods for `outpos_`. + bool IsOutBufferEmpty() const { return outbuf_.size() == 0; } + void ClearOutBuffer() { outbuf_.Clear(); } + + private: + // Called by the underlying socket + void OnConnectEvent(Socket* socket); + void OnReadEvent(Socket* socket); + void OnWriteEvent(Socket* socket); + void OnCloseEvent(Socket* socket, int error); + + std::unique_ptr socket_; + Buffer inbuf_; + Buffer outbuf_; + size_t max_insize_; + size_t max_outsize_; +}; + +class AsyncTCPSocket : public AsyncTCPSocketBase { + public: + // Binds and connects `socket` and creates AsyncTCPSocket for + // it. Takes ownership of `socket`. Returns null if bind() or + // connect() fail (`socket` is destroyed in that case). + static AsyncTCPSocket* Create(Socket* socket, + const SocketAddress& bind_address, + const SocketAddress& remote_address); + explicit AsyncTCPSocket(Socket* socket); + ~AsyncTCPSocket() override {} + + AsyncTCPSocket(const AsyncTCPSocket&) = delete; + AsyncTCPSocket& operator=(const AsyncTCPSocket&) = delete; + + int Send(const void* pv, + size_t cb, + const AsyncSocketPacketOptions& options) override; + size_t ProcessInput(ArrayView) override; +}; + +class AsyncTcpListenSocket : public AsyncListenSocket { + public: + explicit AsyncTcpListenSocket(std::unique_ptr socket); + + State GetState() const override; + SocketAddress GetLocalAddress() const override; + + virtual void HandleIncomingConnection(Socket* socket); + + private: + // Called by the underlying socket + void OnReadEvent(Socket* socket); + + std::unique_ptr socket_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::AsyncTcpListenSocket; +using ::webrtc::AsyncTCPSocket; +using ::webrtc::AsyncTCPSocketBase; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_ASYNC_TCP_SOCKET_H_ diff --git a/pkg/apm/webrtc/rtc_base/async_udp_socket.h b/pkg/apm/webrtc/rtc_base/async_udp_socket.h new file mode 100644 index 00000000..8f30ce7f --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/async_udp_socket.h @@ -0,0 +1,88 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_ASYNC_UDP_SOCKET_H_ +#define RTC_BASE_ASYNC_UDP_SOCKET_H_ + +#include + +#include +#include + +#include "api/sequence_checker.h" +#include "api/units/time_delta.h" +#include "rtc_base/async_packet_socket.h" +#include "rtc_base/buffer.h" +#include "rtc_base/socket.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/socket_factory.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +// Provides the ability to receive packets asynchronously. Sends are not +// buffered since it is acceptable to drop packets under high load. +class AsyncUDPSocket : public AsyncPacketSocket { + public: + // Binds `socket` and creates AsyncUDPSocket for it. Takes ownership + // of `socket`. Returns null if bind() fails (`socket` is destroyed + // in that case). + static AsyncUDPSocket* Create(Socket* socket, + const SocketAddress& bind_address); + // Creates a new socket for sending asynchronous UDP packets using an + // asynchronous socket from the given factory. + static AsyncUDPSocket* Create(SocketFactory* factory, + const SocketAddress& bind_address); + explicit AsyncUDPSocket(Socket* socket); + ~AsyncUDPSocket() = default; + + SocketAddress GetLocalAddress() const override; + SocketAddress GetRemoteAddress() const override; + int Send(const void* pv, + size_t cb, + const AsyncSocketPacketOptions& options) override; + int SendTo(const void* pv, + size_t cb, + const SocketAddress& addr, + const AsyncSocketPacketOptions& options) override; + int Close() override; + + State GetState() const override; + int GetOption(Socket::Option opt, int* value) override; + int SetOption(Socket::Option opt, int value) override; + int GetError() const override; + void SetError(int error) override; + + private: + // Called when the underlying socket is ready to be read from. + void OnReadEvent(Socket* socket); + // Called when the underlying socket is ready to send. + void OnWriteEvent(Socket* socket); + + RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_; + std::unique_ptr socket_; + bool has_set_ect1_options_ = false; + Buffer buffer_ RTC_GUARDED_BY(sequence_checker_); + std::optional socket_time_offset_ + RTC_GUARDED_BY(sequence_checker_); +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::AsyncUDPSocket; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_ASYNC_UDP_SOCKET_H_ diff --git a/pkg/apm/webrtc/rtc_base/base64.h b/pkg/apm/webrtc/rtc_base/base64.h new file mode 100644 index 00000000..24c1d97e --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/base64.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BASE64_H_ +#define RTC_BASE_BASE64_H_ + +#include +#include +#include + +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" + +namespace webrtc { + +inline std::string Base64Encode(ArrayView data) { + return absl::Base64Escape(absl::string_view( + reinterpret_cast(data.data()), data.size())); +} + +inline std::string Base64Encode(absl::string_view data) { + return absl::Base64Escape(data); +} + +enum class Base64DecodeOptions { + kStrict, + // Matches https://infra.spec.whatwg.org/#forgiving-base64-decode. + kForgiving, +}; + +// Returns the decoded data if successful, or std::nullopt if the decoding +// failed. +std::optional Base64Decode( + absl::string_view data, + Base64DecodeOptions options = Base64DecodeOptions::kStrict); + +} // namespace webrtc + +#endif // RTC_BASE_BASE64_H_ diff --git a/pkg/apm/webrtc/rtc_base/bit_buffer.h b/pkg/apm/webrtc/rtc_base/bit_buffer.h new file mode 100644 index 00000000..17084a40 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/bit_buffer.h @@ -0,0 +1,107 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BIT_BUFFER_H_ +#define RTC_BASE_BIT_BUFFER_H_ + +#include // For size_t. +#include // For integer types. + +#include "absl/strings/string_view.h" +#include "api/units/data_size.h" + +namespace webrtc { + +// A BitBuffer API for write operations. Supports symmetric write APIs to the +// reading APIs of BitstreamReader. +// Sizes/counts specify bits/bytes, for clarity. +// Byte order is assumed big-endian/network. +class BitBufferWriter { + public: + static constexpr DataSize kMaxLeb128Length = webrtc::DataSize::Bytes(10); + + // Constructs a bit buffer for the writable buffer of `bytes`. + BitBufferWriter(uint8_t* bytes, size_t byte_count); + + BitBufferWriter(const BitBufferWriter&) = delete; + BitBufferWriter& operator=(const BitBufferWriter&) = delete; + + // Gets the current offset, in bytes/bits, from the start of the buffer. The + // bit offset is the offset into the current byte, in the range [0,7]. + void GetCurrentOffset(size_t* out_byte_offset, size_t* out_bit_offset); + + // The remaining bits in the byte buffer. + uint64_t RemainingBitCount() const; + + // Moves current position `byte_count` bytes forward. Returns false if + // there aren't enough bytes left in the buffer. + bool ConsumeBytes(size_t byte_count); + // Moves current position `bit_count` bits forward. Returns false if + // there aren't enough bits left in the buffer. + bool ConsumeBits(size_t bit_count); + + // Sets the current offset to the provied byte/bit offsets. The bit + // offset is from the given byte, in the range [0,7]. + bool Seek(size_t byte_offset, size_t bit_offset); + + // Writes byte-sized values from the buffer. Returns false if there isn't + // enough data left for the specified type. + bool WriteUInt8(uint8_t val); + bool WriteUInt16(uint16_t val); + bool WriteUInt32(uint32_t val); + + // Writes bit-sized values to the buffer. Returns false if there isn't enough + // room left for the specified number of bits. + bool WriteBits(uint64_t val, size_t bit_count); + + // Writes value in range [0, num_values - 1] + // See ReadNonSymmetric documentation for the format, + // Call SizeNonSymmetricBits to get number of bits needed to store the value. + // Returns false if there isn't enough room left for the value. + bool WriteNonSymmetric(uint32_t val, uint32_t num_values); + // Returns number of bits required to store `val` with NonSymmetric encoding. + static size_t SizeNonSymmetricBits(uint32_t val, uint32_t num_values); + + // Writes the exponential golomb encoded version of the supplied value. + // Returns false if there isn't enough room left for the value. + bool WriteExponentialGolomb(uint32_t val); + // Writes the signed exponential golomb version of the supplied value. + // Signed exponential golomb values are just the unsigned values mapped to the + // sequence 0, 1, -1, 2, -2, etc. in order. + bool WriteSignedExponentialGolomb(int32_t val); + + // Writes the Leb128 encoded value. + bool WriteLeb128(uint64_t val); + + // Writes the string as bytes of data. + bool WriteString(absl::string_view data); + + private: + // The buffer, as a writable array. + uint8_t* const writable_bytes_; + // The total size of `bytes_`. + const size_t byte_count_; + // The current offset, in bytes, from the start of `bytes_`. + size_t byte_offset_; + // The current offset, in bits, into the current byte. + size_t bit_offset_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::BitBufferWriter; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_BIT_BUFFER_H_ diff --git a/pkg/apm/webrtc/rtc_base/bitrate_tracker.h b/pkg/apm/webrtc/rtc_base/bitrate_tracker.h new file mode 100644 index 00000000..9570cd66 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/bitrate_tracker.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BITRATE_TRACKER_H_ +#define RTC_BASE_BITRATE_TRACKER_H_ + +#include +#include + +#include + +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "rtc_base/rate_statistics.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { +// Class to estimate bitrates over running window. +// Timestamps used in Update(), Rate() and SetWindowSize() must never +// decrease for two consecutive calls. +// This class is thread unsafe. +class RTC_EXPORT BitrateTracker { + public: + // max_window_sizes = Maximum window size for the rate estimation. + // Initial window size is set to this, but may be changed + // to something lower by calling SetWindowSize(). + explicit BitrateTracker(TimeDelta max_window_size); + + BitrateTracker(const BitrateTracker&) = default; + BitrateTracker(BitrateTracker&&) = default; + BitrateTracker& operator=(const BitrateTracker&) = delete; + BitrateTracker& operator=(BitrateTracker&&) = delete; + + ~BitrateTracker() = default; + + // Resets instance to original state. + void Reset() { impl_.Reset(); } + + // Updates bitrate with a new data point, moving averaging window as needed. + void Update(int64_t bytes, Timestamp now); + void Update(DataSize size, Timestamp now) { Update(size.bytes(), now); } + + // Returns bitrate, moving averaging window as needed. + // Returns nullopt when bitrate can't be measured. + std::optional Rate(Timestamp now) const; + + // Update the size of the averaging window. The maximum allowed value for + // `window_size` is `max_window_size` as supplied in the constructor. + bool SetWindowSize(TimeDelta window_size, Timestamp now); + + private: + RateStatistics impl_; +}; +} // namespace webrtc + +#endif // RTC_BASE_BITRATE_TRACKER_H_ diff --git a/pkg/apm/webrtc/rtc_base/bitstream_reader.h b/pkg/apm/webrtc/rtc_base/bitstream_reader.h new file mode 100644 index 00000000..43d7a4c8 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/bitstream_reader.h @@ -0,0 +1,152 @@ +/* + * Copyright 2021 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BITSTREAM_READER_H_ +#define RTC_BASE_BITSTREAM_READER_H_ + +#include + +#include "absl/base/attributes.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { + +// A class to parse sequence of bits. Byte order is assumed big-endian/network. +// This class is optimized for successful parsing and binary size. +// Individual calls to `Read` and `ConsumeBits` never fail. Instead they may +// change the class state into 'failure state'. User of this class should verify +// parsing by checking if class is in that 'failure state' by calling `Ok`. +// That verification can be done once after multiple reads. +class BitstreamReader { + public: + explicit BitstreamReader( + ArrayView bytes ABSL_ATTRIBUTE_LIFETIME_BOUND); + explicit BitstreamReader( + absl::string_view bytes ABSL_ATTRIBUTE_LIFETIME_BOUND); + BitstreamReader(const BitstreamReader&) = default; + BitstreamReader& operator=(const BitstreamReader&) = default; + ~BitstreamReader(); + + // Return number of unread bits in the buffer, or negative number if there + // was a reading error. + int RemainingBitCount() const; + + // Returns `true` iff all calls to `Read` and `ConsumeBits` were successful. + bool Ok() const { return RemainingBitCount() >= 0; } + + // Sets `BitstreamReader` into the failure state. + void Invalidate() { remaining_bits_ = -1; } + + // Moves current read position forward. `bits` must be non-negative. + void ConsumeBits(int bits); + + // Reads single bit. Returns 0 or 1. + ABSL_MUST_USE_RESULT int ReadBit(); + + // Reads `bits` from the bitstream. `bits` must be in range [0, 64]. + // Returns an unsigned integer in range [0, 2^bits - 1]. + // On failure sets `BitstreamReader` into the failure state and returns 0. + ABSL_MUST_USE_RESULT uint64_t ReadBits(int bits); + + // Reads unsigned integer of fixed width. + template ::value && + !std::is_same::value && + sizeof(T) <= 8>::type* = nullptr> + ABSL_MUST_USE_RESULT T Read() { + return dchecked_cast(ReadBits(sizeof(T) * 8)); + } + + // Reads single bit as boolean. + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + ABSL_MUST_USE_RESULT bool Read() { + return ReadBit() != 0; + } + + // Reads value in range [0, `num_values` - 1]. + // This encoding is similar to ReadBits(val, Ceil(Log2(num_values)), + // but reduces wastage incurred when encoding non-power of two value ranges + // Non symmetric values are encoded as: + // 1) n = bit_width(num_values) + // 2) k = (1 << n) - num_values + // Value v in range [0, k - 1] is encoded in (n-1) bits. + // Value v in range [k, num_values - 1] is encoded as (v+k) in n bits. + // https://aomediacodec.github.io/av1-spec/#nsn + uint32_t ReadNonSymmetric(uint32_t num_values); + + // Reads exponential golomb encoded value. + // On failure sets `BitstreamReader` into the failure state and returns + // unspecified value. + // Exponential golomb values are encoded as: + // 1) x = source val + 1 + // 2) In binary, write [bit_width(x) - 1] 0s, then x + // To decode, we count the number of leading 0 bits, read that many + 1 bits, + // and increment the result by 1. + // Fails the parsing if the value wouldn't fit in a uint32_t. + uint32_t ReadExponentialGolomb(); + + // Reads signed exponential golomb values at the current offset. Signed + // exponential golomb values are just the unsigned values mapped to the + // sequence 0, 1, -1, 2, -2, etc. in order. + // On failure sets `BitstreamReader` into the failure state and returns + // unspecified value. + int ReadSignedExponentialGolomb(); + + // Reads a LEB128 encoded value. The value will be considered invalid if it + // can't fit into a uint64_t. + uint64_t ReadLeb128(); + + std::string ReadString(int num_bytes); + + private: + void set_last_read_is_verified(bool value) const; + + // Next byte with at least one unread bit. + const uint8_t* bytes_; + + // Number of bits remained to read. + int remaining_bits_; + + // Unused in release mode. + mutable bool last_read_is_verified_ = true; +}; + +inline BitstreamReader::BitstreamReader(ArrayView bytes) + : bytes_(bytes.data()), + remaining_bits_(checked_cast(bytes.size() * 8)) {} + +inline BitstreamReader::BitstreamReader(absl::string_view bytes) + : bytes_(reinterpret_cast(bytes.data())), + remaining_bits_(checked_cast(bytes.size() * 8)) {} + +inline BitstreamReader::~BitstreamReader() { + RTC_DCHECK(last_read_is_verified_) << "Latest calls to Read or ConsumeBit " + "were not checked with Ok function."; +} + +inline void BitstreamReader::set_last_read_is_verified(bool value) const { +#ifdef RTC_DCHECK_IS_ON + last_read_is_verified_ = value; +#endif +} + +inline int BitstreamReader::RemainingBitCount() const { + set_last_read_is_verified(true); + return remaining_bits_; +} + +} // namespace webrtc + +#endif // RTC_BASE_BITSTREAM_READER_H_ diff --git a/pkg/apm/webrtc/rtc_base/boringssl_certificate.h b/pkg/apm/webrtc/rtc_base/boringssl_certificate.h new file mode 100644 index 00000000..5c62c633 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/boringssl_certificate.h @@ -0,0 +1,87 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BORINGSSL_CERTIFICATE_H_ +#define RTC_BASE_BORINGSSL_CERTIFICATE_H_ + +#include +#include +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/buffer.h" +#include "rtc_base/openssl.h" +#include "rtc_base/openssl_key_pair.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_identity.h" + +namespace webrtc { + +// BoringSSLCertificate encapsulates a BoringSSL CRYPTO_BUFFER object holding a +// certificate, which is also reference counted inside the BoringSSL library. +// This offers binary size and memory improvements over the OpenSSL X509 +// object. +class BoringSSLCertificate final : public SSLCertificate { + public: + explicit BoringSSLCertificate(bssl::UniquePtr cert_buffer); + BoringSSLCertificate(bssl::UniquePtr cert_buffer, SSL* ssl); + + static std::unique_ptr Generate( + OpenSSLKeyPair* key_pair, + const SSLIdentityParams& params); + static std::unique_ptr FromPEMString( + absl::string_view pem_string); + + ~BoringSSLCertificate() override; + + BoringSSLCertificate(const BoringSSLCertificate&) = delete; + BoringSSLCertificate& operator=(const BoringSSLCertificate&) = delete; + + std::unique_ptr Clone() const override; + + CRYPTO_BUFFER* cert_buffer() const { return cert_buffer_.get(); } + + std::string ToPEMString() const override; + void ToDER(Buffer* der_buffer) const override; + bool operator==(const BoringSSLCertificate& other) const; + bool operator!=(const BoringSSLCertificate& other) const; + + // Compute the digest of the certificate given `algorithm`. + bool ComputeDigest(absl::string_view algorithm, + Buffer& digest) const override; + + bool GetSignatureDigestAlgorithm(std::string* algorithm) const override; + + int64_t CertificateExpirationTime() const override; + + private: + // A handle to the DER encoded certificate data. + bssl::UniquePtr cert_buffer_; + + private: + SSL* ssl_ = nullptr; + public: + SSL* ssl() const { return ssl_; } +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::BoringSSLCertificate; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_BORINGSSL_CERTIFICATE_H_ diff --git a/pkg/apm/webrtc/rtc_base/boringssl_identity.h b/pkg/apm/webrtc/rtc_base/boringssl_identity.h new file mode 100644 index 00000000..1c6f5ab3 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/boringssl_identity.h @@ -0,0 +1,85 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BORINGSSL_IDENTITY_H_ +#define RTC_BASE_BORINGSSL_IDENTITY_H_ + +#include + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/boringssl_certificate.h" +#include "rtc_base/openssl_key_pair.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_identity.h" + +namespace webrtc { + +// Holds a keypair and certificate together, and a method to generate them +// consistently. Uses CRYPTO_BUFFER instead of X509, which offers binary size +// and memory improvements. +class BoringSSLIdentity final : public SSLIdentity { + public: + static std::unique_ptr CreateWithExpiration( + absl::string_view common_name, + const KeyParams& key_params, + time_t certificate_lifetime); + static std::unique_ptr CreateForTest( + const SSLIdentityParams& params); + static std::unique_ptr CreateFromPEMStrings( + absl::string_view private_key, + absl::string_view certificate); + static std::unique_ptr CreateFromPEMChainStrings( + absl::string_view private_key, + absl::string_view certificate_chain); + ~BoringSSLIdentity() override; + + BoringSSLIdentity(const BoringSSLIdentity&) = delete; + BoringSSLIdentity& operator=(const BoringSSLIdentity&) = delete; + + const BoringSSLCertificate& certificate() const override; + const SSLCertChain& cert_chain() const override; + + // Configure an SSL context object to use our key and certificate. + bool ConfigureIdentity(SSL_CTX* ctx); + + std::string PrivateKeyToPEMString() const override; + std::string PublicKeyToPEMString() const override; + bool operator==(const BoringSSLIdentity& other) const; + bool operator!=(const BoringSSLIdentity& other) const; + + private: + BoringSSLIdentity(std::unique_ptr key_pair, + std::unique_ptr certificate); + BoringSSLIdentity(std::unique_ptr key_pair, + std::unique_ptr cert_chain); + std::unique_ptr CloneInternal() const override; + + static std::unique_ptr CreateInternal( + const SSLIdentityParams& params); + + std::unique_ptr key_pair_; + std::unique_ptr cert_chain_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::BoringSSLIdentity; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_BORINGSSL_IDENTITY_H_ diff --git a/pkg/apm/webrtc/rtc_base/bounded_inline_vector.h b/pkg/apm/webrtc/rtc_base/bounded_inline_vector.h new file mode 100644 index 00000000..f8b7eb3d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/bounded_inline_vector.h @@ -0,0 +1,155 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BOUNDED_INLINE_VECTOR_H_ +#define RTC_BASE_BOUNDED_INLINE_VECTOR_H_ + +#include + +#include +#include +#include + +#include "rtc_base/bounded_inline_vector_impl.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// A small std::vector-like type whose capacity is a compile-time constant. It +// stores all data inline and never heap allocates (beyond what its element type +// requires). Trying to grow it beyond its constant capacity is an error. +// +// TODO(bugs.webrtc.org/11391): Comparison operators. +// TODO(bugs.webrtc.org/11391): Methods for adding and deleting elements. +template +class BoundedInlineVector { + static_assert(!std::is_const::value, "T may not be const"); + static_assert(fixed_capacity > 0, "Capacity must be strictly positive"); + + public: + using size_type = int; + using value_type = T; + using const_iterator = const T*; + + BoundedInlineVector() = default; + BoundedInlineVector(const BoundedInlineVector&) = default; + BoundedInlineVector(BoundedInlineVector&&) = default; + BoundedInlineVector& operator=(const BoundedInlineVector&) = default; + BoundedInlineVector& operator=(BoundedInlineVector&&) = default; + ~BoundedInlineVector() = default; + + // This constructor is implicit, to make it possible to write e.g. + // + // BoundedInlineVector x = {2.72, 3.14}; + // + // and + // + // BoundedInlineVector GetConstants() { + // return {2.72, 3.14}; + // } + template ::value>* = + nullptr> + BoundedInlineVector(Ts&&... elements) // NOLINT(runtime/explicit) + : storage_(std::forward(elements)...) { + static_assert(sizeof...(Ts) <= fixed_capacity, ""); + } + + template < + int other_capacity, + typename std::enable_if_t* = nullptr> + BoundedInlineVector(const BoundedInlineVector& other) { + RTC_DCHECK_LE(other.size(), fixed_capacity); + bounded_inline_vector_impl::CopyElements(other.data(), other.size(), + storage_.data, &storage_.size); + } + + template < + int other_capacity, + typename std::enable_if_t* = nullptr> + BoundedInlineVector(BoundedInlineVector&& other) { + RTC_DCHECK_LE(other.size(), fixed_capacity); + bounded_inline_vector_impl::MoveElements(other.data(), other.size(), + storage_.data, &storage_.size); + } + + template < + int other_capacity, + typename std::enable_if_t* = nullptr> + BoundedInlineVector& operator=( + const BoundedInlineVector& other) { + bounded_inline_vector_impl::DestroyElements(storage_.data, storage_.size); + RTC_DCHECK_LE(other.size(), fixed_capacity); + bounded_inline_vector_impl::CopyElements(other.data(), other.size(), + storage_.data, &storage_.size); + return *this; + } + + template < + int other_capacity, + typename std::enable_if_t* = nullptr> + BoundedInlineVector& operator=( + BoundedInlineVector&& other) { + bounded_inline_vector_impl::DestroyElements(storage_.data, storage_.size); + RTC_DCHECK_LE(other.size(), fixed_capacity); + bounded_inline_vector_impl::MoveElements(other.data(), other.size(), + storage_.data, &storage_.size); + return *this; + } + + bool empty() const { return storage_.size == 0; } + int size() const { return storage_.size; } + constexpr int capacity() const { return fixed_capacity; } + + // Resizes the BoundedInlineVector to the given size, which must not exceed + // its constant capacity. If the size is increased, the added elements are + // default constructed. + void resize(int new_size) { + RTC_DCHECK_GE(new_size, 0); + RTC_DCHECK_LE(new_size, fixed_capacity); + if (new_size > storage_.size) { + bounded_inline_vector_impl::DefaultInitializeElements( + storage_.data + storage_.size, new_size - storage_.size); + } else if (new_size < storage_.size) { + bounded_inline_vector_impl::DestroyElements(storage_.data + new_size, + storage_.size - new_size); + } + storage_.size = new_size; + } + + const T* data() const { return storage_.data; } + T* data() { return storage_.data; } + + const T& operator[](int index) const { + RTC_DCHECK_GE(index, 0); + RTC_DCHECK_LT(index, storage_.size); + return storage_.data[index]; + } + T& operator[](int index) { + RTC_DCHECK_GE(index, 0); + RTC_DCHECK_LT(index, storage_.size); + return storage_.data[index]; + } + + T* begin() { return storage_.data; } + T* end() { return storage_.data + storage_.size; } + const T* begin() const { return storage_.data; } + const T* end() const { return storage_.data + storage_.size; } + const T* cbegin() const { return storage_.data; } + const T* cend() const { return storage_.data + storage_.size; } + + private: + bounded_inline_vector_impl::Storage storage_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_BOUNDED_INLINE_VECTOR_H_ diff --git a/pkg/apm/webrtc/rtc_base/bounded_inline_vector_impl.h b/pkg/apm/webrtc/rtc_base/bounded_inline_vector_impl.h new file mode 100644 index 00000000..82b11b8d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/bounded_inline_vector_impl.h @@ -0,0 +1,216 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_ +#define RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_ + +#include + +#include +#include +#include +#include + +namespace webrtc { +namespace bounded_inline_vector_impl { + +template +struct BoolPack; + +// Tests if all its parameters (x0, x1, ..., xn) are true. The implementation +// checks whether (x0, x1, ..., xn, true) == (true, x0, x1, ..., xn), which is +// true iff true == x0 && x0 == x1 && x1 == x2 ... && xn-1 == xn && xn == true. +template +using AllTrue = std::is_same, BoolPack>; + +template +using AllConvertible = AllTrue::value...>; + +// Initializes part of an uninitialized array. Unlike normal array +// initialization, does not zero the remaining array elements. Caller is +// responsible for ensuring that there is enough space in `data`. +template +void InitializeElements(T* data) {} +template +void InitializeElements(T* data, U&& element, Us&&... elements) { + // Placement new, because we construct a new object in uninitialized memory. + ::new (data) T(std::forward(element)); + InitializeElements(data + 1, std::forward(elements)...); +} + +// Default initializes uninitialized array elements. +template +void DefaultInitializeElements(T* data, int size) { + std::uninitialized_default_construct_n(data, size); +} + +// Copies from source to uninitialized destination. Caller is responsible for +// ensuring that there is enough space in `dst_data`. +template +void CopyElements(const T* src_data, int src_size, T* dst_data, int* dst_size) { + if /*constexpr*/ (std::is_trivially_copy_constructible::value) { + std::memcpy(dst_data, src_data, src_size * sizeof(T)); + } else { + std::uninitialized_copy_n(src_data, src_size, dst_data); + } + *dst_size = src_size; +} + +// Moves from source to uninitialized destination. Caller is responsible for +// ensuring that there is enough space in `dst_data`. +template +void MoveElements(T* src_data, int src_size, T* dst_data, int* dst_size) { + if /*constexpr*/ (std::is_trivially_move_constructible::value) { + std::memcpy(dst_data, src_data, src_size * sizeof(T)); + } else { + std::uninitialized_move_n(src_data, src_size, dst_data); + } + *dst_size = src_size; +} + +// Destroys elements, leaving them uninitialized. +template +void DestroyElements(T* data, int size) { + if /*constexpr*/ (!std::is_trivially_destructible::value) { + for (int i = 0; i < size; ++i) { + data[i].~T(); + } + } +} + +// If elements are trivial and the total capacity is at most this many bytes, +// copy everything instead of just the elements that are in use; this is more +// efficient, and makes BoundedInlineVector trivially copyable. +static constexpr int kSmallSize = 64; + +// Storage implementations. +// +// There are diferent Storage structs for diferent kinds of element types. The +// common contract is the following: +// +// * They have public `size` variables and `data` array members. +// +// * Their owner is responsible for enforcing the invariant that the first +// `size` elements in `data` are initialized, and the remaining elements are +// not initialized. +// +// * They implement default construction, construction with one or more +// elements, copy/move construction, copy/move assignment, and destruction; +// the owner must ensure that the invariant holds whenever these operations +// occur. + +// Storage implementation for nontrivial element types. +template ::value, + bool is_small = (sizeof(T) * fixed_capacity <= kSmallSize)> +struct Storage { + static_assert(!std::is_trivial::value, ""); + + template < + typename... Ts, + typename std::enable_if_t::value>* = nullptr> + explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) { + InitializeElements(data, std::forward(elements)...); + } + + Storage(const Storage& other) { + CopyElements(other.data, other.size, data, &size); + } + + Storage(Storage&& other) { + MoveElements(other.data, other.size, data, &size); + } + + Storage& operator=(const Storage& other) { + if (this != &other) { + DestroyElements(data, size); + CopyElements(other.data, other.size, data, &size); + } + return *this; + } + + Storage& operator=(Storage&& other) { + DestroyElements(data, size); + size = 0; // Needed in case of self assignment. + MoveElements(other.data, other.size, data, &size); + return *this; + } + + ~Storage() { DestroyElements(data, size); } + + int size; + union { + // Since this array is in a union, we get to construct and destroy it + // manually. + T data[fixed_capacity]; // NOLINT(runtime/arrays) + }; +}; + +// Storage implementation for trivial element types when the capacity is small +// enough that we can cheaply copy everything. +template +struct Storage { + static_assert(std::is_trivial::value, ""); + static_assert(sizeof(T) * fixed_capacity <= kSmallSize, ""); + + template < + typename... Ts, + typename std::enable_if_t::value>* = nullptr> + explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) { + InitializeElements(data, std::forward(elements)...); + } + + Storage(const Storage&) = default; + Storage& operator=(const Storage&) = default; + ~Storage() = default; + + int size; + T data[fixed_capacity]; // NOLINT(runtime/arrays) +}; + +// Storage implementation for trivial element types when the capacity is large +// enough that we want to avoid copying uninitialized elements. +template +struct Storage { + static_assert(std::is_trivial::value, ""); + static_assert(sizeof(T) * fixed_capacity > kSmallSize, ""); + + template < + typename... Ts, + typename std::enable_if_t::value>* = nullptr> + explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) { + InitializeElements(data, std::forward(elements)...); + } + + Storage(const Storage& other) : size(other.size) { + std::memcpy(data, other.data, other.size * sizeof(T)); + } + + Storage& operator=(const Storage& other) { + if (this != &other) { + size = other.size; + std::memcpy(data, other.data, other.size * sizeof(T)); + } + return *this; + } + + ~Storage() = default; + + int size; + union { + T data[fixed_capacity]; // NOLINT(runtime/arrays) + }; +}; + +} // namespace bounded_inline_vector_impl +} // namespace webrtc + +#endif // RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_ diff --git a/pkg/apm/webrtc/rtc_base/buffer.h b/pkg/apm/webrtc/rtc_base/buffer.h new file mode 100644 index 00000000..95e8857e --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/buffer.h @@ -0,0 +1,464 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BUFFER_H_ +#define RTC_BASE_BUFFER_H_ + +#include + +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/type_traits.h" +#include "rtc_base/zero_memory.h" + +namespace webrtc { + +namespace internal { + +// (Internal; please don't use outside this file.) Determines if elements of +// type U are compatible with a BufferT. For most types, we just ignore +// top-level const and forbid top-level volatile and require T and U to be +// otherwise equal, but all byte-sized integers (notably char, int8_t, and +// uint8_t) are compatible with each other. (Note: We aim to get rid of this +// behavior, and treat all types the same.) +template +struct BufferCompat { + static constexpr bool value = + !std::is_volatile::value && + ((std::is_integral::value && sizeof(T) == 1) + ? (std::is_integral::value && sizeof(U) == 1) + : (std::is_same::type>::value)); +}; + +} // namespace internal + +// Basic buffer class, can be grown and shrunk dynamically. +// Unlike std::string/vector, does not initialize data when increasing size. +// If "ZeroOnFree" is true, any memory is explicitly cleared before releasing. +// The type alias "ZeroOnFreeBuffer" below should be used instead of setting +// "ZeroOnFree" in the template manually to "true". +template +class BufferT { + // We want T's destructor and default constructor to be trivial, i.e. perform + // no action, so that we don't have to touch the memory we allocate and + // deallocate. And we want T to be trivially copyable, so that we can copy T + // instances with std::memcpy. This is precisely the definition of a trivial + // type. + static_assert(std::is_trivial::value, "T must be a trivial type."); + + // This class relies heavily on being able to mutate its data. + static_assert(!std::is_const::value, "T may not be const"); + + public: + using value_type = T; + using const_iterator = const T*; + + // An empty BufferT. + BufferT() : size_(0), capacity_(0), data_(nullptr) { + RTC_DCHECK(IsConsistent()); + } + + // Disable copy construction and copy assignment, since copying a buffer is + // expensive enough that we want to force the user to be explicit about it. + BufferT(const BufferT&) = delete; + BufferT& operator=(const BufferT&) = delete; + + BufferT(BufferT&& buf) + : size_(buf.size()), + capacity_(buf.capacity()), + data_(std::move(buf.data_)) { + RTC_DCHECK(IsConsistent()); + buf.OnMovedFrom(); + } + + // Construct a buffer with the specified number of uninitialized elements. + explicit BufferT(size_t size) : BufferT(size, size) {} + + BufferT(size_t size, size_t capacity) + : size_(size), + capacity_(std::max(size, capacity)), + data_(capacity_ > 0 ? new T[capacity_] : nullptr) { + RTC_DCHECK(IsConsistent()); + } + + // Construct a buffer and copy the specified number of elements into it. + template ::value>::type* = nullptr> + BufferT(const U* data, size_t size) : BufferT(data, size, size) {} + + template ::value>::type* = nullptr> + BufferT(U* data, size_t size, size_t capacity) : BufferT(size, capacity) { + static_assert(sizeof(T) == sizeof(U), ""); + if (size > 0) { + RTC_DCHECK(data); + std::memcpy(data_.get(), data, size * sizeof(U)); + } + } + + // Construct a buffer from the contents of an array. + template ::value>::type* = nullptr> + BufferT(U (&array)[N]) : BufferT(array, N) {} + + ~BufferT() { MaybeZeroCompleteBuffer(); } + + // Implicit conversion to absl::string_view if T is compatible with char. + template + operator typename std::enable_if::value, + absl::string_view>::type() const { + return absl::string_view(data(), size()); + } + + // Get a pointer to the data. Just .data() will give you a (const) T*, but if + // T is a byte-sized integer, you may also use .data() for any other + // byte-sized integer U. + template ::value>::type* = nullptr> + const U* data() const { + RTC_DCHECK(IsConsistent()); + return reinterpret_cast(data_.get()); + } + + template ::value>::type* = nullptr> + U* data() { + RTC_DCHECK(IsConsistent()); + return reinterpret_cast(data_.get()); + } + + bool empty() const { + RTC_DCHECK(IsConsistent()); + return size_ == 0; + } + + size_t size() const { + RTC_DCHECK(IsConsistent()); + return size_; + } + + size_t capacity() const { + RTC_DCHECK(IsConsistent()); + return capacity_; + } + + BufferT& operator=(BufferT&& buf) { + RTC_DCHECK(buf.IsConsistent()); + MaybeZeroCompleteBuffer(); + size_ = buf.size_; + capacity_ = buf.capacity_; + using std::swap; + swap(data_, buf.data_); + buf.data_.reset(); + buf.OnMovedFrom(); + return *this; + } + + bool operator==(const BufferT& buf) const { + RTC_DCHECK(IsConsistent()); + if (size_ != buf.size_) { + return false; + } + if (std::is_integral::value) { + // Optimization. + return std::memcmp(data_.get(), buf.data_.get(), size_ * sizeof(T)) == 0; + } + for (size_t i = 0; i < size_; ++i) { + if (data_[i] != buf.data_[i]) { + return false; + } + } + return true; + } + + bool operator!=(const BufferT& buf) const { return !(*this == buf); } + + T& operator[](size_t index) { + RTC_DCHECK_LT(index, size_); + return data()[index]; + } + + T operator[](size_t index) const { + RTC_DCHECK_LT(index, size_); + return data()[index]; + } + + T* begin() { return data(); } + T* end() { return data() + size(); } + const T* begin() const { return data(); } + const T* end() const { return data() + size(); } + const T* cbegin() const { return data(); } + const T* cend() const { return data() + size(); } + + // The SetData functions replace the contents of the buffer. They accept the + // same input types as the constructors. + template ::value>::type* = nullptr> + void SetData(const U* data, size_t size) { + RTC_DCHECK(IsConsistent()); + const size_t old_size = size_; + size_ = 0; + AppendData(data, size); + if (ZeroOnFree && size_ < old_size) { + ZeroTrailingData(old_size - size_); + } + } + + template ::value>::type* = nullptr> + void SetData(const U (&array)[N]) { + SetData(array, N); + } + + template ::value>::type* = nullptr> + void SetData(const W& w) { + SetData(w.data(), w.size()); + } + + // Replaces the data in the buffer with at most `max_elements` of data, using + // the function `setter`, which should have the following signature: + // + // size_t setter(ArrayView view) + // + // `setter` is given an appropriately typed ArrayView of length exactly + // `max_elements` that describes the area where it should write the data; it + // should return the number of elements actually written. (If it doesn't fill + // the whole ArrayView, it should leave the unused space at the end.) + template ::value>::type* = nullptr> + size_t SetData(size_t max_elements, F&& setter) { + RTC_DCHECK(IsConsistent()); + const size_t old_size = size_; + size_ = 0; + const size_t written = AppendData(max_elements, std::forward(setter)); + if (ZeroOnFree && size_ < old_size) { + ZeroTrailingData(old_size - size_); + } + return written; + } + + // The AppendData functions add data to the end of the buffer. They accept + // the same input types as the constructors. + template ::value>::type* = nullptr> + void AppendData(const U* data, size_t size) { + if (size == 0) { + return; + } + RTC_DCHECK(data); + RTC_DCHECK(IsConsistent()); + const size_t new_size = size_ + size; + EnsureCapacityWithHeadroom(new_size, true); + static_assert(sizeof(T) == sizeof(U), ""); + std::memcpy(data_.get() + size_, data, size * sizeof(U)); + size_ = new_size; + RTC_DCHECK(IsConsistent()); + } + + template ::value>::type* = nullptr> + void AppendData(const U (&array)[N]) { + AppendData(array, N); + } + + template ::value>::type* = nullptr> + void AppendData(const W& w) { + AppendData(w.data(), w.size()); + } + + template ::value>::type* = nullptr> + void AppendData(const U& item) { + AppendData(&item, 1); + } + + // Appends at most `max_elements` to the end of the buffer, using the function + // `setter`, which should have the following signature: + // + // size_t setter(ArrayView view) + // + // `setter` is given an appropriately typed ArrayView of length exactly + // `max_elements` that describes the area where it should write the data; it + // should return the number of elements actually written. (If it doesn't fill + // the whole ArrayView, it should leave the unused space at the end.) + template ::value>::type* = nullptr> + size_t AppendData(size_t max_elements, F&& setter) { + RTC_DCHECK(IsConsistent()); + const size_t old_size = size_; + SetSize(old_size + max_elements); + U* base_ptr = data() + old_size; + size_t written_elements = setter(ArrayView(base_ptr, max_elements)); + + RTC_CHECK_LE(written_elements, max_elements); + size_ = old_size + written_elements; + RTC_DCHECK(IsConsistent()); + return written_elements; + } + + // Sets the size of the buffer. If the new size is smaller than the old, the + // buffer contents will be kept but truncated; if the new size is greater, + // the existing contents will be kept and the new space will be + // uninitialized. + void SetSize(size_t size) { + const size_t old_size = size_; + EnsureCapacityWithHeadroom(size, true); + size_ = size; + if (ZeroOnFree && size_ < old_size) { + ZeroTrailingData(old_size - size_); + } + } + + // Ensure that the buffer size can be increased to at least capacity without + // further reallocation. (Of course, this operation might need to reallocate + // the buffer.) + void EnsureCapacity(size_t capacity) { + // Don't allocate extra headroom, since the user is asking for a specific + // capacity. + EnsureCapacityWithHeadroom(capacity, false); + } + + // Resets the buffer to zero size without altering capacity. Works even if the + // buffer has been moved from. + void Clear() { + MaybeZeroCompleteBuffer(); + size_ = 0; + RTC_DCHECK(IsConsistent()); + } + + // Swaps two buffers. Also works for buffers that have been moved from. + friend void swap(BufferT& a, BufferT& b) { + using std::swap; + swap(a.size_, b.size_); + swap(a.capacity_, b.capacity_); + swap(a.data_, b.data_); + } + + private: + void EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom) { + RTC_DCHECK(IsConsistent()); + if (capacity <= capacity_) + return; + + // If the caller asks for extra headroom, ensure that the new capacity is + // >= 1.5 times the old capacity. Any constant > 1 is sufficient to prevent + // quadratic behavior; as to why we pick 1.5 in particular, see + // https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md and + // http://www.gahcep.com/cpp-internals-stl-vector-part-1/. + const size_t new_capacity = + extra_headroom ? std::max(capacity, capacity_ + capacity_ / 2) + : capacity; + + std::unique_ptr new_data(new T[new_capacity]); + if (data_ != nullptr) { + std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T)); + } + MaybeZeroCompleteBuffer(); + data_ = std::move(new_data); + capacity_ = new_capacity; + RTC_DCHECK(IsConsistent()); + } + + // Zero the complete buffer if template argument "ZeroOnFree" is true. + void MaybeZeroCompleteBuffer() { + if (ZeroOnFree && capacity_ > 0) { + // It would be sufficient to only zero "size_" elements, as all other + // methods already ensure that the unused capacity contains no sensitive + // data---but better safe than sorry. + ExplicitZeroMemory(data_.get(), capacity_ * sizeof(T)); + } + } + + // Zero the first "count" elements of unused capacity. + void ZeroTrailingData(size_t count) { + RTC_DCHECK(IsConsistent()); + RTC_DCHECK_LE(count, capacity_ - size_); + ExplicitZeroMemory(data_.get() + size_, count * sizeof(T)); + } + + // Precondition for all methods except Clear, operator= and the destructor. + // Postcondition for all methods except move construction and move + // assignment, which leave the moved-from object in a possibly inconsistent + // state. + bool IsConsistent() const { + return (data_ || capacity_ == 0) && capacity_ >= size_; + } + + // Called when *this has been moved from. Conceptually it's a no-op, but we + // can mutate the state slightly to help subsequent sanity checks catch bugs. + void OnMovedFrom() { + RTC_DCHECK(!data_); // Our heap block should have been stolen. +#if RTC_DCHECK_IS_ON + // Ensure that *this is always inconsistent, to provoke bugs. + size_ = 1; + capacity_ = 0; +#else + // Make *this consistent and empty. Shouldn't be necessary, but better safe + // than sorry. + size_ = 0; + capacity_ = 0; +#endif + } + + size_t size_; + size_t capacity_; + std::unique_ptr data_; +}; + +// By far the most common sort of buffer. +using Buffer = BufferT; + +// A buffer that zeros memory before releasing it. +template +using ZeroOnFreeBuffer = BufferT; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +template +using BufferT = ::webrtc::BufferT; +using ::webrtc::Buffer; +template +using ZeroOnFreeBuffer = ::webrtc::ZeroOnFreeBuffer; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_BUFFER_H_ diff --git a/pkg/apm/webrtc/rtc_base/buffer_queue.h b/pkg/apm/webrtc/rtc_base/buffer_queue.h new file mode 100644 index 00000000..fdcc2ba8 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/buffer_queue.h @@ -0,0 +1,78 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BUFFER_QUEUE_H_ +#define RTC_BASE_BUFFER_QUEUE_H_ + +#include + +#include +#include + +#include "api/sequence_checker.h" +#include "rtc_base/buffer.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class BufferQueue final { + public: + // Creates a buffer queue with a given capacity and default buffer size. + BufferQueue(size_t capacity, size_t default_size); + ~BufferQueue(); + + BufferQueue(const BufferQueue&) = delete; + BufferQueue& operator=(const BufferQueue&) = delete; + + // Return number of queued buffers. + size_t size() const; + + // Clear the BufferQueue by moving all Buffers from `queue_` to `free_list_`. + void Clear(); + + // ReadFront will only read one buffer at a time and will truncate buffers + // that don't fit in the passed memory. + // Returns true unless no data could be returned. + bool ReadFront(void* data, size_t bytes, size_t* bytes_read); + + // WriteBack always writes either the complete memory or nothing. + // Returns true unless no data could be written. + bool WriteBack(const void* data, size_t bytes, size_t* bytes_written); + + bool is_writable() const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + return queue_.size() < capacity_; + } + + bool is_readable() const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + return !queue_.empty(); + } + + private: + RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_; + const size_t capacity_; + const size_t default_size_; + std::deque queue_ RTC_GUARDED_BY(sequence_checker_); + std::vector free_list_ RTC_GUARDED_BY(sequence_checker_); +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::BufferQueue; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_BUFFER_QUEUE_H_ diff --git a/pkg/apm/webrtc/rtc_base/byte_buffer.h b/pkg/apm/webrtc/rtc_base/byte_buffer.h new file mode 100644 index 00000000..b962f457 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/byte_buffer.h @@ -0,0 +1,220 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BYTE_BUFFER_H_ +#define RTC_BASE_BYTE_BUFFER_H_ + +#include +#include + +#include + +#include "absl/base/attributes.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "rtc_base/buffer.h" +#include "rtc_base/byte_order.h" + +// Reads/Writes from/to buffer using network byte order (big endian) +namespace webrtc { + +template +class ByteBufferWriterT { + using value_type = typename BufferClassT::value_type; + + public: + ByteBufferWriterT() { Construct(nullptr, kDefaultCapacity); } + ByteBufferWriterT(const value_type* bytes, size_t len) { + Construct(bytes, len); + } + + ByteBufferWriterT(const ByteBufferWriterT&) = delete; + ByteBufferWriterT& operator=(const ByteBufferWriterT&) = delete; + + const value_type* Data() const { return buffer_.data(); } + size_t Length() const { return buffer_.size(); } + size_t Capacity() const { return buffer_.capacity(); } + ArrayView DataView() const { + return MakeArrayView(Data(), Length()); + } + // Accessor that returns a string_view, independent of underlying type. + // Intended to provide access for existing users that expect char* + // when the underlying type changes to uint8_t. + // TODO(bugs.webrtc.org/15665): Delete when users are converted. + absl::string_view DataAsStringView() const { + return absl::string_view(reinterpret_cast(Data()), Length()); + } + const char* DataAsCharPointer() const { + return reinterpret_cast(Data()); + } + + // Write value to the buffer. Resizes the buffer when it is + // neccessary. + void WriteUInt8(uint8_t val) { + WriteBytesInternal(reinterpret_cast(&val), 1); + } + void WriteUInt16(uint16_t val) { + uint16_t v = webrtc::HostToNetwork16(val); + WriteBytesInternal(reinterpret_cast(&v), 2); + } + void WriteUInt24(uint32_t val) { + uint32_t v = webrtc::HostToNetwork32(val); + value_type* start = reinterpret_cast(&v); + ++start; + WriteBytesInternal(start, 3); + } + void WriteUInt32(uint32_t val) { + uint32_t v = webrtc::HostToNetwork32(val); + WriteBytesInternal(reinterpret_cast(&v), 4); + } + void WriteUInt64(uint64_t val) { + uint64_t v = webrtc::HostToNetwork64(val); + WriteBytesInternal(reinterpret_cast(&v), 8); + } + // Serializes an unsigned varint in the format described by + // https://developers.google.com/protocol-buffers/docs/encoding#varints + // with the caveat that integers are 64-bit, not 128-bit. + void WriteUVarint(uint64_t val) { + while (val >= 0x80) { + // Write 7 bits at a time, then set the msb to a continuation byte + // (msb=1). + value_type byte = static_cast(val) | 0x80; + WriteBytesInternal(&byte, 1); + val >>= 7; + } + value_type last_byte = static_cast(val); + WriteBytesInternal(&last_byte, 1); + } + void WriteString(absl::string_view val) { + WriteBytesInternal(reinterpret_cast(val.data()), + val.size()); + } + // Write an array of bytes (uint8_t) + [[deprecated("issues.webrtc.org/4225170 - use Write(ArrayView)")]] + void WriteBytes(const uint8_t* val, size_t len) { + WriteBytesInternal(reinterpret_cast(val), len); + } + + void Write(ArrayView data) { + WriteBytesInternal(data.data(), data.size()); + } + + // Reserves the given number of bytes and returns a value_type* that can be + // written into. Useful for functions that require a value_type* buffer and + // not a ByteBufferWriter. + value_type* ReserveWriteBuffer(size_t len) { + buffer_.SetSize(buffer_.size() + len); + return buffer_.data(); + } + + // Resize the buffer to the specified `size`. + void Resize(size_t size) { buffer_.SetSize(size); } + + // Clears the contents of the buffer. After this, Length() will be 0. + void Clear() { buffer_.Clear(); } + + BufferClassT Extract() && { return std::move(buffer_); } + + private: + static constexpr size_t kDefaultCapacity = 4096; + + void Construct(const value_type* bytes, size_t size) { + if (bytes) { + buffer_.AppendData(bytes, size); + } else { + buffer_.EnsureCapacity(size); + } + } + + void WriteBytesInternal(const value_type* val, size_t len) { + buffer_.AppendData(val, len); + } + + BufferClassT buffer_; + + // There are sensible ways to define these, but they aren't needed in our code + // base. +}; + +class ByteBufferWriter : public ByteBufferWriterT> { + public: + ByteBufferWriter(); + ByteBufferWriter(const uint8_t* bytes, size_t len); + + ByteBufferWriter(const ByteBufferWriter&) = delete; + ByteBufferWriter& operator=(const ByteBufferWriter&) = delete; +}; + +// The ByteBufferReader references the passed data, i.e. the pointer must be +// valid during the lifetime of the reader. +class ByteBufferReader { + public: + explicit ByteBufferReader( + ArrayView bytes ABSL_ATTRIBUTE_LIFETIME_BOUND); + + explicit ByteBufferReader(const ByteBufferWriter& buf); + + ByteBufferReader(const ByteBufferReader&) = delete; + ByteBufferReader& operator=(const ByteBufferReader&) = delete; + + const uint8_t* Data() const { return bytes_ + start_; } + // Returns number of unprocessed bytes. + size_t Length() const { return end_ - start_; } + // Returns a view of the unprocessed data. Does not move current position. + ArrayView DataView() const { + return ArrayView(bytes_ + start_, end_ - start_); + } + + // Read a next value from the buffer. Return false if there isn't + // enough data left for the specified type. + bool ReadUInt8(uint8_t* val); + bool ReadUInt16(uint16_t* val); + bool ReadUInt24(uint32_t* val); + bool ReadUInt32(uint32_t* val); + bool ReadUInt64(uint64_t* val); + bool ReadUVarint(uint64_t* val); + // Copies the val.size() next bytes into val.data(). + bool ReadBytes(ArrayView val); + // Appends next `len` bytes from the buffer to `val`. Returns false + // if there is less than `len` bytes left. + bool ReadString(std::string* val, size_t len); + // Same as `ReadString` except that the returned string_view will point into + // the internal buffer (no additional buffer allocation). + bool ReadStringView(absl::string_view* val, size_t len); + + // Moves current position `size` bytes forward. Returns false if + // there is less than `size` bytes left in the buffer. Consume doesn't + // permanently remove data, so remembered read positions are still valid + // after this call. + bool Consume(size_t size); + + private: + void Construct(const uint8_t* bytes, size_t size); + bool ReadBytes(uint8_t* val, size_t len); + + const uint8_t* bytes_; + size_t size_; + size_t start_; + size_t end_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::ByteBufferReader; +using ::webrtc::ByteBufferWriter; +using ::webrtc::ByteBufferWriterT; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_BYTE_BUFFER_H_ diff --git a/pkg/apm/webrtc/rtc_base/byte_order.h b/pkg/apm/webrtc/rtc_base/byte_order.h new file mode 100644 index 00000000..95c8ebc8 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/byte_order.h @@ -0,0 +1,240 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_BYTE_ORDER_H_ +#define RTC_BASE_BYTE_ORDER_H_ + +#include + +#include + +#if defined(WEBRTC_POSIX) && !defined(__native_client__) +#include +#endif + +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_MAC) +#include + +#define htobe16(v) OSSwapHostToBigInt16(v) +#define htobe32(v) OSSwapHostToBigInt32(v) +#define htobe64(v) OSSwapHostToBigInt64(v) +#define be16toh(v) OSSwapBigToHostInt16(v) +#define be32toh(v) OSSwapBigToHostInt32(v) +#define be64toh(v) OSSwapBigToHostInt64(v) + +#define htole16(v) OSSwapHostToLittleInt16(v) +#define htole32(v) OSSwapHostToLittleInt32(v) +#define htole64(v) OSSwapHostToLittleInt64(v) +#define le16toh(v) OSSwapLittleToHostInt16(v) +#define le32toh(v) OSSwapLittleToHostInt32(v) +#define le64toh(v) OSSwapLittleToHostInt64(v) + +#elif defined(WEBRTC_WIN) || defined(__native_client__) + +#if defined(WEBRTC_WIN) +#include +#include +#else +#include // no-presubmit-check +#endif // defined(WEBRTC_WIN) + +#if defined(WEBRTC_ARCH_LITTLE_ENDIAN) +#define htobe16(v) htons(v) +#define htobe32(v) htonl(v) +#define be16toh(v) ntohs(v) +#define be32toh(v) ntohl(v) +#define htole16(v) (v) +#define htole32(v) (v) +#define htole64(v) (v) +#define le16toh(v) (v) +#define le32toh(v) (v) +#define le64toh(v) (v) +#if defined(WEBRTC_WIN) +#define htobe64(v) _byteswap_uint64(v) +#define be64toh(v) _byteswap_uint64(v) +#endif // defined(WEBRTC_WIN) +#if defined(__native_client__) +#define htobe64(v) __builtin_bswap64(v) +#define be64toh(v) __builtin_bswap64(v) +#endif // defined(__native_client__) + +#elif defined(WEBRTC_ARCH_BIG_ENDIAN) +#define htobe16(v) (v) +#define htobe32(v) (v) +#define be16toh(v) (v) +#define be32toh(v) (v) +#define htole16(v) __builtin_bswap16(v) +#define htole32(v) __builtin_bswap32(v) +#define htole64(v) __builtin_bswap64(v) +#define le16toh(v) __builtin_bswap16(v) +#define le32toh(v) __builtin_bswap32(v) +#define le64toh(v) __builtin_bswap64(v) +#if defined(WEBRTC_WIN) +#define htobe64(v) (v) +#define be64toh(v) (v) +#endif // defined(WEBRTC_WIN) +#if defined(__native_client__) +#define htobe64(v) (v) +#define be64toh(v) (v) +#endif // defined(__native_client__) +#else +#error WEBRTC_ARCH_BIG_ENDIAN or WEBRTC_ARCH_LITTLE_ENDIAN must be defined. +#endif // defined(WEBRTC_ARCH_LITTLE_ENDIAN) + +#elif defined(WEBRTC_POSIX) +#include +#else +#error "Missing byte order functions for this arch." +#endif // defined(WEBRTC_MAC) + +namespace webrtc { + +// Reading and writing of little and big-endian numbers from memory + +inline void Set8(void* memory, size_t offset, uint8_t v) { + static_cast(memory)[offset] = v; +} + +inline uint8_t Get8(const void* memory, size_t offset) { + return static_cast(memory)[offset]; +} + +inline void SetBE16(void* memory, uint16_t v) { + uint16_t val = htobe16(v); + memcpy(memory, &val, sizeof(val)); +} + +inline void SetBE32(void* memory, uint32_t v) { + uint32_t val = htobe32(v); + memcpy(memory, &val, sizeof(val)); +} + +inline void SetBE64(void* memory, uint64_t v) { + uint64_t val = htobe64(v); + memcpy(memory, &val, sizeof(val)); +} + +inline uint16_t GetBE16(const void* memory) { + uint16_t val; + memcpy(&val, memory, sizeof(val)); + return be16toh(val); +} + +inline uint32_t GetBE32(const void* memory) { + uint32_t val; + memcpy(&val, memory, sizeof(val)); + return be32toh(val); +} + +inline uint64_t GetBE64(const void* memory) { + uint64_t val; + memcpy(&val, memory, sizeof(val)); + return be64toh(val); +} + +inline void SetLE16(void* memory, uint16_t v) { + uint16_t val = htole16(v); + memcpy(memory, &val, sizeof(val)); +} + +inline void SetLE32(void* memory, uint32_t v) { + uint32_t val = htole32(v); + memcpy(memory, &val, sizeof(val)); +} + +inline void SetLE64(void* memory, uint64_t v) { + uint64_t val = htole64(v); + memcpy(memory, &val, sizeof(val)); +} + +inline uint16_t GetLE16(const void* memory) { + uint16_t val; + memcpy(&val, memory, sizeof(val)); + return le16toh(val); +} + +inline uint32_t GetLE32(const void* memory) { + uint32_t val; + memcpy(&val, memory, sizeof(val)); + return le32toh(val); +} + +inline uint64_t GetLE64(const void* memory) { + uint64_t val; + memcpy(&val, memory, sizeof(val)); + return le64toh(val); +} + +// Check if the current host is big endian. +inline bool IsHostBigEndian() { +#if defined(WEBRTC_ARCH_BIG_ENDIAN) + return true; +#else + return false; +#endif +} + +inline uint16_t HostToNetwork16(uint16_t n) { + return htobe16(n); +} + +inline uint32_t HostToNetwork32(uint32_t n) { + return htobe32(n); +} + +inline uint64_t HostToNetwork64(uint64_t n) { + return htobe64(n); +} + +inline uint16_t NetworkToHost16(uint16_t n) { + return be16toh(n); +} + +inline uint32_t NetworkToHost32(uint32_t n) { + return be32toh(n); +} + +inline uint64_t NetworkToHost64(uint64_t n) { + return be64toh(n); +} + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::Get8; +using ::webrtc::GetBE16; +using ::webrtc::GetBE32; +using ::webrtc::GetBE64; +using ::webrtc::GetLE16; +using ::webrtc::GetLE32; +using ::webrtc::GetLE64; +using ::webrtc::HostToNetwork16; +using ::webrtc::HostToNetwork32; +using ::webrtc::HostToNetwork64; +using ::webrtc::IsHostBigEndian; +using ::webrtc::NetworkToHost16; +using ::webrtc::NetworkToHost32; +using ::webrtc::NetworkToHost64; +using ::webrtc::Set8; +using ::webrtc::SetBE16; +using ::webrtc::SetBE32; +using ::webrtc::SetBE64; +using ::webrtc::SetLE16; +using ::webrtc::SetLE32; +using ::webrtc::SetLE64; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_BYTE_ORDER_H_ diff --git a/pkg/apm/webrtc/rtc_base/callback_list.cc b/pkg/apm/webrtc/rtc_base/callback_list.cc new file mode 100644 index 00000000..e938f96e --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/callback_list.cc @@ -0,0 +1,120 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/callback_list.h" + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace callback_list_impl { + +CallbackListReceivers::CallbackListReceivers() = default; + +CallbackListReceivers::~CallbackListReceivers() { + RTC_CHECK(!send_in_progress_); +} + +void CallbackListReceivers::RemoveReceivers(const void* removal_tag) { + RTC_DCHECK(removal_tag); + + // We divide the receivers_ vector into three regions: from right to left, the + // "keep" region, the "todo" region, and the "remove" region. The "todo" + // region initially covers the whole vector. + size_t first_todo = 0; // First element of the "todo" + // region. + size_t first_remove = receivers_.size(); // First element of the "remove" + // region. + + // Loop until the "todo" region is empty. + while (first_todo != first_remove) { + if (receivers_[first_todo].removal_tag != removal_tag) { + // The first element of the "todo" region should be kept. Move the + // "keep"/"todo" boundary. + ++first_todo; + } else if (receivers_[first_remove - 1].removal_tag == removal_tag) { + // The last element of the "todo" region should be removed. Move the + // "todo"/"remove" boundary. + if (send_in_progress_) { + // Tag this receiver for removal, which will be done when `ForEach` + // has completed. + receivers_[first_remove - 1].removal_tag = pending_removal_tag(); + } + --first_remove; + } else if (!send_in_progress_) { + // The first element of the "todo" region should be removed, and the last + // element of the "todo" region should be kept. Swap them, and then shrink + // the "todo" region from both ends. + RTC_DCHECK_NE(first_todo, first_remove - 1); + using std::swap; + swap(receivers_[first_todo], receivers_[first_remove - 1]); + RTC_DCHECK_NE(receivers_[first_todo].removal_tag, removal_tag); + ++first_todo; + RTC_DCHECK_EQ(receivers_[first_remove - 1].removal_tag, removal_tag); + --first_remove; + } + } + + if (!send_in_progress_) { + // Discard the remove region. + receivers_.resize(first_remove); + } +} + +void CallbackListReceivers::Foreach(FunctionView fv) { + RTC_CHECK(!send_in_progress_); + bool removals_detected = false; + send_in_progress_ = true; + for (auto& r : receivers_) { + RTC_DCHECK_NE(r.removal_tag, pending_removal_tag()); + fv(r.function); + if (r.removal_tag == pending_removal_tag()) { + removals_detected = true; + } + } + send_in_progress_ = false; + if (removals_detected) { + RemoveReceivers(pending_removal_tag()); + } +} + +template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::TrivialUntypedFunctionArgs<1>); +template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::TrivialUntypedFunctionArgs<2>); +template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::TrivialUntypedFunctionArgs<3>); +template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::TrivialUntypedFunctionArgs<4>); +template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::NontrivialUntypedFunctionArgs); +template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::FunctionPointerUntypedFunctionArgs); + +template void CallbackListReceivers::AddReceiver( + UntypedFunction::TrivialUntypedFunctionArgs<1>); +template void CallbackListReceivers::AddReceiver( + UntypedFunction::TrivialUntypedFunctionArgs<2>); +template void CallbackListReceivers::AddReceiver( + UntypedFunction::TrivialUntypedFunctionArgs<3>); +template void CallbackListReceivers::AddReceiver( + UntypedFunction::TrivialUntypedFunctionArgs<4>); +template void CallbackListReceivers::AddReceiver( + UntypedFunction::NontrivialUntypedFunctionArgs); +template void CallbackListReceivers::AddReceiver( + UntypedFunction::FunctionPointerUntypedFunctionArgs); + +} // namespace callback_list_impl +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/callback_list.h b/pkg/apm/webrtc/rtc_base/callback_list.h new file mode 100644 index 00000000..73a6974a --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/callback_list.h @@ -0,0 +1,223 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_CALLBACK_LIST_H_ +#define RTC_BASE_CALLBACK_LIST_H_ + +#include +#include + +#include "api/function_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/assume.h" +#include "rtc_base/system/inline.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/untyped_function.h" + +namespace webrtc { +namespace callback_list_impl { + +class RTC_EXPORT CallbackListReceivers { + public: + CallbackListReceivers(); + CallbackListReceivers(const CallbackListReceivers&) = delete; + CallbackListReceivers& operator=(const CallbackListReceivers&) = delete; + CallbackListReceivers(CallbackListReceivers&&) = delete; + CallbackListReceivers& operator=(CallbackListReceivers&&) = delete; + ~CallbackListReceivers(); + + template + RTC_NO_INLINE void AddReceiver(const void* removal_tag, + UntypedFunctionArgsT args) { + RTC_CHECK(!send_in_progress_); + RTC_DCHECK(removal_tag != nullptr); + receivers_.push_back({removal_tag, UntypedFunction::Create(args)}); + } + + template + RTC_NO_INLINE void AddReceiver(UntypedFunctionArgsT args) { + RTC_CHECK(!send_in_progress_); + receivers_.push_back({nullptr, UntypedFunction::Create(args)}); + } + + void RemoveReceivers(const void* removal_tag); + + void Foreach(FunctionView fv); + + private: + // Special protected pointer value that's used as a removal_tag for + // receivers that want to unsubscribe from within a callback. + // Note we could use `&receivers_` too, but since it's the first member + // variable of the class, its address will be the same as the instance + // CallbackList instance, so we take an extra step to avoid collision. + const void* pending_removal_tag() const { return &send_in_progress_; } + + struct Callback { + const void* removal_tag; + UntypedFunction function; + }; + + std::vector receivers_; + bool send_in_progress_ = false; +}; + +extern template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::TrivialUntypedFunctionArgs<1>); +extern template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::TrivialUntypedFunctionArgs<2>); +extern template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::TrivialUntypedFunctionArgs<3>); +extern template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::TrivialUntypedFunctionArgs<4>); +extern template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::NontrivialUntypedFunctionArgs); +extern template void CallbackListReceivers::AddReceiver( + const void*, + UntypedFunction::FunctionPointerUntypedFunctionArgs); + +extern template void CallbackListReceivers::AddReceiver( + UntypedFunction::TrivialUntypedFunctionArgs<1>); +extern template void CallbackListReceivers::AddReceiver( + UntypedFunction::TrivialUntypedFunctionArgs<2>); +extern template void CallbackListReceivers::AddReceiver( + UntypedFunction::TrivialUntypedFunctionArgs<3>); +extern template void CallbackListReceivers::AddReceiver( + UntypedFunction::TrivialUntypedFunctionArgs<4>); +extern template void CallbackListReceivers::AddReceiver( + UntypedFunction::NontrivialUntypedFunctionArgs); +extern template void CallbackListReceivers::AddReceiver( + UntypedFunction::FunctionPointerUntypedFunctionArgs); + +} // namespace callback_list_impl + +// A collection of receivers (callable objects) that can be called all at once. +// Optimized for minimal binary size. The template arguments dictate what +// signature the callbacks must have; for example, a CallbackList +// will require callbacks with signature void(int, float). +// +// CallbackList is neither copyable nor movable (could easily be made movable if +// necessary). Callbacks must be movable, but need not be copyable. +// +// Usage example: +// +// // Declaration (usually a member variable). +// CallbackList foo_; +// +// // Register callbacks. This can be done zero or more times. The +// // callbacks must accept the arguments types listed in the CallbackList's +// // template argument list, and must return void. +// foo_.AddReceiver([...](int a, float b) {...}); // Lambda. +// foo_.AddReceiver(SomeFunction); // Function pointer. +// +// // Call the zero or more receivers, one after the other. +// foo_.Send(17, 3.14); +// +// Callback lifetime considerations +// -------------------------------- +// +// CallbackList::AddReceiver() takes ownership of the given callback by moving +// it in place. The callback can be any callable object; in particular, it may +// have a nontrivial destructor, which will be run when the CallbackList is +// destroyed. The callback may thus access data via any type of smart pointer, +// expressing e.g. unique, shared, or weak ownership. Of course, if the data is +// guaranteed to outlive the callback, a plain raw pointer can be used. +// +// Take care when trying to have the callback own reference-counted data. The +// CallbackList will keep the callback alive, and the callback will keep its +// data alive, so as usual with reference-counted ownership, keep an eye out for +// cycles! +// +// Thread safety +// ------------- +// +// Like most C++ types, CallbackList is thread compatible: it's not safe to +// access it concurrently from multiple threads, but it can be made safe if it +// is protected by a mutex, for example. +// +// Excercise some care when deciding what mutexes to hold when you call +// CallbackList::Send(). In particular, do not hold mutexes that callbacks may +// need to grab. If a larger object has a CallbackList member and a single mutex +// that protects all of its data members, this may e.g. make it necessary to +// protect its CallbackList with a separate mutex; otherwise, there will be a +// deadlock if the callbacks try to access the object. +// +// CallbackList as a class data member +// ----------------------------------- +// +// CallbackList is a normal C++ data type, and should be private when it is a +// data member of a class. For thread safety reasons (see above), it is likely +// best to not have an accessor for the entire CallbackList, and instead only +// allow callers to add callbacks: +// +// template +// void AddFooCallback(F&& callback) { +// // Maybe grab a mutex here? +// foo_callbacks_.AddReceiver(std::forward(callback)); +// } +// +template +class CallbackList { + public: + CallbackList() = default; + CallbackList(const CallbackList&) = delete; + CallbackList& operator=(const CallbackList&) = delete; + CallbackList(CallbackList&&) = delete; + CallbackList& operator=(CallbackList&&) = delete; + + // Adds a new receiver. The receiver (a callable object or a function pointer) + // must be movable, but need not be copyable. Its call signature should be + // `void(ArgT...)`. The removal tag is a pointer to an arbitrary object that + // you own, and that will stay alive until the CallbackList is gone, or until + // all receivers using it as a removal tag have been removed; you can use it + // to remove the receiver. + template + void AddReceiver(const void* removal_tag, F&& f) { + receivers_.AddReceiver( + removal_tag, + UntypedFunction::PrepareArgs(std::forward(f))); + } + + // Adds a new receiver with no removal tag. + template + void AddReceiver(F&& f) { + receivers_.AddReceiver( + UntypedFunction::PrepareArgs(std::forward(f))); + } + + // Removes all receivers that were added with the given removal tag. + void RemoveReceivers(const void* removal_tag) { + receivers_.RemoveReceivers(removal_tag); + } + + // Calls all receivers with the given arguments. While the Send is in + // progress, no method calls are allowed; specifically, this means that the + // callbacks may not do anything with this CallbackList instance. + // + // Note: Receivers are called serially, but not necessarily in the same order + // they were added. + template + void Send(ArgU&&... args) { + receivers_.Foreach([&](UntypedFunction& f) { + f.Call(std::forward(args)...); + }); + } + + private: + callback_list_impl::CallbackListReceivers receivers_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_CALLBACK_LIST_H_ diff --git a/pkg/apm/webrtc/rtc_base/checks.cc b/pkg/apm/webrtc/rtc_base/checks.cc new file mode 100644 index 00000000..6fa0514c --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/checks.cc @@ -0,0 +1,240 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Most of this was borrowed (with minor modifications) from V8's and Chromium's +// src/base/logging.cc. + +#include +#include +#include + +#include "absl/strings/string_view.h" + +#if defined(WEBRTC_ANDROID) +#define RTC_LOG_TAG_ANDROID "rtc" +#include // NOLINT +#endif + +#if defined(WEBRTC_WIN) +#include +#endif + +#if defined(WEBRTC_WIN) +#define LAST_SYSTEM_ERROR (::GetLastError()) +#elif defined(__native_client__) && __native_client__ +#define LAST_SYSTEM_ERROR (0) +#elif defined(WEBRTC_POSIX) +#include +#define LAST_SYSTEM_ERROR (errno) +#endif // WEBRTC_WIN + +#include "rtc_base/checks.h" + +namespace { + +#if defined(__GNUC__) +__attribute__((__format__(__printf__, 2, 3))) +#endif +void AppendFormat(std::string* s, const char* fmt, ...) { + va_list args, copy; + va_start(args, fmt); + va_copy(copy, args); + const int predicted_length = std::vsnprintf(nullptr, 0, fmt, copy); + va_end(copy); + + if (predicted_length > 0) { + const size_t size = s->size(); + s->resize(size + predicted_length); + // Pass "+ 1" to vsnprintf to include space for the '\0'. + std::vsnprintf(&((*s)[size]), predicted_length + 1, fmt, args); + } + va_end(args); +} +} // namespace + +namespace webrtc { +namespace webrtc_checks_impl { + +#if !defined(WEBRTC_CHROMIUM_BUILD) +RTC_NORETURN void WriteFatalLog(absl::string_view output) { +#if defined(WEBRTC_ANDROID) + std::string output_str(output); + __android_log_print(ANDROID_LOG_ERROR, RTC_LOG_TAG_ANDROID, "%s\n", + output_str.c_str()); +#endif + fflush(stdout); + fwrite(output.data(), output.size(), 1, stderr); + fflush(stderr); +#if defined(WEBRTC_WIN) + DebugBreak(); +#endif + abort(); +} + +RTC_NORETURN void WriteFatalLog(const char* /* file */, + int /* line */, + absl::string_view output) { + WriteFatalLog(output); +} + +#endif // !defined(WEBRTC_CHROMIUM_BUILD) + +#if RTC_CHECK_MSG_ENABLED +// Reads one argument from args, appends it to s and advances fmt. +// Returns true iff an argument was sucessfully parsed. +bool ParseArg(va_list* args, const CheckArgType** fmt, std::string* s) { + if (**fmt == CheckArgType::kEnd) + return false; + + switch (**fmt) { + case CheckArgType::kInt: + AppendFormat(s, "%d", va_arg(*args, int)); + break; + case CheckArgType::kLong: + AppendFormat(s, "%ld", va_arg(*args, long)); + break; + case CheckArgType::kLongLong: + AppendFormat(s, "%lld", va_arg(*args, long long)); + break; + case CheckArgType::kUInt: + AppendFormat(s, "%u", va_arg(*args, unsigned)); + break; + case CheckArgType::kULong: + AppendFormat(s, "%lu", va_arg(*args, unsigned long)); + break; + case CheckArgType::kULongLong: + AppendFormat(s, "%llu", va_arg(*args, unsigned long long)); + break; + case CheckArgType::kDouble: + AppendFormat(s, "%g", va_arg(*args, double)); + break; + case CheckArgType::kLongDouble: + AppendFormat(s, "%Lg", va_arg(*args, long double)); + break; + case CheckArgType::kCharP: + s->append(va_arg(*args, const char*)); + break; + case CheckArgType::kStdString: + s->append(*va_arg(*args, const std::string*)); + break; + case CheckArgType::kStringView: { + const absl::string_view sv = *va_arg(*args, const absl::string_view*); + s->append(sv.data(), sv.size()); + break; + } + case CheckArgType::kVoidP: + AppendFormat(s, "%p", va_arg(*args, const void*)); + break; + default: + s->append("[Invalid CheckArgType]"); + return false; + } + (*fmt)++; + return true; +} + +RTC_NORETURN void FatalLog(const char* file, + int line, + const char* message, + const CheckArgType* fmt, + ...) { + va_list args; + va_start(args, fmt); + + std::string s; + AppendFormat(&s, + "\n\n" + "#\n" + "# Fatal error in: %s, line %d\n" + "# last system error: %u\n" + "# Check failed: %s", + file, line, LAST_SYSTEM_ERROR, message); + + if (*fmt == CheckArgType::kCheckOp) { + // This log message was generated by RTC_CHECK_OP, so we have to complete + // the error message using the operands that have been passed as the first + // two arguments. + fmt++; + + std::string s1, s2; + if (ParseArg(&args, &fmt, &s1) && ParseArg(&args, &fmt, &s2)) + AppendFormat(&s, " (%s vs. %s)\n# ", s1.c_str(), s2.c_str()); + } else { + s.append("\n# "); + } + + // Append all the user-supplied arguments to the message. + while (ParseArg(&args, &fmt, &s)) + ; + + va_end(args); + + WriteFatalLog(file, line, s); +} +#else // RTC_CHECK_MSG_ENABLED +RTC_NORETURN void FatalLog(const char* file, int line) { + std::string s; + AppendFormat(&s, + "\n\n" + "#\n" + "# Fatal error in: %s, line %d\n" + "# last system error: %u\n" + "# Check failed.\n" + "# ", + file, line, LAST_SYSTEM_ERROR); + WriteFatalLog(file, line, s); +} +#endif // RTC_CHECK_MSG_ENABLED + +#if RTC_DCHECK_IS_ON + +RTC_NORETURN void UnreachableCodeReached(const char* file, int line) { + std::string s; + AppendFormat(&s, + "\n\n" + "#\n" + "# Unreachable code reached: %s, line %d\n" + "# last system error: %u\n" + "# ", + file, line, LAST_SYSTEM_ERROR); + WriteFatalLog(file, line, s); +} + +#else // !RTC_DCHECK_IS_ON + +RTC_NORETURN void UnreachableCodeReached() { + std::string s; + AppendFormat(&s, + "\n\n" + "#\n" + "# Unreachable code reached (file and line unknown)\n" + "# last system error: %u\n" + "# ", + LAST_SYSTEM_ERROR); + WriteFatalLog(s); +} + +#endif // !RTC_DCHECK_IS_ON + +} // namespace webrtc_checks_impl +} // namespace webrtc + +// Function to call from the C version of the RTC_CHECK and RTC_DCHECK macros. +RTC_NORETURN void rtc_FatalMessage(const char* file, + int line, + const char* msg) { +#if RTC_CHECK_MSG_ENABLED + static constexpr webrtc::webrtc_checks_impl::CheckArgType t[] = { + webrtc::webrtc_checks_impl::CheckArgType::kEnd}; + webrtc::webrtc_checks_impl::FatalLog(file, line, msg, t); +#else + webrtc::webrtc_checks_impl::FatalLog(file, line); +#endif +} diff --git a/pkg/apm/webrtc/rtc_base/checks.h b/pkg/apm/webrtc/rtc_base/checks.h new file mode 100644 index 00000000..91414807 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/checks.h @@ -0,0 +1,537 @@ +/* + * Copyright 2006 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_CHECKS_H_ +#define RTC_BASE_CHECKS_H_ + +// If you for some reason need to know if DCHECKs are on, test the value of +// RTC_DCHECK_IS_ON. (Test its value, not if it's defined; it'll always be +// defined, to either a true or a false value.) +#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +#define RTC_DCHECK_IS_ON 1 +#else +#define RTC_DCHECK_IS_ON 0 +#endif + +// Annotate a function that will not return control flow to the caller. +#if defined(_MSC_VER) +#define RTC_NORETURN __declspec(noreturn) +#elif defined(__GNUC__) +#define RTC_NORETURN __attribute__((__noreturn__)) +#else +#define RTC_NORETURN +#endif + +#ifdef __cplusplus +extern "C" { +#endif +RTC_NORETURN void rtc_FatalMessage(const char* file, int line, const char* msg); +#ifdef __cplusplus +} // extern "C" +#endif + +#ifdef RTC_DISABLE_CHECK_MSG +#define RTC_CHECK_MSG_ENABLED 0 +#else +#define RTC_CHECK_MSG_ENABLED 1 +#endif + +#if RTC_CHECK_MSG_ENABLED +#define RTC_CHECK_EVAL_MESSAGE(message) message +#else +#define RTC_CHECK_EVAL_MESSAGE(message) "" +#endif + +#ifdef __cplusplus +// C++ version. + +#include +#include +#include + +#include "absl/strings/has_absl_stringify.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "api/scoped_refptr.h" +#include "rtc_base/numerics/safe_compare.h" +#include "rtc_base/system/inline.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/type_traits.h" + +// The macros here print a message to stderr and abort under various +// conditions. All will accept additional stream messages. For example: +// RTC_DCHECK_EQ(foo, bar) << "I'm printed when foo != bar."; +// +// - RTC_CHECK(x) is an assertion that x is always true, and that if it isn't, +// it's better to terminate the process than to continue. During development, +// the reason that it's better to terminate might simply be that the error +// handling code isn't in place yet; in production, the reason might be that +// the author of the code truly believes that x will always be true, but that +// they recognizes that if they are wrong, abrupt and unpleasant process +// termination is still better than carrying on with the assumption violated. +// +// RTC_CHECK always evaluates its argument, so it's OK for x to have side +// effects. +// +// - RTC_DCHECK(x) is the same as RTC_CHECK(x)---an assertion that x is always +// true---except that x will only be evaluated in debug builds; in production +// builds, x is simply assumed to be true. This is useful if evaluating x is +// expensive and the expected cost of failing to detect the violated +// assumption is acceptable. You should not handle cases where a production +// build fails to spot a violated condition, even those that would result in +// crashes. If the code needs to cope with the error, make it cope, but don't +// call RTC_DCHECK; if the condition really can't occur, but you'd sleep +// better at night knowing that the process will suicide instead of carrying +// on in case you were wrong, use RTC_CHECK instead of RTC_DCHECK. +// +// RTC_DCHECK only evaluates its argument in debug builds, so if x has visible +// side effects, you need to write e.g. +// bool w = x; RTC_DCHECK(w); +// +// - RTC_CHECK_EQ, _NE, _GT, ..., and RTC_DCHECK_EQ, _NE, _GT, ... are +// specialized variants of RTC_CHECK and RTC_DCHECK that print prettier +// messages if the condition doesn't hold. Prefer them to raw RTC_CHECK and +// RTC_DCHECK. +// +// - RTC_FATAL() aborts unconditionally. + +// TODO(bugs.webrtc.org/42232595): Remove this macro once Chrome has migrated. +#define RTC_CHECKS_IN_WEBRTC_NAMESPACE 1 + +namespace webrtc { +namespace webrtc_checks_impl { +enum class CheckArgType : int8_t { + kEnd = 0, + kInt, + kLong, + kLongLong, + kUInt, + kULong, + kULongLong, + kDouble, + kLongDouble, + kCharP, + kStdString, + kStringView, + kVoidP, + + // kCheckOp doesn't represent an argument type. Instead, it is sent as the + // first argument from RTC_CHECK_OP to make FatalLog use the next two + // arguments to build the special CHECK_OP error message + // (the "a == b (1 vs. 2)" bit). + kCheckOp, +}; + +// These two functions are public so they can be overridden from +// webrtc_overrides in chromium. +RTC_NORETURN void WriteFatalLog(const char* file, + int line, + absl::string_view output); +RTC_NORETURN void WriteFatalLog(absl::string_view output); + +#if RTC_CHECK_MSG_ENABLED +RTC_NORETURN RTC_EXPORT void FatalLog(const char* file, + int line, + const char* message, + const CheckArgType* fmt, + ...); +#else +RTC_NORETURN RTC_EXPORT void FatalLog(const char* file, int line); +#endif + +// Wrapper for log arguments. Only ever make values of this type with the +// MakeVal() functions. +template +struct Val { + static constexpr CheckArgType Type() { return N; } + T GetVal() const { return val; } + T val; +}; + +// Case for when we need to construct a temp string and then print that. +// (We can't use Val +// because we need somewhere to store the temp string.) +struct ToStringVal { + static constexpr CheckArgType Type() { return CheckArgType::kStdString; } + const std::string* GetVal() const { return &val; } + std::string val; +}; + +inline Val MakeVal(int x) { + return {x}; +} +inline Val MakeVal(long x) { + return {x}; +} +inline Val MakeVal(long long x) { + return {x}; +} +inline Val MakeVal(unsigned int x) { + return {x}; +} +inline Val MakeVal(unsigned long x) { + return {x}; +} +inline Val MakeVal( + unsigned long long x) { + return {x}; +} + +inline Val MakeVal(double x) { + return {x}; +} +inline Val MakeVal(long double x) { + return {x}; +} + +inline Val MakeVal(const char* x) { + return {x}; +} +inline Val MakeVal( + const std::string& x) { + return {&x}; +} +inline Val MakeVal( + const absl::string_view& x) { + return {&x}; +} + +inline Val MakeVal(const void* x) { + return {x}; +} + +template +inline Val MakeVal( + const webrtc::scoped_refptr& p) { + return {p.get()}; +} + +// The enum class types are not implicitly convertible to arithmetic types. +template ::value && + !absl::HasAbslStringify::value && + !std::is_arithmetic::value>* = nullptr> +inline decltype(MakeVal(std::declval>())) MakeVal( + T x) { + return {static_cast>(x)}; +} + +template ::value>* = nullptr> +ToStringVal MakeVal(const T& x) { + return {absl::StrCat(x)}; +} + +// Ephemeral type that represents the result of the logging << operator. +template +class LogStreamer; + +// Base case: Before the first << argument. +template <> +class LogStreamer<> final { + public: + template ())), + std::enable_if_t::value || + std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(U arg) const { + return LogStreamer(MakeVal(arg), this); + } + + template ())), + std::enable_if_t::value && + !std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(const U& arg) const { + return LogStreamer(MakeVal(arg), this); + } + +#if RTC_CHECK_MSG_ENABLED + template + RTC_NORETURN RTC_FORCE_INLINE static void Call(const char* file, + const int line, + const char* message, + const Us&... args) { + static constexpr CheckArgType t[] = {Us::Type()..., CheckArgType::kEnd}; + FatalLog(file, line, message, t, args.GetVal()...); + } + + template + RTC_NORETURN RTC_FORCE_INLINE static void CallCheckOp(const char* file, + const int line, + const char* message, + const Us&... args) { + static constexpr CheckArgType t[] = {CheckArgType::kCheckOp, Us::Type()..., + CheckArgType::kEnd}; + FatalLog(file, line, message, t, args.GetVal()...); + } +#else + template + RTC_NORETURN RTC_FORCE_INLINE static void Call(const char* file, + const int line) { + FatalLog(file, line); + } +#endif +}; + +// Inductive case: We've already seen at least one << argument. The most recent +// one had type `T`, and the earlier ones had types `Ts`. +template +class LogStreamer final { + public: + RTC_FORCE_INLINE LogStreamer(T arg, const LogStreamer* prior) + : arg_(arg), prior_(prior) {} + + template ())), + std::enable_if_t::value || + std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(U arg) const { + return LogStreamer(MakeVal(arg), this); + } + + template ())), + std::enable_if_t::value && + !std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(const U& arg) const { + return LogStreamer(MakeVal(arg), this); + } + +#if RTC_CHECK_MSG_ENABLED + template + RTC_NORETURN RTC_FORCE_INLINE void Call(const char* file, + const int line, + const char* message, + const Us&... args) const { + prior_->Call(file, line, message, arg_, args...); + } + + template + RTC_NORETURN RTC_FORCE_INLINE void CallCheckOp(const char* file, + const int line, + const char* message, + const Us&... args) const { + prior_->CallCheckOp(file, line, message, arg_, args...); + } +#else + template + RTC_NORETURN RTC_FORCE_INLINE void Call(const char* file, + const int line) const { + prior_->Call(file, line); + } +#endif + + private: + // The most recent argument. + T arg_; + + // Earlier arguments. + const LogStreamer* prior_; +}; + +template +class FatalLogCall final { + public: + FatalLogCall(const char* file, int line, const char* message) + : file_(file), line_(line), message_(message) {} + + // This can be any binary operator with precedence lower than <<. + template + RTC_NORETURN RTC_FORCE_INLINE void operator&( + const LogStreamer& streamer) { +#if RTC_CHECK_MSG_ENABLED + isCheckOp ? streamer.CallCheckOp(file_, line_, message_) + : streamer.Call(file_, line_, message_); +#else + streamer.Call(file_, line_); +#endif + } + + private: + const char* file_; + int line_; + const char* message_; +}; + +#if RTC_DCHECK_IS_ON + +// Be helpful, and include file and line in the RTC_CHECK_NOTREACHED error +// message. +#define RTC_UNREACHABLE_FILE_AND_LINE_CALL_ARGS __FILE__, __LINE__ +RTC_NORETURN RTC_EXPORT void UnreachableCodeReached(const char* file, int line); + +#else + +// Be mindful of binary size, and don't include file and line in the +// RTC_CHECK_NOTREACHED error message. +#define RTC_UNREACHABLE_FILE_AND_LINE_CALL_ARGS +RTC_NORETURN RTC_EXPORT void UnreachableCodeReached(); + +#endif + +} // namespace webrtc_checks_impl + +// The actual stream used isn't important. We reference `ignored` in the code +// but don't evaluate it; this is to avoid "unused variable" warnings (we do so +// in a particularly convoluted way with an extra ?: because that appears to be +// the simplest construct that keeps Visual Studio from complaining about +// condition being unused). +#define RTC_EAT_STREAM_PARAMETERS(ignored) \ + (true ? true : ((void)(ignored), true)) \ + ? static_cast(0) \ + : ::webrtc::webrtc_checks_impl::FatalLogCall("", 0, "") & \ + ::webrtc::webrtc_checks_impl::LogStreamer<>() + +// Call RTC_EAT_STREAM_PARAMETERS with an argument that fails to compile if +// values of the same types as `a` and `b` can't be compared with the given +// operation, and that would evaluate `a` and `b` if evaluated. +#define RTC_EAT_STREAM_PARAMETERS_OP(op, a, b) \ + RTC_EAT_STREAM_PARAMETERS(((void)::webrtc::Safe##op(a, b))) + +// RTC_CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by NDEBUG or anything else, so the check will be executed +// regardless of compilation mode. +// +// We make sure RTC_CHECK et al. always evaluates `condition`, as +// doing RTC_CHECK(FunctionWithSideEffect()) is a common idiom. +// +// RTC_CHECK_OP is a helper macro for binary operators. +// Don't use this macro directly in your code, use RTC_CHECK_EQ et al below. +#if RTC_CHECK_MSG_ENABLED +#define RTC_CHECK(condition) \ + (condition) ? static_cast(0) \ + : ::webrtc::webrtc_checks_impl::FatalLogCall( \ + __FILE__, __LINE__, #condition) & \ + ::webrtc::webrtc_checks_impl::LogStreamer<>() + +#define RTC_CHECK_OP(name, op, val1, val2) \ + ::webrtc::Safe##name((val1), (val2)) \ + ? static_cast(0) \ + : ::webrtc::webrtc_checks_impl::FatalLogCall( \ + __FILE__, __LINE__, #val1 " " #op " " #val2) & \ + ::webrtc::webrtc_checks_impl::LogStreamer<>() << (val1) << (val2) +#else +#define RTC_CHECK(condition) \ + (condition) ? static_cast(0) \ + : true ? ::webrtc::webrtc_checks_impl::FatalLogCall(__FILE__, \ + __LINE__, "") & \ + ::webrtc::webrtc_checks_impl::LogStreamer<>() \ + : ::webrtc::webrtc_checks_impl::FatalLogCall("", 0, "") & \ + ::webrtc::webrtc_checks_impl::LogStreamer<>() + +#define RTC_CHECK_OP(name, op, val1, val2) \ + ::webrtc::Safe##name((val1), (val2)) ? static_cast(0) \ + : true ? ::webrtc::webrtc_checks_impl::FatalLogCall(__FILE__, \ + __LINE__, "") & \ + ::webrtc::webrtc_checks_impl::LogStreamer<>() \ + : ::webrtc::webrtc_checks_impl::FatalLogCall("", 0, "") & \ + ::webrtc::webrtc_checks_impl::LogStreamer<>() +#endif + +#define RTC_CHECK_EQ(val1, val2) RTC_CHECK_OP(Eq, ==, val1, val2) +#define RTC_CHECK_NE(val1, val2) RTC_CHECK_OP(Ne, !=, val1, val2) +#define RTC_CHECK_LE(val1, val2) RTC_CHECK_OP(Le, <=, val1, val2) +#define RTC_CHECK_LT(val1, val2) RTC_CHECK_OP(Lt, <, val1, val2) +#define RTC_CHECK_GE(val1, val2) RTC_CHECK_OP(Ge, >=, val1, val2) +#define RTC_CHECK_GT(val1, val2) RTC_CHECK_OP(Gt, >, val1, val2) + +// The RTC_DCHECK macro is equivalent to RTC_CHECK except that it only generates +// code in debug builds. It does reference the condition parameter in all cases, +// though, so callers won't risk getting warnings about unused variables. +#if RTC_DCHECK_IS_ON +#define RTC_DCHECK(condition) RTC_CHECK(condition) +#define RTC_DCHECK_EQ(v1, v2) RTC_CHECK_EQ(v1, v2) +#define RTC_DCHECK_NE(v1, v2) RTC_CHECK_NE(v1, v2) +#define RTC_DCHECK_LE(v1, v2) RTC_CHECK_LE(v1, v2) +#define RTC_DCHECK_LT(v1, v2) RTC_CHECK_LT(v1, v2) +#define RTC_DCHECK_GE(v1, v2) RTC_CHECK_GE(v1, v2) +#define RTC_DCHECK_GT(v1, v2) RTC_CHECK_GT(v1, v2) +#else +#define RTC_DCHECK(condition) RTC_EAT_STREAM_PARAMETERS(condition) +#define RTC_DCHECK_EQ(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Eq, v1, v2) +#define RTC_DCHECK_NE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Ne, v1, v2) +#define RTC_DCHECK_LE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Le, v1, v2) +#define RTC_DCHECK_LT(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Lt, v1, v2) +#define RTC_DCHECK_GE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Ge, v1, v2) +#define RTC_DCHECK_GT(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Gt, v1, v2) +#endif + +#define RTC_UNREACHABLE_CODE_HIT false +#define RTC_DCHECK_NOTREACHED() RTC_DCHECK(RTC_UNREACHABLE_CODE_HIT) + +// Kills the process with an error message. Never returns. Use when you wish to +// assert that a point in the code is never reached. +#define RTC_CHECK_NOTREACHED() \ + do { \ + ::webrtc::webrtc_checks_impl::UnreachableCodeReached( \ + RTC_UNREACHABLE_FILE_AND_LINE_CALL_ARGS); \ + } while (0) + +#define RTC_FATAL() \ + ::webrtc::webrtc_checks_impl::FatalLogCall(__FILE__, __LINE__, \ + "FATAL()") & \ + ::webrtc::webrtc_checks_impl::LogStreamer<>() + +// Performs the integer division a/b and returns the result. CHECKs that the +// remainder is zero. +template +inline T CheckedDivExact(T a, T b) { + RTC_CHECK_EQ(a % b, 0) << a << " is not evenly divisible by " << b; + return a / b; +} + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::CheckedDivExact; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#else // __cplusplus not defined +// C version. Lacks many features compared to the C++ version, but usage +// guidelines are the same. + +#define RTC_CHECK(condition) \ + do { \ + if (!(condition)) { \ + rtc_FatalMessage(__FILE__, __LINE__, \ + RTC_CHECK_EVAL_MESSAGE("CHECK failed: " #condition)); \ + } \ + } while (0) + +#define RTC_CHECK_EQ(a, b) RTC_CHECK((a) == (b)) +#define RTC_CHECK_NE(a, b) RTC_CHECK((a) != (b)) +#define RTC_CHECK_LE(a, b) RTC_CHECK((a) <= (b)) +#define RTC_CHECK_LT(a, b) RTC_CHECK((a) < (b)) +#define RTC_CHECK_GE(a, b) RTC_CHECK((a) >= (b)) +#define RTC_CHECK_GT(a, b) RTC_CHECK((a) > (b)) + +#define RTC_DCHECK(condition) \ + do { \ + if (RTC_DCHECK_IS_ON && !(condition)) { \ + rtc_FatalMessage(__FILE__, __LINE__, \ + RTC_CHECK_EVAL_MESSAGE("DCHECK failed: " #condition)); \ + } \ + } while (0) + +#define RTC_DCHECK_EQ(a, b) RTC_DCHECK((a) == (b)) +#define RTC_DCHECK_NE(a, b) RTC_DCHECK((a) != (b)) +#define RTC_DCHECK_LE(a, b) RTC_DCHECK((a) <= (b)) +#define RTC_DCHECK_LT(a, b) RTC_DCHECK((a) < (b)) +#define RTC_DCHECK_GE(a, b) RTC_DCHECK((a) >= (b)) +#define RTC_DCHECK_GT(a, b) RTC_DCHECK((a) > (b)) + +#endif // __cplusplus + +#endif // RTC_BASE_CHECKS_H_ diff --git a/pkg/apm/webrtc/rtc_base/compile_assert_c.h b/pkg/apm/webrtc/rtc_base/compile_assert_c.h new file mode 100644 index 00000000..db2e4a81 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/compile_assert_c.h @@ -0,0 +1,25 @@ +/* + * 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 RTC_BASE_COMPILE_ASSERT_C_H_ +#define RTC_BASE_COMPILE_ASSERT_C_H_ + +// Use this macro to verify at compile time that certain restrictions are met. +// The argument is the boolean expression to evaluate. +// Example: +// RTC_COMPILE_ASSERT(sizeof(foo) < 128); +// Note: In C++, use static_assert instead! +#define RTC_COMPILE_ASSERT(expression) \ + switch (0) { \ + case 0: \ + case expression:; \ + } + +#endif // RTC_BASE_COMPILE_ASSERT_C_H_ diff --git a/pkg/apm/webrtc/rtc_base/containers/containers.go b/pkg/apm/webrtc/rtc_base/containers/containers.go new file mode 100644 index 00000000..c9a22c95 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/containers/containers.go @@ -0,0 +1,10 @@ +//go:build console + +package containers + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/rtc_base/containers/flat_map.h b/pkg/apm/webrtc/rtc_base/containers/flat_map.h new file mode 100644 index 00000000..d1f757f6 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/containers/flat_map.h @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This implementation is borrowed from Chromium. + +#ifndef RTC_BASE_CONTAINERS_FLAT_MAP_H_ +#define RTC_BASE_CONTAINERS_FLAT_MAP_H_ + +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/containers/flat_tree.h" // IWYU pragma: export + +namespace webrtc { + +namespace flat_containers_internal { + +// An implementation of the flat_tree GetKeyFromValue template parameter that +// extracts the key as the first element of a pair. +struct GetFirst { + template + constexpr const Key& operator()(const std::pair& p) const { + return p.first; + } +}; + +} // namespace flat_containers_internal + +// flat_map is a container with a std::map-like interface that stores its +// contents in a sorted container, by default a vector. +// +// Its implementation mostly tracks the corresponding standardization proposal +// https://wg21.link/P0429, except that the storage of keys and values is not +// split. +// +// PROS +// +// - Good memory locality. +// - Low overhead, especially for smaller maps. +// - Performance is good for more workloads than you might expect (see +// //base/containers/README.md in Chromium repository) +// - Supports C++14 map interface. +// +// CONS +// +// - Inserts and removals are O(n). +// +// IMPORTANT NOTES +// +// - Iterators are invalidated across mutations. This means that the following +// line of code has undefined behavior since adding a new element could +// resize the container, invalidating all iterators: +// container["new element"] = it.second; +// - If possible, construct a flat_map in one operation by inserting into +// a container and moving that container into the flat_map constructor. +// +// QUICK REFERENCE +// +// Most of the core functionality is inherited from flat_tree. Please see +// flat_tree.h for more details for most of these functions. As a quick +// reference, the functions available are: +// +// Constructors (inputs need not be sorted): +// flat_map(const flat_map&); +// flat_map(flat_map&&); +// flat_map(InputIterator first, InputIterator last, +// const Compare& compare = Compare()); +// flat_map(const container_type& items, +// const Compare& compare = Compare()); +// flat_map(container_type&& items, +// const Compare& compare = Compare()); // Re-use storage. +// flat_map(std::initializer_list ilist, +// const Compare& comp = Compare()); +// +// Constructors (inputs need to be sorted): +// flat_map(sorted_unique_t, +// InputIterator first, InputIterator last, +// const Compare& compare = Compare()); +// flat_map(sorted_unique_t, +// const container_type& items, +// const Compare& compare = Compare()); +// flat_map(sorted_unique_t, +// container_type&& items, +// const Compare& compare = Compare()); // Re-use storage. +// flat_map(sorted_unique_t, +// std::initializer_list ilist, +// const Compare& comp = Compare()); +// +// Assignment functions: +// flat_map& operator=(const flat_map&); +// flat_map& operator=(flat_map&&); +// flat_map& operator=(initializer_list); +// +// Memory management functions: +// void reserve(size_t); +// size_t capacity() const; +// void shrink_to_fit(); +// +// Size management functions: +// void clear(); +// size_t size() const; +// size_t max_size() const; +// bool empty() const; +// +// Iterator functions: +// iterator begin(); +// const_iterator begin() const; +// const_iterator cbegin() const; +// iterator end(); +// const_iterator end() const; +// const_iterator cend() const; +// reverse_iterator rbegin(); +// const reverse_iterator rbegin() const; +// const_reverse_iterator crbegin() const; +// reverse_iterator rend(); +// const_reverse_iterator rend() const; +// const_reverse_iterator crend() const; +// +// Insert and accessor functions: +// mapped_type& operator[](const key_type&); +// mapped_type& operator[](key_type&&); +// mapped_type& at(const K&); +// const mapped_type& at(const K&) const; +// pair insert(const value_type&); +// pair insert(value_type&&); +// iterator insert(const_iterator hint, const value_type&); +// iterator insert(const_iterator hint, value_type&&); +// void insert(InputIterator first, InputIterator last); +// pair insert_or_assign(K&&, M&&); +// iterator insert_or_assign(const_iterator hint, K&&, M&&); +// pair emplace(Args&&...); +// iterator emplace_hint(const_iterator, Args&&...); +// pair try_emplace(K&&, Args&&...); +// iterator try_emplace(const_iterator hint, K&&, Args&&...); + +// Underlying type functions: +// container_type extract() &&; +// void replace(container_type&&); +// +// Erase functions: +// iterator erase(iterator); +// iterator erase(const_iterator); +// iterator erase(const_iterator first, const_iterator& last); +// template size_t erase(const K& key); +// +// Comparators (see std::map documentation). +// key_compare key_comp() const; +// value_compare value_comp() const; +// +// Search functions: +// template size_t count(const K&) const; +// template iterator find(const K&); +// template const_iterator find(const K&) const; +// template bool contains(const K&) const; +// template pair equal_range(const K&); +// template iterator lower_bound(const K&); +// template const_iterator lower_bound(const K&) const; +// template iterator upper_bound(const K&); +// template const_iterator upper_bound(const K&) const; +// +// General functions: +// void swap(flat_map&); +// +// Non-member operators: +// bool operator==(const flat_map&, const flat_map); +// bool operator!=(const flat_map&, const flat_map); +// bool operator<(const flat_map&, const flat_map); +// bool operator>(const flat_map&, const flat_map); +// bool operator>=(const flat_map&, const flat_map); +// bool operator<=(const flat_map&, const flat_map); +// +template , + class Container = std::vector>> +class flat_map : public ::webrtc::flat_containers_internal::flat_tree< + Key, + flat_containers_internal::GetFirst, + Compare, + Container> { + private: + using tree = typename ::webrtc::flat_containers_internal:: + flat_tree; + + public: + using key_type = typename tree::key_type; + using mapped_type = Mapped; + using value_type = typename tree::value_type; + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + using iterator = typename tree::iterator; + using const_iterator = typename tree::const_iterator; + using reverse_iterator = typename tree::reverse_iterator; + using const_reverse_iterator = typename tree::const_reverse_iterator; + using container_type = typename tree::container_type; + + // -------------------------------------------------------------------------- + // Lifetime and assignments. + // + // Note: we explicitly bring operator= in because otherwise + // flat_map<...> x; + // x = {...}; + // Would first create a flat_map and then move assign it. This most likely + // would be optimized away but still affects our debug builds. + + using tree::tree; + using tree::operator=; + + // Out-of-bound calls to at() will CHECK. + template + mapped_type& at(const K& key); + template + const mapped_type& at(const K& key) const; + + // -------------------------------------------------------------------------- + // Map-specific insert operations. + // + // Normal insert() functions are inherited from flat_tree. + // + // Assume that every operation invalidates iterators and references. + // Insertion of one element can take O(size). + + mapped_type& operator[](const key_type& key); + mapped_type& operator[](key_type&& key); + + template + std::pair insert_or_assign(K&& key, M&& obj); + template + iterator insert_or_assign(const_iterator hint, K&& key, M&& obj); + + template + std::enable_if_t::value, + std::pair> + try_emplace(K&& key, Args&&... args); + + template + std::enable_if_t::value, iterator> + try_emplace(const_iterator hint, K&& key, Args&&... args); + + // -------------------------------------------------------------------------- + // General operations. + // + // Assume that swap invalidates iterators and references. + + void swap(flat_map& other) noexcept; + + friend void swap(flat_map& lhs, flat_map& rhs) noexcept { lhs.swap(rhs); } +}; + +// ---------------------------------------------------------------------------- +// Lookups. + +template +template +auto flat_map::at(const K& key) + -> mapped_type& { + iterator found = tree::find(key); + RTC_CHECK(found != tree::end()); + return found->second; +} + +template +template +auto flat_map::at(const K& key) const + -> const mapped_type& { + const_iterator found = tree::find(key); + RTC_CHECK(found != tree::cend()); + return found->second; +} + +// ---------------------------------------------------------------------------- +// Insert operations. + +template +auto flat_map::operator[](const key_type& key) + -> mapped_type& { + iterator found = tree::lower_bound(key); + if (found == tree::end() || tree::key_comp()(key, found->first)) + found = tree::unsafe_emplace(found, key, mapped_type()); + return found->second; +} + +template +auto flat_map::operator[](key_type&& key) + -> mapped_type& { + iterator found = tree::lower_bound(key); + if (found == tree::end() || tree::key_comp()(key, found->first)) + found = tree::unsafe_emplace(found, std::move(key), mapped_type()); + return found->second; +} + +template +template +auto flat_map::insert_or_assign(K&& key, + M&& obj) + -> std::pair { + auto result = + tree::emplace_key_args(key, std::forward(key), std::forward(obj)); + if (!result.second) + result.first->second = std::forward(obj); + return result; +} + +template +template +auto flat_map::insert_or_assign( + const_iterator hint, + K&& key, + M&& obj) -> iterator { + auto result = tree::emplace_hint_key_args(hint, key, std::forward(key), + std::forward(obj)); + if (!result.second) + result.first->second = std::forward(obj); + return result.first; +} + +template +template +auto flat_map::try_emplace(K&& key, + Args&&... args) + -> std::enable_if_t::value, + std::pair> { + return tree::emplace_key_args( + key, std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(args)...)); +} + +template +template +auto flat_map::try_emplace(const_iterator hint, + K&& key, + Args&&... args) + -> std::enable_if_t::value, iterator> { + return tree::emplace_hint_key_args( + hint, key, std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(args)...)) + .first; +} + +// ---------------------------------------------------------------------------- +// General operations. + +template +void flat_map::swap(flat_map& other) noexcept { + tree::swap(other); +} + +// Erases all elements that match predicate. It has O(size) complexity. +// +// flat_map last_times; +// ... +// EraseIf(last_times, +// [&](const auto& element) { return now - element.second > kLimit; }); + +// NOLINTNEXTLINE(misc-unused-using-decls) +using ::webrtc::flat_containers_internal::EraseIf; + +} // namespace webrtc + +#endif // RTC_BASE_CONTAINERS_FLAT_MAP_H_ diff --git a/pkg/apm/webrtc/rtc_base/containers/flat_set.h b/pkg/apm/webrtc/rtc_base/containers/flat_set.h new file mode 100644 index 00000000..355690b0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/containers/flat_set.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This implementation is borrowed from Chromium. + +#ifndef RTC_BASE_CONTAINERS_FLAT_SET_H_ +#define RTC_BASE_CONTAINERS_FLAT_SET_H_ + +#include +#include + +#include "rtc_base/containers/flat_tree.h" // IWYU pragma: export +#include "rtc_base/containers/identity.h" + +namespace webrtc { + +// flat_set is a container with a std::set-like interface that stores its +// contents in a sorted container, by default a vector. +// +// Its implementation mostly tracks the corresponding standardization proposal +// https://wg21.link/P1222. +// +// +// PROS +// +// - Good memory locality. +// - Low overhead, especially for smaller sets. +// - Performance is good for more workloads than you might expect (see +// //base/containers/README.md in Chromium repository) +// - Supports C++14 set interface. +// +// CONS +// +// - Inserts and removals are O(n). +// +// IMPORTANT NOTES +// +// - Iterators are invalidated across mutations. +// - If possible, construct a flat_set in one operation by inserting into +// a container and moving that container into the flat_set constructor. +// - For multiple removals use base::EraseIf() which is O(n) rather than +// O(n * removed_items). +// +// QUICK REFERENCE +// +// Most of the core functionality is inherited from flat_tree. Please see +// flat_tree.h for more details for most of these functions. As a quick +// reference, the functions available are: +// +// Constructors (inputs need not be sorted): +// flat_set(const flat_set&); +// flat_set(flat_set&&); +// flat_set(InputIterator first, InputIterator last, +// const Compare& compare = Compare()); +// flat_set(const container_type& items, +// const Compare& compare = Compare()); +// flat_set(container_type&& items, +// const Compare& compare = Compare()); // Re-use storage. +// flat_set(std::initializer_list ilist, +// const Compare& comp = Compare()); +// +// Constructors (inputs need to be sorted): +// flat_set(sorted_unique_t, +// InputIterator first, InputIterator last, +// const Compare& compare = Compare()); +// flat_set(sorted_unique_t, +// const container_type& items, +// const Compare& compare = Compare()); +// flat_set(sorted_unique_t, +// container_type&& items, +// const Compare& compare = Compare()); // Re-use storage. +// flat_set(sorted_unique_t, +// std::initializer_list ilist, +// const Compare& comp = Compare()); +// +// Assignment functions: +// flat_set& operator=(const flat_set&); +// flat_set& operator=(flat_set&&); +// flat_set& operator=(initializer_list); +// +// Memory management functions: +// void reserve(size_t); +// size_t capacity() const; +// void shrink_to_fit(); +// +// Size management functions: +// void clear(); +// size_t size() const; +// size_t max_size() const; +// bool empty() const; +// +// Iterator functions: +// iterator begin(); +// const_iterator begin() const; +// const_iterator cbegin() const; +// iterator end(); +// const_iterator end() const; +// const_iterator cend() const; +// reverse_iterator rbegin(); +// const reverse_iterator rbegin() const; +// const_reverse_iterator crbegin() const; +// reverse_iterator rend(); +// const_reverse_iterator rend() const; +// const_reverse_iterator crend() const; +// +// Insert and accessor functions: +// pair insert(const key_type&); +// pair insert(key_type&&); +// void insert(InputIterator first, InputIterator last); +// iterator insert(const_iterator hint, const key_type&); +// iterator insert(const_iterator hint, key_type&&); +// pair emplace(Args&&...); +// iterator emplace_hint(const_iterator, Args&&...); +// +// Underlying type functions: +// container_type extract() &&; +// void replace(container_type&&); +// +// Erase functions: +// iterator erase(iterator); +// iterator erase(const_iterator); +// iterator erase(const_iterator first, const_iterator& last); +// template size_t erase(const K& key); +// +// Comparators (see std::set documentation). +// key_compare key_comp() const; +// value_compare value_comp() const; +// +// Search functions: +// template size_t count(const K&) const; +// template iterator find(const K&); +// template const_iterator find(const K&) const; +// template bool contains(const K&) const; +// template pair equal_range(K&); +// template iterator lower_bound(const K&); +// template const_iterator lower_bound(const K&) const; +// template iterator upper_bound(const K&); +// template const_iterator upper_bound(const K&) const; +// +// General functions: +// void swap(flat_set&); +// +// Non-member operators: +// bool operator==(const flat_set&, const flat_set); +// bool operator!=(const flat_set&, const flat_set); +// bool operator<(const flat_set&, const flat_set); +// bool operator>(const flat_set&, const flat_set); +// bool operator>=(const flat_set&, const flat_set); +// bool operator<=(const flat_set&, const flat_set); +// +template , + class Container = std::vector> +using flat_set = typename ::webrtc::flat_containers_internal:: + flat_tree; + +// ---------------------------------------------------------------------------- +// General operations. + +// Erases all elements that match predicate. It has O(size) complexity. +// +// flat_set numbers; +// ... +// EraseIf(numbers, [](int number) { return number % 2 == 1; }); + +// NOLINTNEXTLINE(misc-unused-using-decls) +using ::webrtc::flat_containers_internal::EraseIf; + +} // namespace webrtc + +#endif // RTC_BASE_CONTAINERS_FLAT_SET_H_ diff --git a/pkg/apm/webrtc/rtc_base/containers/flat_tree.cc b/pkg/apm/webrtc/rtc_base/containers/flat_tree.cc new file mode 100644 index 00000000..9e86db19 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/containers/flat_tree.cc @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This implementation is borrowed from Chromium. + +#include "rtc_base/containers/flat_tree.h" + +namespace webrtc { + +sorted_unique_t sorted_unique; + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/containers/flat_tree.h b/pkg/apm/webrtc/rtc_base/containers/flat_tree.h new file mode 100644 index 00000000..480784ce --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/containers/flat_tree.h @@ -0,0 +1,1099 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This implementation is borrowed from Chromium. + +#ifndef RTC_BASE_CONTAINERS_FLAT_TREE_H_ +#define RTC_BASE_CONTAINERS_FLAT_TREE_H_ + +#include +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/no_unique_address.h" + +namespace webrtc { +// Tag type that allows skipping the sort_and_unique step when constructing a +// flat_tree in case the underlying container is already sorted and has no +// duplicate elements. +struct sorted_unique_t { + constexpr sorted_unique_t() = default; +}; +extern sorted_unique_t sorted_unique; + +namespace flat_containers_internal { + +// Helper functions used in RTC_DCHECKs below to make sure that inputs tagged +// with sorted_unique are indeed sorted and unique. +template +constexpr bool is_sorted_and_unique(const Range& range, Comp comp) { + // Being unique implies that there are no adjacent elements that + // compare equal. So this checks that each element is strictly less + // than the element after it. + return absl::c_adjacent_find(range, std::not_fn(comp)) == std::end(range); +} + +// This is a convenience trait inheriting from std::true_type if Iterator is at +// least a ForwardIterator and thus supports multiple passes over a range. +template +using is_multipass = + std::is_base_of::iterator_category>; + +// Uses SFINAE to detect whether type has is_transparent member. +template +struct IsTransparentCompare : std::false_type {}; +template +struct IsTransparentCompare> + : std::true_type {}; + +// Helper inspired by C++20's std::to_array to convert a C-style array to a +// std::array. As opposed to the C++20 version this implementation does not +// provide an overload for rvalues and does not strip cv qualifers from the +// returned std::array::value_type. The returned value_type needs to be +// specified explicitly, allowing the construction of std::arrays with const +// elements. +// +// Reference: https://en.cppreference.com/w/cpp/container/array/to_array +template +constexpr std::array ToArrayImpl(const T (&data)[N], + std::index_sequence) { + return {{data[I]...}}; +} + +template +constexpr std::array ToArray(const T (&data)[N]) { + return ToArrayImpl(data, std::make_index_sequence()); +} + +// std::pair's operator= is not constexpr prior to C++20. Thus we need this +// small helper to invoke operator= on the .first and .second member explicitly. +template +constexpr void Assign(T& lhs, T&& rhs) { + lhs = std::move(rhs); +} + +template +constexpr void Assign(std::pair& lhs, std::pair&& rhs) { + Assign(lhs.first, std::move(rhs.first)); + Assign(lhs.second, std::move(rhs.second)); +} + +// constexpr swap implementation. std::swap is not constexpr prior to C++20. +template +constexpr void Swap(T& lhs, T& rhs) { + T tmp = std::move(lhs); + Assign(lhs, std::move(rhs)); + Assign(rhs, std::move(tmp)); +} + +// constexpr prev implementation. std::prev is not constexpr prior to C++17. +template +constexpr BidirIt Prev(BidirIt it) { + return --it; +} + +// constexpr next implementation. std::next is not constexpr prior to C++17. +template +constexpr InputIt Next(InputIt it) { + return ++it; +} + +// constexpr sort implementation. std::sort is not constexpr prior to C++20. +// While insertion sort has a quadratic worst case complexity, it was chosen +// because it has linear complexity for nearly sorted data, is stable, and +// simple to implement. +template +constexpr void InsertionSort(BidirIt first, BidirIt last, const Compare& comp) { + if (first == last) + return; + + for (auto it = Next(first); it != last; ++it) { + for (auto curr = it; curr != first && comp(*curr, *Prev(curr)); --curr) + Swap(*curr, *Prev(curr)); + } +} + +// Implementation ------------------------------------------------------------- + +// Implementation for the sorted associative flat_set and flat_map using a +// sorted vector as the backing store. Do not use directly. +// +// The use of "value" in this is like std::map uses, meaning it's the thing +// contained (in the case of map it's a pair). The Key is how +// things are looked up. In the case of a set, Key == Value. In the case of +// a map, the Key is a component of a Value. +// +// The helper class GetKeyFromValue provides the means to extract a key from a +// value for comparison purposes. It should implement: +// const Key& operator()(const Value&). +template +class flat_tree { + public: + // -------------------------------------------------------------------------- + // Types. + // + using key_type = Key; + using key_compare = KeyCompare; + using value_type = typename Container::value_type; + + // Wraps the templated key comparison to compare values. + struct value_compare { + constexpr bool operator()(const value_type& left, + const value_type& right) const { + GetKeyFromValue extractor; + return comp(extractor(left), extractor(right)); + } + + RTC_NO_UNIQUE_ADDRESS key_compare comp; + }; + + using pointer = typename Container::pointer; + using const_pointer = typename Container::const_pointer; + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using reverse_iterator = typename Container::reverse_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + using container_type = Container; + + // -------------------------------------------------------------------------- + // Lifetime. + // + // Constructors that take range guarantee O(N * log^2(N)) + O(N) complexity + // and take O(N * log(N)) + O(N) if extra memory is available (N is a range + // length). + // + // Assume that move constructors invalidate iterators and references. + // + // The constructors that take ranges, lists, and vectors do not require that + // the input be sorted. + // + // When passing the webrtc::sorted_unique tag as the first argument no sort + // and unique step takes places. This is useful if the underlying container + // already has the required properties. + + flat_tree() = default; + flat_tree(const flat_tree&) = default; + flat_tree(flat_tree&&) = default; + + explicit flat_tree(const key_compare& comp); + + template + flat_tree(InputIterator first, + InputIterator last, + const key_compare& comp = key_compare()); + + flat_tree(const container_type& items, + const key_compare& comp = key_compare()); + + explicit flat_tree(container_type&& items, + const key_compare& comp = key_compare()); + + flat_tree(std::initializer_list ilist, + const key_compare& comp = key_compare()); + + template + flat_tree(sorted_unique_t, + InputIterator first, + InputIterator last, + const key_compare& comp = key_compare()); + + flat_tree(sorted_unique_t, + const container_type& items, + const key_compare& comp = key_compare()); + + constexpr flat_tree(sorted_unique_t, + container_type&& items, + const key_compare& comp = key_compare()); + + flat_tree(sorted_unique_t, + std::initializer_list ilist, + const key_compare& comp = key_compare()); + + ~flat_tree() = default; + + // -------------------------------------------------------------------------- + // Assignments. + // + // Assume that move assignment invalidates iterators and references. + + flat_tree& operator=(const flat_tree&) = default; + flat_tree& operator=(flat_tree&&) = default; + // Takes the first if there are duplicates in the initializer list. + flat_tree& operator=(std::initializer_list ilist); + + // -------------------------------------------------------------------------- + // Memory management. + // + // Beware that shrink_to_fit() simply forwards the request to the + // container_type and its implementation is free to optimize otherwise and + // leave capacity() to be greater that its size. + // + // reserve() and shrink_to_fit() invalidate iterators and references. + + void reserve(size_type new_capacity); + size_type capacity() const; + void shrink_to_fit(); + + // -------------------------------------------------------------------------- + // Size management. + // + // clear() leaves the capacity() of the flat_tree unchanged. + + void clear(); + + constexpr size_type size() const; + constexpr size_type max_size() const; + constexpr bool empty() const; + + // -------------------------------------------------------------------------- + // Iterators. + // + // Iterators follow the ordering defined by the key comparator used in + // construction of the flat_tree. + + iterator begin(); + constexpr const_iterator begin() const; + const_iterator cbegin() const; + + iterator end(); + constexpr const_iterator end() const; + const_iterator cend() const; + + reverse_iterator rbegin(); + const_reverse_iterator rbegin() const; + const_reverse_iterator crbegin() const; + + reverse_iterator rend(); + const_reverse_iterator rend() const; + const_reverse_iterator crend() const; + + // -------------------------------------------------------------------------- + // Insert operations. + // + // Assume that every operation invalidates iterators and references. + // Insertion of one element can take O(size). Capacity of flat_tree grows in + // an implementation-defined manner. + // + // NOTE: Prefer to build a new flat_tree from a std::vector (or similar) + // instead of calling insert() repeatedly. + + std::pair insert(const value_type& val); + std::pair insert(value_type&& val); + + iterator insert(const_iterator position_hint, const value_type& x); + iterator insert(const_iterator position_hint, value_type&& x); + + // This method inserts the values from the range [first, last) into the + // current tree. + template + void insert(InputIterator first, InputIterator last); + + template + std::pair emplace(Args&&... args); + + template + iterator emplace_hint(const_iterator position_hint, Args&&... args); + + // -------------------------------------------------------------------------- + // Underlying type operations. + // + // Assume that either operation invalidates iterators and references. + + // Extracts the container_type and returns it to the caller. Ensures that + // `this` is `empty()` afterwards. + container_type extract() &&; + + // Replaces the container_type with `body`. Expects that `body` is sorted + // and has no repeated elements with regard to value_comp(). + void replace(container_type&& body); + + // -------------------------------------------------------------------------- + // Erase operations. + // + // Assume that every operation invalidates iterators and references. + // + // erase(position), erase(first, last) can take O(size). + // erase(key) may take O(size) + O(log(size)). + // + // Prefer webrtc::EraseIf() or some other variation on erase(remove(), end()) + // idiom when deleting multiple non-consecutive elements. + + iterator erase(iterator position); + // Artificially templatized to break ambiguity if `iterator` and + // `const_iterator` are the same type. + template + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + template + size_type erase(const K& key); + + // -------------------------------------------------------------------------- + // Comparators. + + constexpr key_compare key_comp() const; + constexpr value_compare value_comp() const; + + // -------------------------------------------------------------------------- + // Search operations. + // + // Search operations have O(log(size)) complexity. + + template + size_type count(const K& key) const; + + template + iterator find(const K& key); + + template + const_iterator find(const K& key) const; + + template + bool contains(const K& key) const; + + template + std::pair equal_range(const K& key); + + template + std::pair equal_range(const K& key) const; + + template + iterator lower_bound(const K& key); + + template + const_iterator lower_bound(const K& key) const; + + template + iterator upper_bound(const K& key); + + template + const_iterator upper_bound(const K& key) const; + + // -------------------------------------------------------------------------- + // General operations. + // + // Assume that swap invalidates iterators and references. + // + // Implementation note: currently we use operator==() and operator<() on + // std::vector, because they have the same contract we need, so we use them + // directly for brevity and in case it is more optimal than calling equal() + // and lexicograhpical_compare(). If the underlying container type is changed, + // this code may need to be modified. + + void swap(flat_tree& other) noexcept; + + friend bool operator==(const flat_tree& lhs, const flat_tree& rhs) { + return lhs.body_ == rhs.body_; + } + + friend bool operator!=(const flat_tree& lhs, const flat_tree& rhs) { + return !(lhs == rhs); + } + + friend bool operator<(const flat_tree& lhs, const flat_tree& rhs) { + return lhs.body_ < rhs.body_; + } + + friend bool operator>(const flat_tree& lhs, const flat_tree& rhs) { + return rhs < lhs; + } + + friend bool operator>=(const flat_tree& lhs, const flat_tree& rhs) { + return !(lhs < rhs); + } + + friend bool operator<=(const flat_tree& lhs, const flat_tree& rhs) { + return !(lhs > rhs); + } + + friend void swap(flat_tree& lhs, flat_tree& rhs) noexcept { lhs.swap(rhs); } + + protected: + // Emplaces a new item into the tree that is known not to be in it. This + // is for implementing map operator[]. + template + iterator unsafe_emplace(const_iterator position, Args&&... args); + + // Attempts to emplace a new element with key `key`. Only if `key` is not yet + // present, construct value_type from `args` and insert it. Returns an + // iterator to the element with key `key` and a bool indicating whether an + // insertion happened. + template + std::pair emplace_key_args(const K& key, Args&&... args); + + // Similar to `emplace_key_args`, but checks `hint` first as a possible + // insertion position. + template + std::pair emplace_hint_key_args(const_iterator hint, + const K& key, + Args&&... args); + + private: + // Helper class for e.g. lower_bound that can compare a value on the left + // to a key on the right. + struct KeyValueCompare { + // The key comparison object must outlive this class. + explicit KeyValueCompare(const key_compare& comp) : comp_(comp) {} + + template + bool operator()(const T& lhs, const U& rhs) const { + return comp_(extract_if_value_type(lhs), extract_if_value_type(rhs)); + } + + private: + const key_type& extract_if_value_type(const value_type& v) const { + GetKeyFromValue extractor; + return extractor(v); + } + + template + const K& extract_if_value_type(const K& k) const { + return k; + } + + const key_compare& comp_; + }; + + iterator const_cast_it(const_iterator c_it) { + auto distance = std::distance(cbegin(), c_it); + return std::next(begin(), distance); + } + + // This method is inspired by both std::map::insert(P&&) and + // std::map::insert_or_assign(const K&, V&&). It inserts val if an equivalent + // element is not present yet, otherwise it overwrites. It returns an iterator + // to the modified element and a flag indicating whether insertion or + // assignment happened. + template + std::pair insert_or_assign(V&& val) { + auto position = lower_bound(GetKeyFromValue()(val)); + + if (position == end() || value_comp()(val, *position)) + return {body_.emplace(position, std::forward(val)), true}; + + *position = std::forward(val); + return {position, false}; + } + + // This method is similar to insert_or_assign, with the following differences: + // - Instead of searching [begin(), end()) it only searches [first, last). + // - In case no equivalent element is found, val is appended to the end of the + // underlying body and an iterator to the next bigger element in [first, + // last) is returned. + template + std::pair append_or_assign(iterator first, + iterator last, + V&& val) { + auto position = std::lower_bound(first, last, val, value_comp()); + + if (position == last || value_comp()(val, *position)) { + // emplace_back might invalidate position, which is why distance needs to + // be cached. + const difference_type distance = std::distance(begin(), position); + body_.emplace_back(std::forward(val)); + return {std::next(begin(), distance), true}; + } + + *position = std::forward(val); + return {position, false}; + } + + // This method is similar to insert, with the following differences: + // - Instead of searching [begin(), end()) it only searches [first, last). + // - In case no equivalent element is found, val is appended to the end of the + // underlying body and an iterator to the next bigger element in [first, + // last) is returned. + template + std::pair append_unique(iterator first, + iterator last, + V&& val) { + auto position = std::lower_bound(first, last, val, value_comp()); + + if (position == last || value_comp()(val, *position)) { + // emplace_back might invalidate position, which is why distance needs to + // be cached. + const difference_type distance = std::distance(begin(), position); + body_.emplace_back(std::forward(val)); + return {std::next(begin(), distance), true}; + } + + return {position, false}; + } + + void sort_and_unique(iterator first, iterator last) { + // Preserve stability for the unique code below. + std::stable_sort(first, last, value_comp()); + + // lhs is already <= rhs due to sort, therefore !(lhs < rhs) <=> lhs == rhs. + auto equal_comp = std::not_fn(value_comp()); + erase(std::unique(first, last, equal_comp), last); + } + + void sort_and_unique() { sort_and_unique(begin(), end()); } + + // To support comparators that may not be possible to default-construct, we + // have to store an instance of Compare. Since Compare commonly is stateless, + // we use the RTC_NO_UNIQUE_ADDRESS attribute to save space. + RTC_NO_UNIQUE_ADDRESS key_compare comp_; + // Declare after `key_compare_comp_` to workaround GCC ICE. For details + // see https://crbug.com/1156268 + container_type body_; + + // If the compare is not transparent we want to construct key_type once. + template + using KeyTypeOrK = typename std:: + conditional::value, K, key_type>::type; +}; + +// ---------------------------------------------------------------------------- +// Lifetime. + +template +flat_tree::flat_tree( + const KeyCompare& comp) + : comp_(comp) {} + +template +template +flat_tree::flat_tree( + InputIterator first, + InputIterator last, + const KeyCompare& comp) + : comp_(comp), body_(first, last) { + sort_and_unique(); +} + +template +flat_tree::flat_tree( + const container_type& items, + const KeyCompare& comp) + : comp_(comp), body_(items) { + sort_and_unique(); +} + +template +flat_tree::flat_tree( + container_type&& items, + const KeyCompare& comp) + : comp_(comp), body_(std::move(items)) { + sort_and_unique(); +} + +template +flat_tree::flat_tree( + std::initializer_list ilist, + const KeyCompare& comp) + : flat_tree(std::begin(ilist), std::end(ilist), comp) {} + +template +template +flat_tree::flat_tree( + sorted_unique_t, + InputIterator first, + InputIterator last, + const KeyCompare& comp) + : comp_(comp), body_(first, last) { + RTC_DCHECK(is_sorted_and_unique(*this, value_comp())); +} + +template +flat_tree::flat_tree( + sorted_unique_t, + const container_type& items, + const KeyCompare& comp) + : comp_(comp), body_(items) { + RTC_DCHECK(is_sorted_and_unique(*this, value_comp())); +} + +template +constexpr flat_tree::flat_tree( + sorted_unique_t, + container_type&& items, + const KeyCompare& comp) + : comp_(comp), body_(std::move(items)) { + RTC_DCHECK(is_sorted_and_unique(*this, value_comp())); +} + +template +flat_tree::flat_tree( + sorted_unique_t, + std::initializer_list ilist, + const KeyCompare& comp) + : flat_tree(sorted_unique, std::begin(ilist), std::end(ilist), comp) {} + +// ---------------------------------------------------------------------------- +// Assignments. + +template +auto flat_tree::operator=( + std::initializer_list ilist) -> flat_tree& { + body_ = ilist; + sort_and_unique(); + return *this; +} + +// ---------------------------------------------------------------------------- +// Memory management. + +template +void flat_tree::reserve( + size_type new_capacity) { + body_.reserve(new_capacity); +} + +template +auto flat_tree::capacity() const + -> size_type { + return body_.capacity(); +} + +template +void flat_tree::shrink_to_fit() { + body_.shrink_to_fit(); +} + +// ---------------------------------------------------------------------------- +// Size management. + +template +void flat_tree::clear() { + body_.clear(); +} + +template +constexpr auto flat_tree::size() + const -> size_type { + return body_.size(); +} + +template +constexpr auto +flat_tree::max_size() const + -> size_type { + return body_.max_size(); +} + +template +constexpr bool flat_tree::empty() + const { + return body_.empty(); +} + +// ---------------------------------------------------------------------------- +// Iterators. + +template +auto flat_tree::begin() + -> iterator { + return body_.begin(); +} + +template +constexpr auto flat_tree::begin() + const -> const_iterator { + return std::begin(body_); +} + +template +auto flat_tree::cbegin() const + -> const_iterator { + return body_.cbegin(); +} + +template +auto flat_tree::end() -> iterator { + return body_.end(); +} + +template +constexpr auto flat_tree::end() + const -> const_iterator { + return std::end(body_); +} + +template +auto flat_tree::cend() const + -> const_iterator { + return body_.cend(); +} + +template +auto flat_tree::rbegin() + -> reverse_iterator { + return body_.rbegin(); +} + +template +auto flat_tree::rbegin() const + -> const_reverse_iterator { + return body_.rbegin(); +} + +template +auto flat_tree::crbegin() const + -> const_reverse_iterator { + return body_.crbegin(); +} + +template +auto flat_tree::rend() + -> reverse_iterator { + return body_.rend(); +} + +template +auto flat_tree::rend() const + -> const_reverse_iterator { + return body_.rend(); +} + +template +auto flat_tree::crend() const + -> const_reverse_iterator { + return body_.crend(); +} + +// ---------------------------------------------------------------------------- +// Insert operations. +// +// Currently we use position_hint the same way as eastl or boost: +// https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector_set.h#L493 + +template +auto flat_tree::insert( + const value_type& val) -> std::pair { + return emplace_key_args(GetKeyFromValue()(val), val); +} + +template +auto flat_tree::insert( + value_type&& val) -> std::pair { + return emplace_key_args(GetKeyFromValue()(val), std::move(val)); +} + +template +auto flat_tree::insert( + const_iterator position_hint, + const value_type& val) -> iterator { + return emplace_hint_key_args(position_hint, GetKeyFromValue()(val), val) + .first; +} + +template +auto flat_tree::insert( + const_iterator position_hint, + value_type&& val) -> iterator { + return emplace_hint_key_args(position_hint, GetKeyFromValue()(val), + std::move(val)) + .first; +} + +template +template +void flat_tree::insert( + InputIterator first, + InputIterator last) { + if (first == last) + return; + + // Dispatch to single element insert if the input range contains a single + // element. + if (is_multipass() && std::next(first) == last) { + insert(end(), *first); + return; + } + + // Provide a convenience lambda to obtain an iterator pointing past the last + // old element. This needs to be dymanic due to possible re-allocations. + auto middle = [this, size = size()] { return std::next(begin(), size); }; + + // For batch updates initialize the first insertion point. + difference_type pos_first_new = size(); + + // Loop over the input range while appending new values and overwriting + // existing ones, if applicable. Keep track of the first insertion point. + for (; first != last; ++first) { + std::pair result = append_unique(begin(), middle(), *first); + if (result.second) { + pos_first_new = + std::min(pos_first_new, std::distance(begin(), result.first)); + } + } + + // The new elements might be unordered and contain duplicates, so post-process + // the just inserted elements and merge them with the rest, inserting them at + // the previously found spot. + sort_and_unique(middle(), end()); + std::inplace_merge(std::next(begin(), pos_first_new), middle(), end(), + value_comp()); +} + +template +template +auto flat_tree::emplace( + Args&&... args) -> std::pair { + return insert(value_type(std::forward(args)...)); +} + +template +template +auto flat_tree::emplace_hint( + const_iterator position_hint, + Args&&... args) -> iterator { + return insert(position_hint, value_type(std::forward(args)...)); +} + +// ---------------------------------------------------------------------------- +// Underlying type operations. + +template +auto flat_tree:: + extract() && -> container_type { + return std::exchange(body_, container_type()); +} + +template +void flat_tree::replace( + container_type&& body) { + // Ensure that `body` is sorted and has no repeated elements according to + // `value_comp()`. + RTC_DCHECK(is_sorted_and_unique(body, value_comp())); + body_ = std::move(body); +} + +// ---------------------------------------------------------------------------- +// Erase operations. + +template +auto flat_tree::erase( + iterator position) -> iterator { + RTC_CHECK(position != body_.end()); + return body_.erase(position); +} + +template +template +auto flat_tree::erase( + const_iterator position) -> iterator { + RTC_CHECK(position != body_.end()); + return body_.erase(position); +} + +template +template +auto flat_tree::erase(const K& val) + -> size_type { + auto eq_range = equal_range(val); + auto res = std::distance(eq_range.first, eq_range.second); + erase(eq_range.first, eq_range.second); + return res; +} + +template +auto flat_tree::erase( + const_iterator first, + const_iterator last) -> iterator { + return body_.erase(first, last); +} + +// ---------------------------------------------------------------------------- +// Comparators. + +template +constexpr auto +flat_tree::key_comp() const + -> key_compare { + return comp_; +} + +template +constexpr auto +flat_tree::value_comp() const + -> value_compare { + return value_compare{comp_}; +} + +// ---------------------------------------------------------------------------- +// Search operations. + +template +template +auto flat_tree::count( + const K& key) const -> size_type { + auto eq_range = equal_range(key); + return std::distance(eq_range.first, eq_range.second); +} + +template +template +auto flat_tree::find(const K& key) + -> iterator { + return const_cast_it(std::as_const(*this).find(key)); +} + +template +template +auto flat_tree::find( + const K& key) const -> const_iterator { + auto eq_range = equal_range(key); + return (eq_range.first == eq_range.second) ? end() : eq_range.first; +} + +template +template +bool flat_tree::contains( + const K& key) const { + auto lower = lower_bound(key); + return lower != end() && !comp_(key, GetKeyFromValue()(*lower)); +} + +template +template +auto flat_tree::equal_range( + const K& key) -> std::pair { + auto res = std::as_const(*this).equal_range(key); + return {const_cast_it(res.first), const_cast_it(res.second)}; +} + +template +template +auto flat_tree::equal_range( + const K& key) const -> std::pair { + auto lower = lower_bound(key); + + KeyValueCompare comp(comp_); + if (lower == end() || comp(key, *lower)) + return {lower, lower}; + + return {lower, std::next(lower)}; +} + +template +template +auto flat_tree::lower_bound( + const K& key) -> iterator { + return const_cast_it(std::as_const(*this).lower_bound(key)); +} + +template +template +auto flat_tree::lower_bound( + const K& key) const -> const_iterator { + static_assert(std::is_convertible&, const K&>::value, + "Requested type cannot be bound to the container's key_type " + "which is required for a non-transparent compare."); + + const KeyTypeOrK& key_ref = key; + + KeyValueCompare comp(comp_); + return absl::c_lower_bound(*this, key_ref, comp); +} + +template +template +auto flat_tree::upper_bound( + const K& key) -> iterator { + return const_cast_it(std::as_const(*this).upper_bound(key)); +} + +template +template +auto flat_tree::upper_bound( + const K& key) const -> const_iterator { + static_assert(std::is_convertible&, const K&>::value, + "Requested type cannot be bound to the container's key_type " + "which is required for a non-transparent compare."); + + const KeyTypeOrK& key_ref = key; + + KeyValueCompare comp(comp_); + return absl::c_upper_bound(*this, key_ref, comp); +} + +// ---------------------------------------------------------------------------- +// General operations. + +template +void flat_tree::swap( + flat_tree& other) noexcept { + std::swap(*this, other); +} + +template +template +auto flat_tree::unsafe_emplace( + const_iterator position, + Args&&... args) -> iterator { + return body_.emplace(position, std::forward(args)...); +} + +template +template +auto flat_tree::emplace_key_args( + const K& key, + Args&&... args) -> std::pair { + auto lower = lower_bound(key); + if (lower == end() || comp_(key, GetKeyFromValue()(*lower))) + return {unsafe_emplace(lower, std::forward(args)...), true}; + return {lower, false}; +} + +template +template +auto flat_tree:: + emplace_hint_key_args(const_iterator hint, const K& key, Args&&... args) + -> std::pair { + KeyValueCompare comp(comp_); + if ((hint == begin() || comp(*std::prev(hint), key))) { + if (hint == end() || comp(key, *hint)) { + // *(hint - 1) < key < *hint => key did not exist and hint is correct. + return {unsafe_emplace(hint, std::forward(args)...), true}; + } + if (!comp(*hint, key)) { + // key == *hint => no-op, return correct hint. + return {const_cast_it(hint), false}; + } + } + // hint was not helpful, dispatch to hintless version. + return emplace_key_args(key, std::forward(args)...); +} + +// ---------------------------------------------------------------------------- +// Free functions. + +// Erases all elements that match predicate. It has O(size) complexity. +template +size_t EraseIf( + webrtc::flat_containers_internal:: + flat_tree& container, + Predicate pred) { + auto it = std::remove_if(container.begin(), container.end(), + std::forward(pred)); + size_t removed = std::distance(it, container.end()); + container.erase(it, container.end()); + return removed; +} + +} // namespace flat_containers_internal +} // namespace webrtc + +#endif // RTC_BASE_CONTAINERS_FLAT_TREE_H_ diff --git a/pkg/apm/webrtc/rtc_base/containers/identity.h b/pkg/apm/webrtc/rtc_base/containers/identity.h new file mode 100644 index 00000000..29592931 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/containers/identity.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This implementation is borrowed from Chromium. + +#ifndef RTC_BASE_CONTAINERS_IDENTITY_H_ +#define RTC_BASE_CONTAINERS_IDENTITY_H_ + +#include + +namespace webrtc { + +// Implementation of C++20's std::identity. +// +// Reference: +// - https://en.cppreference.com/w/cpp/utility/functional/identity +// - https://wg21.link/func.identity +struct identity { + template + constexpr T&& operator()(T&& t) const noexcept { + return std::forward(t); + } + + using is_transparent = void; +}; + +} // namespace webrtc + +#endif // RTC_BASE_CONTAINERS_IDENTITY_H_ diff --git a/pkg/apm/webrtc/rtc_base/containers/invoke.h b/pkg/apm/webrtc/rtc_base/containers/invoke.h new file mode 100644 index 00000000..5d17a70b --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/containers/invoke.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This implementation is borrowed from Chromium. + +#ifndef RTC_BASE_CONTAINERS_INVOKE_H_ +#define RTC_BASE_CONTAINERS_INVOKE_H_ + +#include +#include + +namespace webrtc { + +namespace invoke_internal { + +// Helper struct and alias to deduce the class type from a member function +// pointer or member object pointer. +template +struct member_pointer_class {}; + +template +struct member_pointer_class { + using type = ClassT; +}; + +template +using member_pointer_class_t = typename member_pointer_class::type; + +// Utility struct to detect specializations of std::reference_wrapper. +template +struct is_reference_wrapper : std::false_type {}; + +template +struct is_reference_wrapper> : std::true_type {}; + +// Small helpers used below in invoke_internal::invoke to make the SFINAE more +// concise. +template +const bool& IsMemFunPtr = + std::is_member_function_pointer>::value; + +template +const bool& IsMemObjPtr = std::is_member_object_pointer>::value; + +template >> +const bool& IsMemPtrToBaseOf = + std::is_base_of>::value; + +template +const bool& IsRefWrapper = is_reference_wrapper>::value; + +template +using EnableIf = std::enable_if_t; + +// Invokes a member function pointer on a reference to an object of a suitable +// type. Covers bullet 1 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.1 +template && IsMemPtrToBaseOf> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) { + return (std::forward(t1).*f)(std::forward(args)...); +} + +// Invokes a member function pointer on a std::reference_wrapper to an object of +// a suitable type. Covers bullet 2 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.2 +template && IsRefWrapper> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) { + return (t1.get().*f)(std::forward(args)...); +} + +// Invokes a member function pointer on a pointer-like type to an object of a +// suitable type. Covers bullet 3 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.3 +template && !IsMemPtrToBaseOf && + !IsRefWrapper> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) { + return ((*std::forward(t1)).*f)(std::forward(args)...); +} + +// Invokes a member object pointer on a reference to an object of a suitable +// type. Covers bullet 4 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.4 +template && IsMemPtrToBaseOf> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) { + return std::forward(t1).*f; +} + +// Invokes a member object pointer on a std::reference_wrapper to an object of +// a suitable type. Covers bullet 5 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.5 +template && IsRefWrapper> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) { + return t1.get().*f; +} + +// Invokes a member object pointer on a pointer-like type to an object of a +// suitable type. Covers bullet 6 of the INVOKE definition. +// +// Reference: https://wg21.link/func.require#1.6 +template && !IsMemPtrToBaseOf && + !IsRefWrapper> = true> +constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) { + return (*std::forward(t1)).*f; +} + +// Invokes a regular function or function object. Covers bullet 7 of the INVOKE +// definition. +// +// Reference: https://wg21.link/func.require#1.7 +template +constexpr decltype(auto) InvokeImpl(F&& f, Args&&... args) { + return std::forward(f)(std::forward(args)...); +} + +} // namespace invoke_internal + +// Implementation of C++17's std::invoke. This is not based on implementation +// referenced in original std::invoke proposal, but rather a manual +// implementation, so that it can be constexpr. +// +// References: +// - https://wg21.link/n4169#implementability +// - https://en.cppreference.com/w/cpp/utility/functional/invoke +// - https://wg21.link/func.invoke +template +constexpr decltype(auto) invoke(F&& f, Args&&... args) { + return invoke_internal::InvokeImpl(std::forward(f), + std::forward(args)...); +} + +} // namespace webrtc + +#endif // RTC_BASE_CONTAINERS_INVOKE_H_ diff --git a/pkg/apm/webrtc/rtc_base/containers/move_only_int.h b/pkg/apm/webrtc/rtc_base/containers/move_only_int.h new file mode 100644 index 00000000..8f745aa6 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/containers/move_only_int.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This implementation is borrowed from Chromium. + +#ifndef RTC_BASE_CONTAINERS_MOVE_ONLY_INT_H_ +#define RTC_BASE_CONTAINERS_MOVE_ONLY_INT_H_ + +namespace webrtc { + +// A move-only class that holds an integer. This is designed for testing +// containers. See also CopyOnlyInt. +class MoveOnlyInt { + public: + explicit MoveOnlyInt(int data = 1) : data_(data) {} + MoveOnlyInt(const MoveOnlyInt& other) = delete; + MoveOnlyInt& operator=(const MoveOnlyInt& other) = delete; + MoveOnlyInt(MoveOnlyInt&& other) : data_(other.data_) { other.data_ = 0; } + ~MoveOnlyInt() { data_ = 0; } + + MoveOnlyInt& operator=(MoveOnlyInt&& other) { + data_ = other.data_; + other.data_ = 0; + return *this; + } + + friend bool operator==(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) { + return lhs.data_ == rhs.data_; + } + + friend bool operator!=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) { + return !operator==(lhs, rhs); + } + + friend bool operator<(const MoveOnlyInt& lhs, int rhs) { + return lhs.data_ < rhs; + } + + friend bool operator<(int lhs, const MoveOnlyInt& rhs) { + return lhs < rhs.data_; + } + + friend bool operator<(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) { + return lhs.data_ < rhs.data_; + } + + friend bool operator>(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) { + return rhs < lhs; + } + + friend bool operator<=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) { + return !(rhs < lhs); + } + + friend bool operator>=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) { + return !(lhs < rhs); + } + + int data() const { return data_; } + + private: + volatile int data_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_CONTAINERS_MOVE_ONLY_INT_H_ diff --git a/pkg/apm/webrtc/rtc_base/copy_on_write_buffer.cc b/pkg/apm/webrtc/rtc_base/copy_on_write_buffer.cc new file mode 100644 index 00000000..51bdb17f --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/copy_on_write_buffer.cc @@ -0,0 +1,127 @@ +/* + * Copyright 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 "rtc_base/copy_on_write_buffer.h" + +#include + +#include "absl/strings/string_view.h" + +namespace webrtc { + +CopyOnWriteBuffer::CopyOnWriteBuffer() : offset_(0), size_(0) { + RTC_DCHECK(IsConsistent()); +} + +CopyOnWriteBuffer::CopyOnWriteBuffer(const CopyOnWriteBuffer& buf) + : buffer_(buf.buffer_), offset_(buf.offset_), size_(buf.size_) {} + +CopyOnWriteBuffer::CopyOnWriteBuffer(CopyOnWriteBuffer&& buf) noexcept + : buffer_(std::move(buf.buffer_)), offset_(buf.offset_), size_(buf.size_) { + buf.offset_ = 0; + buf.size_ = 0; + RTC_DCHECK(IsConsistent()); +} + +CopyOnWriteBuffer::CopyOnWriteBuffer(absl::string_view s) + : CopyOnWriteBuffer(s.data(), s.length()) {} + +CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size) + : buffer_(size > 0 ? new RefCountedBuffer(size) : nullptr), + offset_(0), + size_(size) { + RTC_DCHECK(IsConsistent()); +} + +CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size, size_t capacity) + : buffer_(size > 0 || capacity > 0 ? new RefCountedBuffer(size, capacity) + : nullptr), + offset_(0), + size_(size) { + RTC_DCHECK(IsConsistent()); +} + +CopyOnWriteBuffer::~CopyOnWriteBuffer() = default; + +bool CopyOnWriteBuffer::operator==(const CopyOnWriteBuffer& buf) const { + // Must either be the same view of the same buffer or have the same contents. + RTC_DCHECK(IsConsistent()); + RTC_DCHECK(buf.IsConsistent()); + return size_ == buf.size_ && + (cdata() == buf.cdata() || memcmp(cdata(), buf.cdata(), size_) == 0); +} + +void CopyOnWriteBuffer::SetSize(size_t size) { + RTC_DCHECK(IsConsistent()); + if (!buffer_) { + if (size > 0) { + buffer_ = new RefCountedBuffer(size); + offset_ = 0; + size_ = size; + } + RTC_DCHECK(IsConsistent()); + return; + } + + if (size <= size_) { + size_ = size; + return; + } + + UnshareAndEnsureCapacity(std::max(capacity(), size)); + buffer_->SetSize(size + offset_); + size_ = size; + RTC_DCHECK(IsConsistent()); +} + +void CopyOnWriteBuffer::EnsureCapacity(size_t new_capacity) { + RTC_DCHECK(IsConsistent()); + if (!buffer_) { + if (new_capacity > 0) { + buffer_ = new RefCountedBuffer(0, new_capacity); + offset_ = 0; + size_ = 0; + } + RTC_DCHECK(IsConsistent()); + return; + } else if (new_capacity <= capacity()) { + return; + } + + UnshareAndEnsureCapacity(new_capacity); + RTC_DCHECK(IsConsistent()); +} + +void CopyOnWriteBuffer::Clear() { + if (!buffer_) + return; + + if (buffer_->HasOneRef()) { + buffer_->Clear(); + } else { + buffer_ = new RefCountedBuffer(0, capacity()); + } + offset_ = 0; + size_ = 0; + RTC_DCHECK(IsConsistent()); +} + +void CopyOnWriteBuffer::UnshareAndEnsureCapacity(size_t new_capacity) { + if (buffer_->HasOneRef() && new_capacity <= capacity()) { + return; + } + + buffer_ = + new RefCountedBuffer(buffer_->data() + offset_, size_, new_capacity); + offset_ = 0; + RTC_DCHECK(IsConsistent()); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/copy_on_write_buffer.h b/pkg/apm/webrtc/rtc_base/copy_on_write_buffer.h new file mode 100644 index 00000000..6dfa2891 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/copy_on_write_buffer.h @@ -0,0 +1,329 @@ +/* + * Copyright 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. + */ + +#ifndef RTC_BASE_COPY_ON_WRITE_BUFFER_H_ +#define RTC_BASE_COPY_ON_WRITE_BUFFER_H_ + +#include + +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/scoped_refptr.h" +#include "rtc_base/buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/type_traits.h" + +namespace webrtc { + +class RTC_EXPORT CopyOnWriteBuffer { + public: + // An empty buffer. + CopyOnWriteBuffer(); + // Share the data with an existing buffer. + CopyOnWriteBuffer(const CopyOnWriteBuffer& buf); + // Move contents from an existing buffer. + CopyOnWriteBuffer(CopyOnWriteBuffer&& buf) noexcept; + + // Construct a buffer from a string, convenient for unittests. + explicit CopyOnWriteBuffer(absl::string_view s); + + // Construct a buffer with the specified number of uninitialized bytes. + explicit CopyOnWriteBuffer(size_t size); + CopyOnWriteBuffer(size_t size, size_t capacity); + + // Construct a buffer and copy the specified number of bytes into it. The + // source array may be (const) uint8_t*, int8_t*, or char*. + template ::value>::type* = nullptr> + CopyOnWriteBuffer(const T* data, size_t size) + : CopyOnWriteBuffer(data, size, size) {} + template ::value>::type* = nullptr> + CopyOnWriteBuffer(const T* data, size_t size, size_t capacity) + : CopyOnWriteBuffer(size, capacity) { + if (buffer_) { + std::memcpy(buffer_->data(), data, size); + offset_ = 0; + size_ = size; + } + } + + // Construct a buffer from the contents of an array. + template ::value>::type* = nullptr> + CopyOnWriteBuffer(const T (&array)[N]) // NOLINT: runtime/explicit + : CopyOnWriteBuffer(array, N) {} + + // Construct a buffer from a vector like type. + template ().data())>, + typename std::enable_if_t< + !std::is_same::value && + HasDataAndSize::value && + internal::BufferCompat::value>* = nullptr> + explicit CopyOnWriteBuffer(const VecT& v) + : CopyOnWriteBuffer(v.data(), v.size()) {} + + // Construct a buffer from a vector like type and a capacity argument + template ().data())>, + typename std::enable_if_t< + !std::is_same::value && + HasDataAndSize::value && + internal::BufferCompat::value>* = nullptr> + explicit CopyOnWriteBuffer(const VecT& v, size_t capacity) + : CopyOnWriteBuffer(v.data(), v.size(), capacity) {} + + ~CopyOnWriteBuffer(); + + // Get a pointer to the data. Just .data() will give you a (const) uint8_t*, + // but you may also use .data() and .data(). + template ::value>::type* = nullptr> + const T* data() const { + return cdata(); + } + + // Get writable pointer to the data. This will create a copy of the underlying + // data if it is shared with other buffers. + template ::value>::type* = nullptr> + T* MutableData() { + RTC_DCHECK(IsConsistent()); + if (!buffer_) { + return nullptr; + } + UnshareAndEnsureCapacity(capacity()); + return buffer_->data() + offset_; + } + + // Get const pointer to the data. This will not create a copy of the + // underlying data if it is shared with other buffers. + template ::value>::type* = nullptr> + const T* cdata() const { + RTC_DCHECK(IsConsistent()); + if (!buffer_) { + return nullptr; + } + return buffer_->data() + offset_; + } + + bool empty() const { return size_ == 0; } + + size_t size() const { + RTC_DCHECK(IsConsistent()); + return size_; + } + + size_t capacity() const { + RTC_DCHECK(IsConsistent()); + return buffer_ ? buffer_->capacity() - offset_ : 0; + } + + const uint8_t* begin() const { return data(); } + const uint8_t* end() const { return data() + size_; } + + CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) { + RTC_DCHECK(IsConsistent()); + RTC_DCHECK(buf.IsConsistent()); + if (&buf != this) { + buffer_ = buf.buffer_; + offset_ = buf.offset_; + size_ = buf.size_; + } + return *this; + } + + CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) { + RTC_DCHECK(IsConsistent()); + RTC_DCHECK(buf.IsConsistent()); + buffer_ = std::move(buf.buffer_); + offset_ = buf.offset_; + size_ = buf.size_; + buf.offset_ = 0; + buf.size_ = 0; + return *this; + } + + bool operator==(const CopyOnWriteBuffer& buf) const; + + bool operator!=(const CopyOnWriteBuffer& buf) const { + return !(*this == buf); + } + + uint8_t operator[](size_t index) const { + RTC_DCHECK_LT(index, size()); + return cdata()[index]; + } + + // Replace the contents of the buffer. Accepts the same types as the + // constructors. + template ::value>::type* = nullptr> + void SetData(const T* data, size_t size) { + RTC_DCHECK(IsConsistent()); + if (!buffer_) { + buffer_ = size > 0 ? new RefCountedBuffer(data, size) : nullptr; + } else if (!buffer_->HasOneRef()) { + buffer_ = new RefCountedBuffer(data, size, capacity()); + } else { + buffer_->SetData(data, size); + } + offset_ = 0; + size_ = size; + + RTC_DCHECK(IsConsistent()); + } + + template ::value>::type* = nullptr> + void SetData(const T (&array)[N]) { + SetData(array, N); + } + + void SetData(const CopyOnWriteBuffer& buf) { + RTC_DCHECK(IsConsistent()); + RTC_DCHECK(buf.IsConsistent()); + if (&buf != this) { + buffer_ = buf.buffer_; + offset_ = buf.offset_; + size_ = buf.size_; + } + } + + // Append data to the buffer. Accepts the same types as the constructors. + template ::value>::type* = nullptr> + void AppendData(const T* data, size_t size) { + RTC_DCHECK(IsConsistent()); + if (!buffer_) { + buffer_ = new RefCountedBuffer(data, size); + offset_ = 0; + size_ = size; + RTC_DCHECK(IsConsistent()); + return; + } + + UnshareAndEnsureCapacity(std::max(capacity(), size_ + size)); + + buffer_->SetSize(offset_ + + size_); // Remove data to the right of the slice. + buffer_->AppendData(data, size); + size_ += size; + + RTC_DCHECK(IsConsistent()); + } + + template ::value>::type* = nullptr> + void AppendData(const T (&array)[N]) { + AppendData(array, N); + } + + template ().data())>, + typename std::enable_if_t< + HasDataAndSize::value && + internal::BufferCompat::value>* = nullptr> + void AppendData(const VecT& v) { + AppendData(v.data(), v.size()); + } + + // Sets the size of the buffer. If the new size is smaller than the old, the + // buffer contents will be kept but truncated; if the new size is greater, + // the existing contents will be kept and the new space will be + // uninitialized. + void SetSize(size_t size); + + // Ensure that the buffer size can be increased to at least capacity without + // further reallocation. (Of course, this operation might need to reallocate + // the buffer.) + void EnsureCapacity(size_t capacity); + + // Resets the buffer to zero size without altering capacity. Works even if the + // buffer has been moved from. + void Clear(); + + // Swaps two buffers. + friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) { + a.buffer_.swap(b.buffer_); + std::swap(a.offset_, b.offset_); + std::swap(a.size_, b.size_); + } + + CopyOnWriteBuffer Slice(size_t offset, size_t length) const { + CopyOnWriteBuffer slice(*this); + RTC_DCHECK_LE(offset, size_); + RTC_DCHECK_LE(length + offset, size_); + slice.offset_ += offset; + slice.size_ = length; + return slice; + } + + private: + using RefCountedBuffer = FinalRefCountedObject; + // Create a copy of the underlying data if it is referenced from other Buffer + // objects or there is not enough capacity. + void UnshareAndEnsureCapacity(size_t new_capacity); + + // Pre- and postcondition of all methods. + bool IsConsistent() const { + if (buffer_) { + return buffer_->capacity() > 0 && offset_ <= buffer_->size() && + offset_ + size_ <= buffer_->size(); + } else { + return size_ == 0 && offset_ == 0; + } + } + + // buffer_ is either null, or points to an webrtc::Buffer with capacity > 0. + scoped_refptr buffer_; + // This buffer may represent a slice of a original data. + size_t offset_; // Offset of a current slice in the original data in buffer_. + // Should be 0 if the buffer_ is empty. + size_t size_; // Size of a current slice in the original data in buffer_. + // Should be 0 if the buffer_ is empty. +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::CopyOnWriteBuffer; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_COPY_ON_WRITE_BUFFER_H_ diff --git a/pkg/apm/webrtc/rtc_base/cpu_time.h b/pkg/apm/webrtc/rtc_base/cpu_time.h new file mode 100644 index 00000000..870a5a5f --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/cpu_time.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_CPU_TIME_H_ +#define RTC_BASE_CPU_TIME_H_ + +#include + +namespace webrtc { + +// Returns total CPU time of a current process in nanoseconds. +// Time base is unknown, therefore use only to calculate deltas. +int64_t GetProcessCpuTimeNanos(); + +// Returns total CPU time of a current thread in nanoseconds. +// Time base is unknown, therefore use only to calculate deltas. +int64_t GetThreadCpuTimeNanos(); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::GetProcessCpuTimeNanos; +using ::webrtc::GetThreadCpuTimeNanos; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_CPU_TIME_H_ diff --git a/pkg/apm/webrtc/rtc_base/crc32.h b/pkg/apm/webrtc/rtc_base/crc32.h new file mode 100644 index 00000000..6d208b96 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/crc32.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_CRC32_H_ +#define RTC_BASE_CRC32_H_ + +#include +#include + +#include + +#include "absl/strings/string_view.h" + +namespace webrtc { + +// Updates a CRC32 checksum with `len` bytes from `buf`. `initial` holds the +// checksum result from the previous update; for the first call, it should be 0. +uint32_t UpdateCrc32(uint32_t initial, const void* buf, size_t len); + +// Computes a CRC32 checksum using `len` bytes from `buf`. +inline uint32_t ComputeCrc32(const void* buf, size_t len) { + return UpdateCrc32(0, buf, len); +} +inline uint32_t ComputeCrc32(absl::string_view str) { + return ComputeCrc32(str.data(), str.size()); +} + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::ComputeCrc32; +using ::webrtc::UpdateCrc32; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_CRC32_H_ diff --git a/pkg/apm/webrtc/rtc_base/crypto_random.h b/pkg/apm/webrtc/rtc_base/crypto_random.h new file mode 100644 index 00000000..aaa18035 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/crypto_random.h @@ -0,0 +1,107 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_CRYPTO_RANDOM_H_ +#define RTC_BASE_CRYPTO_RANDOM_H_ + +#include +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Interface for RNG implementations. +class RandomGenerator { + public: + virtual ~RandomGenerator() {} + virtual bool Init(const void* seed, size_t len) = 0; + virtual bool Generate(void* buf, size_t len) = 0; +}; + +// Sets the default random generator as the source of randomness. The default +// source uses the OpenSSL RNG and provides cryptographically secure randomness. +void SetDefaultRandomGenerator(); + +// Set a custom random generator. Results produced by CreateRandomXyz +// are cryptographically random iff the output of the supplied generator is +// cryptographically random. +void SetRandomGenerator(std::unique_ptr generator); + +// For testing, we can return predictable data. +void SetRandomTestMode(bool test); + +// Initializes the RNG, and seeds it with the specified entropy. +bool InitRandom(int seed); +bool InitRandom(const char* seed, size_t len); + +// Generates a (cryptographically) random string of the given length. +// We generate base64 values so that they will be printable. +RTC_EXPORT std::string CreateRandomString(size_t length); + +// Generates a (cryptographically) random string of the given length. +// We generate base64 values so that they will be printable. +// Return false if the random number generator failed. +RTC_EXPORT bool CreateRandomString(size_t length, std::string* str); + +// Generates a (cryptographically) random string of the given length, +// with characters from the given table. Return false if the random +// number generator failed. +// For ease of implementation, the function requires that the table +// size evenly divide 256; otherwise, it returns false. +RTC_EXPORT bool CreateRandomString(size_t length, + absl::string_view table, + std::string* str); + +// Generates (cryptographically) random data of the given length. +// Return false if the random number generator failed. +bool CreateRandomData(size_t length, std::string* data); + +// Generates a (cryptographically) random UUID version 4 string. +std::string CreateRandomUuid(); + +// Generates a random id. +uint32_t CreateRandomId(); + +// Generates a 64 bit random id. +RTC_EXPORT uint64_t CreateRandomId64(); + +// Generates a random id > 0. +uint32_t CreateRandomNonZeroId(); + +// Generates a random double between 0.0 (inclusive) and 1.0 (exclusive). +double CreateRandomDouble(); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::CreateRandomData; +using ::webrtc::CreateRandomDouble; +using ::webrtc::CreateRandomId; +using ::webrtc::CreateRandomId64; +using ::webrtc::CreateRandomNonZeroId; +using ::webrtc::CreateRandomString; +using ::webrtc::CreateRandomUuid; +using ::webrtc::InitRandom; +using ::webrtc::RandomGenerator; +using ::webrtc::SetDefaultRandomGenerator; +using ::webrtc::SetRandomGenerator; +using ::webrtc::SetRandomTestMode; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_CRYPTO_RANDOM_H_ diff --git a/pkg/apm/webrtc/rtc_base/data_rate_limiter.h b/pkg/apm/webrtc/rtc_base/data_rate_limiter.h new file mode 100644 index 00000000..088cb637 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/data_rate_limiter.h @@ -0,0 +1,66 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_DATA_RATE_LIMITER_H_ +#define RTC_BASE_DATA_RATE_LIMITER_H_ + +#include + +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Limits the rate of use to a certain maximum quantity per period of +// time. Use, for example, for simple bandwidth throttling. +// +// It's implemented like a diet plan: You have so many calories per +// day. If you hit the limit, you can't eat any more until the next +// day. +class RTC_EXPORT DataRateLimiter { + public: + // For example, 100kb per second. + DataRateLimiter(size_t max, double period) + : max_per_period_(max), + period_length_(period), + used_in_period_(0), + period_start_(0.0), + period_end_(period) {} + virtual ~DataRateLimiter() {} + + // Returns true if if the desired quantity is available in the + // current period (< (max - used)). Once the given time passes the + // end of the period, used is set to zero and more use is available. + bool CanUse(size_t desired, double time); + // Increment the quantity used this period. If past the end of a + // period, a new period is started. + void Use(size_t used, double time); + + size_t used_in_period() const { return used_in_period_; } + + size_t max_per_period() const { return max_per_period_; } + + private: + size_t max_per_period_; + double period_length_; + size_t used_in_period_; + double period_start_; + double period_end_; +}; +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::DataRateLimiter; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_DATA_RATE_LIMITER_H_ diff --git a/pkg/apm/webrtc/rtc_base/deprecated/recursive_critical_section.h b/pkg/apm/webrtc/rtc_base/deprecated/recursive_critical_section.h new file mode 100644 index 00000000..e347e483 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/deprecated/recursive_critical_section.h @@ -0,0 +1,116 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_DEPRECATED_RECURSIVE_CRITICAL_SECTION_H_ +#define RTC_BASE_DEPRECATED_RECURSIVE_CRITICAL_SECTION_H_ + +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/thread_annotations.h" + +// IWYU pragma: begin_keep +#if defined(WEBRTC_WIN) +// clang-format off +// clang formating would change include order. + +// Include winsock2.h before including to maintain consistency with +// win32.h. To include win32.h directly, it must be broken out into its own +// build target. +#include +#include +#include // must come after windows headers. +// clang-format on +#endif // defined(WEBRTC_WIN) + +#if defined(WEBRTC_POSIX) +#include +#endif + +// See notes in the 'Performance' unit test for the effects of this flag. +#define RTC_USE_NATIVE_MUTEX_ON_MAC 1 + +#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC +#include +#endif +// IWYU pragma: end_keep + +namespace webrtc { + +// NOTE: This class is deprecated. Please use webrtc::Mutex instead! +// Search using https://www.google.com/?q=recursive+lock+considered+harmful +// to find the reasons. +// +// Locking methods (Enter, TryEnter, Leave)are const to permit protecting +// members inside a const context without requiring mutable +// RecursiveCriticalSections everywhere. RecursiveCriticalSection is +// reentrant lock. +class RTC_LOCKABLE RecursiveCriticalSection { + public: + RecursiveCriticalSection(); + ~RecursiveCriticalSection(); + + void Enter() const RTC_EXCLUSIVE_LOCK_FUNCTION(); + bool TryEnter() const RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true); + void Leave() const RTC_UNLOCK_FUNCTION(); + + private: + // Use only for RTC_DCHECKing. + bool CurrentThreadIsOwner() const; + +#if defined(WEBRTC_WIN) + mutable CRITICAL_SECTION crit_; +#elif defined(WEBRTC_POSIX) +#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC + // Number of times the lock has been locked + number of threads waiting. + // TODO(tommi): We could use this number and subtract the recursion count + // to find places where we have multiple threads contending on the same lock. + mutable std::atomic lock_queue_; + // `recursion_` represents the recursion count + 1 for the thread that owns + // the lock. Only modified by the thread that owns the lock. + mutable int recursion_; + // Used to signal a single waiting thread when the lock becomes available. + mutable dispatch_semaphore_t semaphore_; + // The thread that currently holds the lock. Required to handle recursion. + mutable PlatformThreadRef owning_thread_; +#else + mutable pthread_mutex_t mutex_; +#endif + mutable PlatformThreadRef thread_; // Only used by RTC_DCHECKs. + mutable int recursion_count_; // Only used by RTC_DCHECKs. +#else // !defined(WEBRTC_WIN) && !defined(WEBRTC_POSIX) +#error Unsupported platform. +#endif +}; + +// CritScope, for serializing execution through a scope. +class RTC_SCOPED_LOCKABLE CritScope { + public: + explicit CritScope(const RecursiveCriticalSection* cs) + RTC_EXCLUSIVE_LOCK_FUNCTION(cs); + ~CritScope() RTC_UNLOCK_FUNCTION(); + + CritScope(const CritScope&) = delete; + CritScope& operator=(const CritScope&) = delete; + + private: + const RecursiveCriticalSection* const cs_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::CritScope; +using ::webrtc::RecursiveCriticalSection; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_DEPRECATED_RECURSIVE_CRITICAL_SECTION_H_ diff --git a/pkg/apm/webrtc/rtc_base/dscp.h b/pkg/apm/webrtc/rtc_base/dscp.h new file mode 100644 index 00000000..db3eb74c --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/dscp.h @@ -0,0 +1,76 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_DSCP_H_ +#define RTC_BASE_DSCP_H_ + +namespace webrtc { +// Differentiated Services Code Point. +// See http://tools.ietf.org/html/rfc2474 for details. +enum DiffServCodePoint { + DSCP_NO_CHANGE = -1, + DSCP_DEFAULT = 0, // Same as DSCP_CS0 + DSCP_CS0 = 0, // The default + DSCP_CS1 = 8, // Bulk/background traffic + DSCP_AF11 = 10, + DSCP_AF12 = 12, + DSCP_AF13 = 14, + DSCP_CS2 = 16, + DSCP_AF21 = 18, + DSCP_AF22 = 20, + DSCP_AF23 = 22, + DSCP_CS3 = 24, + DSCP_AF31 = 26, + DSCP_AF32 = 28, + DSCP_AF33 = 30, + DSCP_CS4 = 32, + DSCP_AF41 = 34, // Video + DSCP_AF42 = 36, // Video + DSCP_AF43 = 38, // Video + DSCP_CS5 = 40, // Video + DSCP_EF = 46, // Voice + DSCP_CS6 = 48, // Voice + DSCP_CS7 = 56, // Control messages +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::DiffServCodePoint; +using ::webrtc::DSCP_AF11; +using ::webrtc::DSCP_AF12; +using ::webrtc::DSCP_AF13; +using ::webrtc::DSCP_AF21; +using ::webrtc::DSCP_AF22; +using ::webrtc::DSCP_AF23; +using ::webrtc::DSCP_AF31; +using ::webrtc::DSCP_AF32; +using ::webrtc::DSCP_AF33; +using ::webrtc::DSCP_AF41; +using ::webrtc::DSCP_AF42; +using ::webrtc::DSCP_AF43; +using ::webrtc::DSCP_CS0; +using ::webrtc::DSCP_CS1; +using ::webrtc::DSCP_CS2; +using ::webrtc::DSCP_CS3; +using ::webrtc::DSCP_CS4; +using ::webrtc::DSCP_CS5; +using ::webrtc::DSCP_CS6; +using ::webrtc::DSCP_CS7; +using ::webrtc::DSCP_DEFAULT; +using ::webrtc::DSCP_EF; +using ::webrtc::DSCP_NO_CHANGE; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_DSCP_H_ diff --git a/pkg/apm/webrtc/rtc_base/event.cc b/pkg/apm/webrtc/rtc_base/event.cc new file mode 100644 index 00000000..b78e249c --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/event.cc @@ -0,0 +1,210 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/event.h" + +#if defined(WEBRTC_WIN) +#include +#elif defined(WEBRTC_POSIX) +#include +#include +#include +#include +#else +#error "Must define either WEBRTC_WIN or WEBRTC_POSIX." +#endif + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/synchronization/yield_policy.h" +#include "rtc_base/system/warn_current_thread_is_deadlocked.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +using ::webrtc::TimeDelta; + +Event::Event() : Event(false, false) {} + +#if defined(WEBRTC_WIN) + +Event::Event(bool manual_reset, bool initially_signaled) { + event_handle_ = ::CreateEvent(nullptr, // Security attributes. + manual_reset, initially_signaled, + nullptr); // Name. + RTC_CHECK(event_handle_); +} + +Event::~Event() { + CloseHandle(event_handle_); +} + +void Event::Set() { + SetEvent(event_handle_); +} + +void Event::Reset() { + ResetEvent(event_handle_); +} + +bool Event::Wait(TimeDelta give_up_after, TimeDelta /*warn_after*/) { + ScopedYieldPolicy::YieldExecution(); + const DWORD ms = + give_up_after.IsPlusInfinity() + ? INFINITE + : give_up_after.RoundUpTo(webrtc::TimeDelta::Millis(1)).ms(); + return (WaitForSingleObject(event_handle_, ms) == WAIT_OBJECT_0); +} + +#elif defined(WEBRTC_POSIX) + +// On MacOS, clock_gettime is available from version 10.12, and on +// iOS, from version 10.0. So we can't use it yet. +#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) +#define USE_CLOCK_GETTIME 0 +#define USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP 0 +// On Android, pthread_condattr_setclock is available from version 21. By +// default, we target a new enough version for 64-bit platforms but not for +// 32-bit platforms. For older versions, use +// pthread_cond_timedwait_monotonic_np. +#elif defined(WEBRTC_ANDROID) && (__ANDROID_API__ < 21) +#define USE_CLOCK_GETTIME 1 +#define USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP 1 +#else +#define USE_CLOCK_GETTIME 1 +#define USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP 0 +#endif + +Event::Event(bool manual_reset, bool initially_signaled) + : is_manual_reset_(manual_reset), event_status_(initially_signaled) { + RTC_CHECK(pthread_mutex_init(&event_mutex_, nullptr) == 0); + pthread_condattr_t cond_attr; + RTC_CHECK(pthread_condattr_init(&cond_attr) == 0); +#if USE_CLOCK_GETTIME && !USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP + RTC_CHECK(pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC) == 0); +#endif + RTC_CHECK(pthread_cond_init(&event_cond_, &cond_attr) == 0); + pthread_condattr_destroy(&cond_attr); +} + +Event::~Event() { + pthread_mutex_destroy(&event_mutex_); + pthread_cond_destroy(&event_cond_); +} + +void Event::Set() { + pthread_mutex_lock(&event_mutex_); + event_status_ = true; + pthread_cond_broadcast(&event_cond_); + pthread_mutex_unlock(&event_mutex_); +} + +void Event::Reset() { + pthread_mutex_lock(&event_mutex_); + event_status_ = false; + pthread_mutex_unlock(&event_mutex_); +} + +namespace { + +timespec GetTimespec(TimeDelta duration_from_now) { + timespec ts; + + // Get the current time. +#if USE_CLOCK_GETTIME + clock_gettime(CLOCK_MONOTONIC, &ts); +#else + timeval tv; + gettimeofday(&tv, nullptr); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * webrtc::kNumNanosecsPerMicrosec; +#endif + + // Add the specified number of milliseconds to it. + int64_t microsecs_from_now = duration_from_now.us(); + ts.tv_sec += microsecs_from_now / kNumMicrosecsPerSec; + ts.tv_nsec += + (microsecs_from_now % kNumMicrosecsPerSec) * kNumNanosecsPerMicrosec; + + // Normalize. + if (ts.tv_nsec >= kNumNanosecsPerSec) { + ts.tv_sec++; + ts.tv_nsec -= kNumNanosecsPerSec; + } + + return ts; +} + +} // namespace + +bool Event::Wait(TimeDelta give_up_after, TimeDelta warn_after) { + // Instant when we'll log a warning message (because we've been waiting so + // long it might be a bug), but not yet give up waiting. nullopt if we + // shouldn't log a warning. + const std::optional warn_ts = + warn_after >= give_up_after ? std::nullopt + : std::make_optional(GetTimespec(warn_after)); + + // Instant when we'll stop waiting and return an error. nullopt if we should + // never give up. + const std::optional give_up_ts = + give_up_after.IsPlusInfinity() + ? std::nullopt + : std::make_optional(GetTimespec(give_up_after)); + + ScopedYieldPolicy::YieldExecution(); + pthread_mutex_lock(&event_mutex_); + + // Wait for `event_cond_` to trigger and `event_status_` to be set, with the + // given timeout (or without a timeout if none is given). + const auto wait = [&](const std::optional timeout_ts) { + int error = 0; + while (!event_status_ && error == 0) { + if (timeout_ts == std::nullopt) { + error = pthread_cond_wait(&event_cond_, &event_mutex_); + } else { +#if USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP + error = pthread_cond_timedwait_monotonic_np(&event_cond_, &event_mutex_, + &*timeout_ts); +#else + error = + pthread_cond_timedwait(&event_cond_, &event_mutex_, &*timeout_ts); +#endif + } + } + return error; + }; + + int error; + if (warn_ts == std::nullopt) { + error = wait(give_up_ts); + } else { + error = wait(warn_ts); + if (error == ETIMEDOUT) { + webrtc::WarnThatTheCurrentThreadIsProbablyDeadlocked(); + error = wait(give_up_ts); + } + } + + // NOTE(liulk): Exactly one thread will auto-reset this event. All + // the other threads will think it's unsignaled. This seems to be + // consistent with auto-reset events in WEBRTC_WIN + if (error == 0 && !is_manual_reset_) + event_status_ = false; + + pthread_mutex_unlock(&event_mutex_); + + return (error == 0); +} + +#endif + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/event.h b/pkg/apm/webrtc/rtc_base/event.h new file mode 100644 index 00000000..0f5527e4 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/event.h @@ -0,0 +1,147 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_EVENT_H_ +#define RTC_BASE_EVENT_H_ + +#include "api/units/time_delta.h" + +#if defined(WEBRTC_WIN) +#include +#elif defined(WEBRTC_POSIX) +#include +#else +#error "Must define either WEBRTC_WIN or WEBRTC_POSIX." +#endif + +#include "rtc_base/synchronization/yield_policy.h" + +namespace webrtc { + +// RTC_DISALLOW_WAIT() utility +// +// Sets a stack-scoped flag that disallows use of `webrtc::Event::Wait` by means +// of raising a DCHECK when a call to `webrtc::Event::Wait()` is made.. +// This is useful to guard synchronization-free scopes against regressions. +// +// Example of what this would catch (`ScopeToProtect` calls `Foo`): +// +// void Foo(TaskQueue* tq) { +// Event event; +// tq->PostTask([&event]() { +// event.Set(); +// }); +// event.Wait(Event::kForever); // <- Will trigger a DCHECK. +// } +// +// void ScopeToProtect() { +// TaskQueue* tq = GetSomeTaskQueue(); +// RTC_DISALLOW_WAIT(); // Policy takes effect. +// Foo(tq); +// } +// +#if RTC_DCHECK_IS_ON +#define RTC_DISALLOW_WAIT() ScopedDisallowWait disallow_wait_##__LINE__ +#else +#define RTC_DISALLOW_WAIT() +#endif + +class Event { + public: + // TODO(bugs.webrtc.org/14366): Consider removing this redundant alias. + static constexpr TimeDelta kForever = TimeDelta::PlusInfinity(); + static constexpr TimeDelta kDefaultWarnDuration = TimeDelta::Seconds(3); + + Event(); + Event(bool manual_reset, bool initially_signaled); + Event(const Event&) = delete; + Event& operator=(const Event&) = delete; + ~Event(); + + void Set(); + void Reset(); + + // Waits for the event to become signaled, but logs a warning if it takes more + // than `warn_after`, and gives up completely if it takes more than + // `give_up_after`. (If `warn_after >= give_up_after`, no warning will be + // logged.) Either or both may be `kForever`, which means wait indefinitely. + // + // Care is taken so that the underlying OS wait call isn't requested to sleep + // shorter than `give_up_after`. + // + // Returns true if the event was signaled, false if there was a timeout or + // some other error. + bool Wait(TimeDelta give_up_after, TimeDelta warn_after); + + // Waits with the given timeout and a reasonable default warning timeout. + bool Wait(TimeDelta give_up_after) { + return Wait(give_up_after, give_up_after.IsPlusInfinity() + ? kDefaultWarnDuration + : kForever); + } + + private: +#if defined(WEBRTC_WIN) + HANDLE event_handle_; +#elif defined(WEBRTC_POSIX) + pthread_mutex_t event_mutex_; + pthread_cond_t event_cond_; + const bool is_manual_reset_; + bool event_status_; +#endif +}; + +// These classes are provided for compatibility with Chromium. +// The webrtc::Event implementation is overriden inside of Chromium for the +// purposes of detecting when threads are blocked that shouldn't be as well as +// to use the more accurate event implementation that's there than is provided +// by default on some platforms (e.g. Windows). +// When building with standalone WebRTC, this class is a noop. +// For further information, please see the +// ScopedAllowBaseSyncPrimitives(ForTesting) classes in Chromium. +class ScopedAllowBaseSyncPrimitives { + public: + ScopedAllowBaseSyncPrimitives() {} + ~ScopedAllowBaseSyncPrimitives() {} +}; + +class ScopedAllowBaseSyncPrimitivesForTesting { + public: + ScopedAllowBaseSyncPrimitivesForTesting() {} + ~ScopedAllowBaseSyncPrimitivesForTesting() {} +}; + +#if RTC_DCHECK_IS_ON +class ScopedDisallowWait { + public: + ScopedDisallowWait() = default; + + private: + class DisallowYieldHandler : public YieldInterface { + public: + void YieldExecution() override { RTC_DCHECK_NOTREACHED(); } + } handler_; + webrtc::ScopedYieldPolicy policy{&handler_}; +}; +#endif + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::Event; +using ::webrtc::ScopedAllowBaseSyncPrimitives; +using ::webrtc::ScopedAllowBaseSyncPrimitivesForTesting; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_EVENT_H_ diff --git a/pkg/apm/webrtc/rtc_base/event_tracer.h b/pkg/apm/webrtc/rtc_base/event_tracer.h new file mode 100644 index 00000000..941c44c0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/event_tracer.h @@ -0,0 +1,102 @@ +/* + * 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 RTC_BASE_EVENT_TRACER_H_ +#define RTC_BASE_EVENT_TRACER_H_ + +// This file defines the interface for event tracing in WebRTC. +// +// Event log handlers are set through SetupEventTracer(). User of this API will +// provide two function pointers to handle event tracing calls. +// +// * GetCategoryEnabledPtr +// Event tracing system calls this function to determine if a particular +// event category is enabled. +// +// * AddTraceEventPtr +// Adds a tracing event. It is the user's responsibility to log the data +// provided. +// +// Parameters for the above two functions are described in trace_event.h. + +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +#if defined(RTC_USE_PERFETTO) +void RegisterPerfettoTrackEvents(); +#else +typedef const unsigned char* (*GetCategoryEnabledPtr)(const char* name); +typedef void (*AddTraceEventPtr)(char phase, + const unsigned char* category_enabled, + const char* name, + unsigned long long id, + int num_args, + const char** arg_names, + const unsigned char* arg_types, + const unsigned long long* arg_values, + unsigned char flags); + +// User of WebRTC can call this method to setup event tracing. +// +// This method must be called before any WebRTC methods. Functions +// provided should be thread-safe. +void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr, + AddTraceEventPtr add_trace_event_ptr); + +// This class defines interface for the event tracing system to call +// internally. Do not call these methods directly. +class EventTracer { + public: + static const unsigned char* GetCategoryEnabled(const char* name); + + static void AddTraceEvent(char phase, + const unsigned char* category_enabled, + const char* name, + unsigned long long id, + int num_args, + const char** arg_names, + const unsigned char* arg_types, + const unsigned long long* arg_values, + unsigned char flags); +}; +#endif + +namespace tracing { +// Set up internal event tracer. +// TODO(webrtc:15917): Implement for perfetto. +RTC_EXPORT void SetupInternalTracer(bool enable_all_categories = true); +RTC_EXPORT bool StartInternalCapture(absl::string_view filename); +RTC_EXPORT void StartInternalCaptureToFile(FILE* file); +RTC_EXPORT void StopInternalCapture(); +// Make sure we run this, this will tear down the internal tracing. +RTC_EXPORT void ShutdownInternalTracer(); +} // namespace tracing + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +namespace tracing { +using ::webrtc::tracing::SetupInternalTracer; +using ::webrtc::tracing::ShutdownInternalTracer; +using ::webrtc::tracing::StartInternalCapture; +using ::webrtc::tracing::StartInternalCaptureToFile; +using ::webrtc::tracing::StopInternalCapture; +} // namespace tracing +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_EVENT_TRACER_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/alr_experiment.h b/pkg/apm/webrtc/rtc_base/experiments/alr_experiment.h new file mode 100644 index 00000000..bc4514e0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/alr_experiment.h @@ -0,0 +1,49 @@ +/* + * 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 RTC_BASE_EXPERIMENTS_ALR_EXPERIMENT_H_ +#define RTC_BASE_EXPERIMENTS_ALR_EXPERIMENT_H_ + +#include + +#include + +#include "absl/strings/string_view.h" +#include "api/field_trials_view.h" + +namespace webrtc { +struct AlrExperimentSettings { + public: + float pacing_factor; + int64_t max_paced_queue_time; + int alr_bandwidth_usage_percent; + int alr_start_budget_level_percent; + int alr_stop_budget_level_percent; + // Will be sent to the receive side for stats slicing. + // Can be 0..6, because it's sent as a 3 bits value and there's also + // reserved value to indicate absence of experiment. + int group_id; + + static constexpr absl::string_view kScreenshareProbingBweExperimentName = + "WebRTC-ProbingScreenshareBwe"; + static constexpr absl::string_view kStrictPacingAndProbingExperimentName = + "WebRTC-StrictPacingAndProbing"; + + static std::optional CreateFromFieldTrial( + const FieldTrialsView& key_value_config, + absl::string_view experiment_name); + static bool MaxOneFieldTrialEnabled(const FieldTrialsView& key_value_config); + + private: + AlrExperimentSettings() = default; +}; +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_ALR_EXPERIMENT_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/balanced_degradation_settings.h b/pkg/apm/webrtc/rtc_base/experiments/balanced_degradation_settings.h new file mode 100644 index 00000000..6993681f --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/balanced_degradation_settings.h @@ -0,0 +1,142 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_EXPERIMENTS_BALANCED_DEGRADATION_SETTINGS_H_ +#define RTC_BASE_EXPERIMENTS_BALANCED_DEGRADATION_SETTINGS_H_ + +#include +#include + +#include "api/field_trials_view.h" +#include "api/video_codecs/video_encoder.h" + +namespace webrtc { + +class BalancedDegradationSettings { + public: + static constexpr int kNoFpsDiff = -100; + + BalancedDegradationSettings(const FieldTrialsView& field_trials); + ~BalancedDegradationSettings(); + + struct CodecTypeSpecific { + CodecTypeSpecific() {} + CodecTypeSpecific(int qp_low, int qp_high, int fps, int kbps, int kbps_res) + : qp_low(qp_low), + qp_high(qp_high), + fps(fps), + kbps(kbps), + kbps_res(kbps_res) {} + + bool operator==(const CodecTypeSpecific& o) const { + return qp_low == o.qp_low && qp_high == o.qp_high && fps == o.fps && + kbps == o.kbps && kbps_res == o.kbps_res; + } + + std::optional GetQpLow() const; + std::optional GetQpHigh() const; + std::optional GetFps() const; + std::optional GetKbps() const; + std::optional GetKbpsRes() const; + + // Optional settings. + int qp_low = 0; + int qp_high = 0; + int fps = 0; // If unset, defaults to `fps` in Config. + int kbps = 0; // If unset, defaults to `kbps` in Config. + int kbps_res = 0; // If unset, defaults to `kbps_res` in Config. + }; + + struct Config { + Config(); + Config(int pixels, + int fps, + int kbps, + int kbps_res, + int fps_diff, + CodecTypeSpecific vp8, + CodecTypeSpecific vp9, + CodecTypeSpecific h264, + CodecTypeSpecific av1, + CodecTypeSpecific generic); + + bool operator==(const Config& o) const { + return pixels == o.pixels && fps == o.fps && kbps == o.kbps && + kbps_res == o.kbps_res && fps_diff == o.fps_diff && vp8 == o.vp8 && + vp9 == o.vp9 && h264 == o.h264 && av1 == o.av1 && + generic == o.generic; + } + + // Example: + // WebRTC-Video-BalancedDegradationSettings/pixels:100|200|300,fps:5|15|25/ + // pixels <= 100 -> min framerate: 5 fps + // pixels <= 200 -> min framerate: 15 fps + // pixels <= 300 -> min framerate: 25 fps + // + // WebRTC-Video-BalancedDegradationSettings/pixels:100|200|300, + // fps:5|15|25, // Min framerate. + // kbps:0|60|70, // Min bitrate needed to adapt up. + // kbps_res:0|65|75/ // Min bitrate needed to adapt up in resolution. + // + // pixels: fps: kbps: kbps_res: + // 300 30 - - + // 300 25 70 kbps 75 kbps + // 200 25 70 kbps - + // 200 15 60 kbps 65 kbps + // 100 15 60 kbps - + // 100 5 + // optional optional + + int pixels = 0; // Video frame size. + // If the frame size is less than or equal to `pixels`: + int fps = 0; // Min framerate to be used. + int kbps = 0; // Min bitrate needed to adapt up (resolution/fps). + int kbps_res = 0; // Min bitrate needed to adapt up in resolution. + int fps_diff = kNoFpsDiff; // Min fps reduction needed (input fps - `fps`) + // w/o triggering a new subsequent downgrade + // check. + CodecTypeSpecific vp8; + CodecTypeSpecific vp9; + CodecTypeSpecific h264; + CodecTypeSpecific av1; + CodecTypeSpecific generic; + }; + + // Returns configurations from field trial on success (default on failure). + std::vector GetConfigs() const; + + // Gets the min/max framerate from `configs_` based on `pixels`. + int MinFps(VideoCodecType type, int pixels) const; + int MaxFps(VideoCodecType type, int pixels) const; + + // Checks if quality can be increased based on `pixels` and `bitrate_bps`. + bool CanAdaptUp(VideoCodecType type, int pixels, uint32_t bitrate_bps) const; + bool CanAdaptUpResolution(VideoCodecType type, + int pixels, + uint32_t bitrate_bps) const; + + // Gets the min framerate diff from `configs_` based on `pixels`. + std::optional MinFpsDiff(int pixels) const; + + // Gets QpThresholds for the codec `type` based on `pixels`. + std::optional GetQpThresholds(VideoCodecType type, + int pixels) const; + + private: + std::optional GetMinFpsConfig(int pixels) const; + std::optional GetMaxFpsConfig(int pixels) const; + Config GetConfig(int pixels) const; + + std::vector configs_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_BALANCED_DEGRADATION_SETTINGS_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/encoder_info_settings.h b/pkg/apm/webrtc/rtc_base/experiments/encoder_info_settings.h new file mode 100644 index 00000000..a8d00beb --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/encoder_info_settings.h @@ -0,0 +1,103 @@ +/* + * Copyright 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_ +#define RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/field_trials_view.h" +#include "api/video_codecs/video_encoder.h" +#include "rtc_base/experiments/field_trial_parser.h" + +namespace webrtc { + +class EncoderInfoSettings { + public: + virtual ~EncoderInfoSettings(); + + // Bitrate limits per resolution. + struct BitrateLimit { + int frame_size_pixels = 0; // The video frame size. + int min_start_bitrate_bps = 0; // The minimum bitrate to start encoding. + int min_bitrate_bps = 0; // The minimum bitrate. + int max_bitrate_bps = 0; // The maximum bitrate. + }; + + std::optional requested_resolution_alignment() const; + bool apply_alignment_to_all_simulcast_layers() const { + return apply_alignment_to_all_simulcast_layers_.Get(); + } + std::vector resolution_bitrate_limits() + const { + return resolution_bitrate_limits_; + } + + static std::vector + GetDefaultSinglecastBitrateLimits(VideoCodecType codec_type); + + static std::optional + GetDefaultSinglecastBitrateLimitsForResolution(VideoCodecType codec_type, + int frame_size_pixels); + + static std::vector + GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted(VideoCodecType codec_type); + + static std::optional + GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted( + std::optional frame_size_pixels, + const std::vector& + resolution_bitrate_limits); + + protected: + EncoderInfoSettings(const FieldTrialsView& field_trials, + absl::string_view name); + + private: + FieldTrialOptional requested_resolution_alignment_; + FieldTrialFlag apply_alignment_to_all_simulcast_layers_; + std::vector resolution_bitrate_limits_; +}; + +// EncoderInfo settings for SimulcastEncoderAdapter. +class SimulcastEncoderAdapterEncoderInfoSettings : public EncoderInfoSettings { + public: + explicit SimulcastEncoderAdapterEncoderInfoSettings( + const FieldTrialsView& field_trials); + ~SimulcastEncoderAdapterEncoderInfoSettings() override {} +}; + +// EncoderInfo settings for LibvpxVp8Encoder. +class LibvpxVp8EncoderInfoSettings : public EncoderInfoSettings { + public: + explicit LibvpxVp8EncoderInfoSettings(const FieldTrialsView& field_trials); + ~LibvpxVp8EncoderInfoSettings() override {} +}; + +// EncoderInfo settings for LibvpxVp9Encoder. +class LibvpxVp9EncoderInfoSettings : public EncoderInfoSettings { + public: + explicit LibvpxVp9EncoderInfoSettings(const FieldTrialsView& field_trials); + ~LibvpxVp9EncoderInfoSettings() override {} +}; + +// EncoderInfo settings for LibaomAv1Encoder. +class LibaomAv1EncoderInfoSettings : public EncoderInfoSettings { + public: + explicit LibaomAv1EncoderInfoSettings(const FieldTrialsView& field_trials); + ~LibaomAv1EncoderInfoSettings() override {} +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/experiments.go b/pkg/apm/webrtc/rtc_base/experiments/experiments.go new file mode 100644 index 00000000..3998e583 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/experiments.go @@ -0,0 +1,10 @@ +//go:build console + +package experiments + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/rtc_base/experiments/field_trial_list.h b/pkg/apm/webrtc/rtc_base/experiments/field_trial_list.h new file mode 100644 index 00000000..910f2540 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/field_trial_list.h @@ -0,0 +1,226 @@ +/* + * 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 RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_ +#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_ + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/experiments/field_trial_parser.h" +#include "rtc_base/string_encode.h" + +// List support for field trial strings. FieldTrialList and FieldTrialStructList +// are used similarly to the other FieldTrialParameters, but take a variable +// number of parameters. A FieldTrialList parses a |-delimeted string into a +// list of T, using ParseTypedParameter to parse the individual tokens. +// Example string: "my_list:1|2|3,empty_list,other_list:aardvark". + +// A FieldTrialStructList combines multiple lists into a list-of-structs. It +// ensures that all its sublists parse correctly and have the same length, then +// uses user-supplied accessor functions to write those elements into structs of +// a user-supplied type. + +// See the unit test for usage and behavior. + +namespace webrtc { + +class FieldTrialListBase : public FieldTrialParameterInterface { + protected: + friend class FieldTrialListWrapper; + explicit FieldTrialListBase(absl::string_view key); + + bool Failed() const; + bool Used() const; + + virtual int Size() = 0; + + bool failed_; + bool parse_got_called_; +}; + +// This class represents a vector of type T. The elements are separated by a | +// and parsed using ParseTypedParameter. +template +class FieldTrialList : public FieldTrialListBase { + public: + explicit FieldTrialList(absl::string_view key) : FieldTrialList(key, {}) {} + FieldTrialList(absl::string_view key, std::initializer_list default_values) + : FieldTrialListBase(key), values_(default_values) {} + + std::vector Get() const { return values_; } + operator std::vector() const { return Get(); } + typename std::vector::const_reference operator[](size_t index) const { + return values_[index]; + } + const std::vector* operator->() const { return &values_; } + + protected: + bool Parse(std::optional str_value) override { + parse_got_called_ = true; + + if (!str_value) { + values_.clear(); + return true; + } + + std::vector new_values_; + + for (const absl::string_view token : split(str_value.value(), '|')) { + std::optional value = ParseTypedParameter(token); + if (value) { + new_values_.push_back(*value); + } else { + failed_ = true; + return false; + } + } + + values_.swap(new_values_); + return true; + } + + int Size() override { return values_.size(); } + + private: + std::vector values_; +}; + +class FieldTrialListWrapper { + public: + virtual ~FieldTrialListWrapper() = default; + + // Takes the element at the given index in the wrapped list and writes it to + // the given struct. + virtual void WriteElement(void* struct_to_write, int index) = 0; + + virtual FieldTrialListBase* GetList() = 0; + + int Length(); + + // Returns true iff the wrapped list has failed to parse at least one token. + bool Failed(); + + bool Used(); + + protected: + FieldTrialListWrapper() = default; +}; + +namespace field_trial_list_impl { +// The LambdaTypeTraits struct provides type information about lambdas in the +// template expressions below. +template +struct LambdaTypeTraits : public LambdaTypeTraits {}; + +template +struct LambdaTypeTraits { + using ret = RetType; + using src = SourceType; +}; + +template +struct TypedFieldTrialListWrapper : FieldTrialListWrapper { + public: + TypedFieldTrialListWrapper(absl::string_view key, + std::function sink) + : list_(key), sink_(sink) {} + + void WriteElement(void* struct_to_write, int index) override { + sink_(struct_to_write, list_[index]); + } + + FieldTrialListBase* GetList() override { return &list_; } + + private: + FieldTrialList list_; + std::function sink_; +}; + +} // namespace field_trial_list_impl + +template > +FieldTrialListWrapper* FieldTrialStructMember(absl::string_view key, + F accessor) { + return new field_trial_list_impl::TypedFieldTrialListWrapper< + typename Traits::ret>(key, [accessor](void* s, typename Traits::ret t) { + *accessor(static_cast(s)) = t; + }); +} + +// This base class is here to reduce the amount of code we have to generate for +// each type of FieldTrialStructList. +class FieldTrialStructListBase : public FieldTrialParameterInterface { + protected: + FieldTrialStructListBase( + std::initializer_list sub_lists) + : FieldTrialParameterInterface(""), sub_lists_() { + // Take ownership of the list wrappers generated by FieldTrialStructMember + // on the call site. + for (FieldTrialListWrapper* const* it = sub_lists.begin(); + it != sub_lists.end(); it++) { + sub_parameters_.push_back((*it)->GetList()); + sub_lists_.push_back(std::unique_ptr(*it)); + } + } + + // Check that all of our sublists that were in the field trial string had the + // same number of elements. If they do, we return that length. If they had + // different lengths, any sublist had parse failures or no sublists had + // user-supplied values, we return -1. + int ValidateAndGetLength(); + + bool Parse(std::optional str_value) override; + + std::vector> sub_lists_; +}; + +template +class FieldTrialStructList : public FieldTrialStructListBase { + public: + FieldTrialStructList(std::initializer_list l, + std::initializer_list default_list) + : FieldTrialStructListBase(l), values_(default_list) {} + + std::vector Get() const { return values_; } + operator std::vector() const { return Get(); } + const S& operator[](size_t index) const { return values_[index]; } + const std::vector* operator->() const { return &values_; } + + protected: + void ParseDone() override { + int length = ValidateAndGetLength(); + + if (length == -1) + return; + + std::vector new_values(length, S()); + + for (std::unique_ptr& li : sub_lists_) { + if (li->Used()) { + for (int i = 0; i < length; i++) { + li->WriteElement(&new_values[i], i); + } + } + } + + values_.swap(new_values); + } + + private: + std::vector values_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/field_trial_parser.cc b/pkg/apm/webrtc/rtc_base/experiments/field_trial_parser.cc new file mode 100644 index 00000000..1288c72c --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/field_trial_parser.cc @@ -0,0 +1,260 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "rtc_base/experiments/field_trial_parser.h" + +#include + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { + +FieldTrialParameterInterface::FieldTrialParameterInterface( + absl::string_view key) + : key_(key) {} +FieldTrialParameterInterface::~FieldTrialParameterInterface() { + RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_ + << "' never used."; +} + +void ParseFieldTrial( + std::initializer_list fields, + absl::string_view trial_string) { + std::map field_map; + FieldTrialParameterInterface* keyless_field = nullptr; + for (FieldTrialParameterInterface* field : fields) { + field->MarkAsUsed(); + if (!field->sub_parameters_.empty()) { + for (FieldTrialParameterInterface* sub_field : field->sub_parameters_) { + RTC_DCHECK(!sub_field->key_.empty()); + sub_field->MarkAsUsed(); + field_map[sub_field->key_] = sub_field; + } + continue; + } + + if (field->key_.empty()) { + RTC_DCHECK(!keyless_field); + keyless_field = field; + } else { + field_map[field->key_] = field; + } + } + bool logged_unknown_key = false; + + absl::string_view tail = trial_string; + while (!tail.empty()) { + size_t key_end = tail.find_first_of(",:"); + absl::string_view key = tail.substr(0, key_end); + std::optional opt_value; + if (key_end == absl::string_view::npos) { + tail = ""; + } else if (tail[key_end] == ':') { + tail = tail.substr(key_end + 1); + size_t value_end = tail.find(','); + opt_value.emplace(tail.substr(0, value_end)); + if (value_end == absl::string_view::npos) { + tail = ""; + } else { + tail = tail.substr(value_end + 1); + } + } else { + RTC_DCHECK_EQ(tail[key_end], ','); + tail = tail.substr(key_end + 1); + } + + auto field = field_map.find(key); + if (field != field_map.end()) { + if (!field->second->Parse(std::move(opt_value))) { + RTC_LOG(LS_WARNING) << "Failed to read field with key: '" << key + << "' in trial: \"" << trial_string << "\""; + } + } else if (!opt_value && keyless_field && !key.empty()) { + if (!keyless_field->Parse(std::string(key))) { + RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '" + << key << "' in trial: \"" << trial_string << "\""; + } + } else if (key.empty() || key[0] != '_') { + // "_" is be used to prefix keys that are part of the string for + // debugging purposes but not neccessarily used. + // e.g. WebRTC-Experiment/param: value, _DebuggingString + if (!logged_unknown_key) { + RTC_LOG(LS_INFO) << "No field with key: '" << key + << "' (found in trial: \"" << trial_string << "\")"; + std::string valid_keys; + for (const auto& f : field_map) { + valid_keys.append(f.first.data(), f.first.size()); + valid_keys += ", "; + } + RTC_LOG(LS_INFO) << "Valid keys are: " << valid_keys; + logged_unknown_key = true; + } + } + } + + for (FieldTrialParameterInterface* field : fields) { + field->ParseDone(); + } +} + +template <> +std::optional ParseTypedParameter(absl::string_view str) { + if (str == "true" || str == "1") { + return true; + } else if (str == "false" || str == "0") { + return false; + } + return std::nullopt; +} + +template <> +std::optional ParseTypedParameter(absl::string_view str) { + double value; + char unit[2]{0, 0}; + if (sscanf(std::string(str).c_str(), "%lf%1s", &value, unit) >= 1) { + if (unit[0] == '%') + return value / 100; + return value; + } else { + return std::nullopt; + } +} + +template <> +std::optional ParseTypedParameter(absl::string_view str) { + int64_t value; + if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) { + if (IsValueInRangeForNumericType(value)) { + return static_cast(value); + } + } + return std::nullopt; +} + +template <> +std::optional ParseTypedParameter(absl::string_view str) { + int64_t value; + if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) { + if (IsValueInRangeForNumericType(value)) { + return static_cast(value); + } + } + return std::nullopt; +} + +template <> +std::optional ParseTypedParameter( + absl::string_view str) { + return std::string(str); +} + +template <> +std::optional> ParseTypedParameter>( + absl::string_view str) { + return ParseOptionalParameter(str); +} +template <> +std::optional> ParseTypedParameter>( + absl::string_view str) { + return ParseOptionalParameter(str); +} +template <> +std::optional> +ParseTypedParameter>(absl::string_view str) { + return ParseOptionalParameter(str); +} +template <> +std::optional> ParseTypedParameter>( + absl::string_view str) { + return ParseOptionalParameter(str); +} + +FieldTrialFlag::FieldTrialFlag(absl::string_view key) + : FieldTrialFlag(key, false) {} + +FieldTrialFlag::FieldTrialFlag(absl::string_view key, bool default_value) + : FieldTrialParameterInterface(key), value_(default_value) {} + +bool FieldTrialFlag::Get() const { + return value_; +} + +webrtc::FieldTrialFlag::operator bool() const { + return value_; +} + +bool FieldTrialFlag::Parse(std::optional str_value) { + // Only set the flag if there is no argument provided. + if (str_value) { + std::optional opt_value = ParseTypedParameter(*str_value); + if (!opt_value) + return false; + value_ = *opt_value; + } else { + value_ = true; + } + return true; +} + +AbstractFieldTrialEnum::AbstractFieldTrialEnum( + absl::string_view key, + int default_value, + std::map mapping) + : FieldTrialParameterInterface(key), + value_(default_value), + enum_mapping_(mapping) { + for (auto& key_val : enum_mapping_) + valid_values_.insert(key_val.second); +} +AbstractFieldTrialEnum::AbstractFieldTrialEnum(const AbstractFieldTrialEnum&) = + default; +AbstractFieldTrialEnum::~AbstractFieldTrialEnum() = default; + +bool AbstractFieldTrialEnum::Parse(std::optional str_value) { + if (str_value) { + auto it = enum_mapping_.find(*str_value); + if (it != enum_mapping_.end()) { + value_ = it->second; + return true; + } + std::optional value = ParseTypedParameter(*str_value); + if (value.has_value() && + (valid_values_.find(*value) != valid_values_.end())) { + value_ = *value; + return true; + } + } + return false; +} + +template class FieldTrialParameter; +template class FieldTrialParameter; +template class FieldTrialParameter; +template class FieldTrialParameter; +template class FieldTrialParameter; + +template class FieldTrialConstrained; +template class FieldTrialConstrained; +template class FieldTrialConstrained; + +template class FieldTrialOptional; +template class FieldTrialOptional; +template class FieldTrialOptional; +template class FieldTrialOptional; +template class FieldTrialOptional; + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/experiments/field_trial_parser.h b/pkg/apm/webrtc/rtc_base/experiments/field_trial_parser.h new file mode 100644 index 00000000..890da805 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/field_trial_parser.h @@ -0,0 +1,290 @@ +/* + * 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 RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_ +#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_ + +#include + +#include +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" + +// Field trial parser functionality. Provides funcitonality to parse field trial +// argument strings in key:value format. Each parameter is described using +// key:value, parameters are separated with a ,. Values can't include the comma +// character, since there's no quote facility. For most types, white space is +// ignored. Parameters are declared with a given type for which an +// implementation of ParseTypedParameter should be provided. The +// ParseTypedParameter implementation is given whatever is between the : and the +// ,. If the key is provided without : a FieldTrialOptional will use nullopt. + +// Example string: "my_optional,my_int:3,my_string:hello" + +// For further description of usage and behavior, see the examples in the unit +// tests. + +namespace webrtc { +class FieldTrialParameterInterface { + public: + virtual ~FieldTrialParameterInterface(); + std::string key() const { return key_; } + + protected: + // Protected to allow implementations to provide assignment and copy. + FieldTrialParameterInterface(const FieldTrialParameterInterface&) = default; + FieldTrialParameterInterface& operator=(const FieldTrialParameterInterface&) = + default; + explicit FieldTrialParameterInterface(absl::string_view key); + friend void ParseFieldTrial( + std::initializer_list fields, + absl::string_view trial_string); + void MarkAsUsed() { used_ = true; } + virtual bool Parse(std::optional str_value) = 0; + + virtual void ParseDone() {} + + std::vector sub_parameters_; + + private: + std::string key_; + bool used_ = false; +}; + +// ParseFieldTrial function parses the given string and fills the given fields +// with extracted values if available. +void ParseFieldTrial( + std::initializer_list fields, + absl::string_view trial_string); + +// Specialize this in code file for custom types. Should return std::nullopt if +// the given string cannot be properly parsed. +template +std::optional ParseTypedParameter(absl::string_view); + +// This class uses the ParseTypedParameter function to implement a parameter +// implementation with an enforced default value. +template +class FieldTrialParameter : public FieldTrialParameterInterface { + public: + FieldTrialParameter(absl::string_view key, T default_value) + : FieldTrialParameterInterface(key), value_(default_value) {} + T Get() const { return value_; } + operator T() const { return Get(); } + const T* operator->() const { return &value_; } + + void SetForTest(T value) { value_ = value; } + + protected: + bool Parse(std::optional str_value) override { + if (str_value) { + std::optional value = ParseTypedParameter(*str_value); + if (value.has_value()) { + value_ = value.value(); + return true; + } + } + return false; + } + + private: + T value_; +}; + +// This class uses the ParseTypedParameter function to implement a parameter +// implementation with an enforced default value and a range constraint. Values +// outside the configured range will be ignored. +template +class FieldTrialConstrained : public FieldTrialParameterInterface { + public: + FieldTrialConstrained(absl::string_view key, + T default_value, + std::optional lower_limit, + std::optional upper_limit) + : FieldTrialParameterInterface(key), + value_(default_value), + lower_limit_(lower_limit), + upper_limit_(upper_limit) {} + T Get() const { return value_; } + operator T() const { return Get(); } + const T* operator->() const { return &value_; } + + protected: + bool Parse(std::optional str_value) override { + if (str_value) { + std::optional value = ParseTypedParameter(*str_value); + if (value && (!lower_limit_ || *value >= *lower_limit_) && + (!upper_limit_ || *value <= *upper_limit_)) { + value_ = *value; + return true; + } + } + return false; + } + + private: + T value_; + std::optional lower_limit_; + std::optional upper_limit_; +}; + +class AbstractFieldTrialEnum : public FieldTrialParameterInterface { + public: + AbstractFieldTrialEnum(absl::string_view key, + int default_value, + std::map mapping); + ~AbstractFieldTrialEnum() override; + AbstractFieldTrialEnum(const AbstractFieldTrialEnum&); + + protected: + bool Parse(std::optional str_value) override; + + protected: + int value_; + std::map enum_mapping_; + std::set valid_values_; +}; + +// The FieldTrialEnum class can be used to quickly define a parser for a +// specific enum. It handles values provided as integers and as strings if a +// mapping is provided. +template +class FieldTrialEnum : public AbstractFieldTrialEnum { + public: + FieldTrialEnum(absl::string_view key, + T default_value, + std::map mapping) + : AbstractFieldTrialEnum(key, + static_cast(default_value), + ToIntMap(mapping)) {} + T Get() const { return static_cast(value_); } + operator T() const { return Get(); } + + private: + static std::map ToIntMap(std::map mapping) { + std::map res; + for (const auto& it : mapping) + res[it.first] = static_cast(it.second); + return res; + } +}; + +// This class uses the ParseTypedParameter function to implement an optional +// parameter implementation that can default to std::nullopt. +template +class FieldTrialOptional : public FieldTrialParameterInterface { + public: + explicit FieldTrialOptional(absl::string_view key) + : FieldTrialParameterInterface(key) {} + FieldTrialOptional(absl::string_view key, std::optional default_value) + : FieldTrialParameterInterface(key), value_(default_value) {} + std::optional GetOptional() const { return value_; } + const T& Value() const { return value_.value(); } + const T& operator*() const { return value_.value(); } + const T* operator->() const { return &value_.value(); } + explicit operator bool() const { return value_.has_value(); } + + protected: + bool Parse(std::optional str_value) override { + if (str_value) { + std::optional value = ParseTypedParameter(*str_value); + if (!value.has_value()) + return false; + value_ = value.value(); + } else { + value_ = std::nullopt; + } + return true; + } + + private: + std::optional value_; +}; + +// Equivalent to a FieldTrialParameter in the case that both key and value +// are present. If key is missing, evaluates to false. If key is present, but no +// explicit value is provided, the flag evaluates to true. +class FieldTrialFlag : public FieldTrialParameterInterface { + public: + explicit FieldTrialFlag(absl::string_view key); + FieldTrialFlag(absl::string_view key, bool default_value); + bool Get() const; + explicit operator bool() const; + + protected: + bool Parse(std::optional str_value) override; + + private: + bool value_; +}; + +template +std::optional> ParseOptionalParameter(absl::string_view str) { + if (str.empty()) + return std::optional(); + auto parsed = ParseTypedParameter(str); + if (parsed.has_value()) + return parsed; + return std::nullopt; +} + +template <> +std::optional ParseTypedParameter(absl::string_view str); +template <> +std::optional ParseTypedParameter(absl::string_view str); +template <> +std::optional ParseTypedParameter(absl::string_view str); +template <> +std::optional ParseTypedParameter(absl::string_view str); +template <> +std::optional ParseTypedParameter( + absl::string_view str); + +template <> +std::optional> ParseTypedParameter>( + absl::string_view str); +template <> +std::optional> ParseTypedParameter>( + absl::string_view str); +template <> +std::optional> +ParseTypedParameter>(absl::string_view str); +template <> +std::optional> ParseTypedParameter>( + absl::string_view str); + +// Accepts true, false, else parsed with sscanf %i, true if != 0. +extern template class FieldTrialParameter; +// Interpreted using sscanf %lf. +extern template class FieldTrialParameter; +// Interpreted using sscanf %i. +extern template class FieldTrialParameter; +// Interpreted using sscanf %u. +extern template class FieldTrialParameter; +// Using the given value as is. +extern template class FieldTrialParameter; + +extern template class FieldTrialConstrained; +extern template class FieldTrialConstrained; +extern template class FieldTrialConstrained; + +extern template class FieldTrialOptional; +extern template class FieldTrialOptional; +extern template class FieldTrialOptional; +extern template class FieldTrialOptional; +extern template class FieldTrialOptional; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/field_trial_units.cc b/pkg/apm/webrtc/rtc_base/experiments/field_trial_units.cc new file mode 100644 index 00000000..ddb954a2 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/field_trial_units.cc @@ -0,0 +1,115 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "rtc_base/experiments/field_trial_units.h" + +#include + +#include +#include +#include + +#include "absl/strings/string_view.h" + +// Large enough to fit "seconds", the longest supported unit name. +#define RTC_TRIAL_UNIT_LENGTH_STR "7" +#define RTC_TRIAL_UNIT_SIZE 8 + +namespace webrtc { +namespace { + +struct ValueWithUnit { + double value; + std::string unit; +}; + +std::optional ParseValueWithUnit(absl::string_view str) { + if (str == "inf") { + return ValueWithUnit{std::numeric_limits::infinity(), ""}; + } else if (str == "-inf") { + return ValueWithUnit{-std::numeric_limits::infinity(), ""}; + } else { + double double_val; + char unit_char[RTC_TRIAL_UNIT_SIZE]; + unit_char[0] = 0; + if (sscanf(std::string(str).c_str(), "%lf%" RTC_TRIAL_UNIT_LENGTH_STR "s", + &double_val, unit_char) >= 1) { + return ValueWithUnit{double_val, unit_char}; + } + } + return std::nullopt; +} +} // namespace + +template <> +std::optional ParseTypedParameter(absl::string_view str) { + std::optional result = ParseValueWithUnit(str); + if (result) { + if (result->unit.empty() || result->unit == "kbps") { + return DataRate::KilobitsPerSec(result->value); + } else if (result->unit == "bps") { + return DataRate::BitsPerSec(result->value); + } + } + return std::nullopt; +} + +template <> +std::optional ParseTypedParameter(absl::string_view str) { + std::optional result = ParseValueWithUnit(str); + if (result) { + if (result->unit.empty() || result->unit == "bytes") + return DataSize::Bytes(result->value); + } + return std::nullopt; +} + +template <> +std::optional ParseTypedParameter(absl::string_view str) { + std::optional result = ParseValueWithUnit(str); + if (result) { + if (result->unit == "s" || result->unit == "seconds") { + return TimeDelta::Seconds(result->value); + } else if (result->unit == "us") { + return TimeDelta::Micros(result->value); + } else if (result->unit.empty() || result->unit == "ms") { + return TimeDelta::Millis(result->value); + } + } + return std::nullopt; +} + +template <> +std::optional> +ParseTypedParameter>(absl::string_view str) { + return ParseOptionalParameter(str); +} +template <> +std::optional> +ParseTypedParameter>(absl::string_view str) { + return ParseOptionalParameter(str); +} +template <> +std::optional> +ParseTypedParameter>(absl::string_view str) { + return ParseOptionalParameter(str); +} + +template class FieldTrialParameter; +template class FieldTrialParameter; +template class FieldTrialParameter; + +template class FieldTrialConstrained; +template class FieldTrialConstrained; +template class FieldTrialConstrained; + +template class FieldTrialOptional; +template class FieldTrialOptional; +template class FieldTrialOptional; +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/experiments/field_trial_units.h b/pkg/apm/webrtc/rtc_base/experiments/field_trial_units.h new file mode 100644 index 00000000..0bab5fee --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/field_trial_units.h @@ -0,0 +1,41 @@ +/* + * 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 RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_ +#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_ + +#include "absl/strings/string_view.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "rtc_base/experiments/field_trial_parser.h" + +namespace webrtc { + +template <> +std::optional ParseTypedParameter(absl::string_view str); +template <> +std::optional ParseTypedParameter(absl::string_view str); +template <> +std::optional ParseTypedParameter(absl::string_view str); + +extern template class FieldTrialParameter; +extern template class FieldTrialParameter; +extern template class FieldTrialParameter; + +extern template class FieldTrialConstrained; +extern template class FieldTrialConstrained; +extern template class FieldTrialConstrained; + +extern template class FieldTrialOptional; +extern template class FieldTrialOptional; +extern template class FieldTrialOptional; +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/keyframe_interval_settings.h b/pkg/apm/webrtc/rtc_base/experiments/keyframe_interval_settings.h new file mode 100644 index 00000000..a9c1971a --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/keyframe_interval_settings.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_ +#define RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_ + +#include + +#include "api/field_trials_view.h" +#include "rtc_base/experiments/field_trial_parser.h" + +namespace webrtc { + +// TODO: bugs.webrtc.org/42220470 - Remove and replace with proper configuration +// parameter, or move to using FIR if intent is to avoid triggering multiple +// times to PLIs corresponding to the same request when RTT is large. +class KeyframeIntervalSettings final { + public: + explicit KeyframeIntervalSettings(const FieldTrialsView& key_value_config); + + // Sender side. + // The encoded keyframe send rate is <= 1/MinKeyframeSendIntervalMs(). + std::optional MinKeyframeSendIntervalMs() const; + + private: + FieldTrialOptional min_keyframe_send_interval_ms_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/min_video_bitrate_experiment.h b/pkg/apm/webrtc/rtc_base/experiments/min_video_bitrate_experiment.h new file mode 100644 index 00000000..af6acb3c --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/min_video_bitrate_experiment.h @@ -0,0 +1,32 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_EXPERIMENTS_MIN_VIDEO_BITRATE_EXPERIMENT_H_ +#define RTC_BASE_EXPERIMENTS_MIN_VIDEO_BITRATE_EXPERIMENT_H_ + +#include + +#include "api/field_trials_view.h" +#include "api/units/data_rate.h" +#include "api/video/video_codec_type.h" + +namespace webrtc { + +extern const int kDefaultMinVideoBitrateBps; + +// Return the experiment-driven minimum video bitrate. +// If no experiment is effective, returns nullopt. +std::optional GetExperimentalMinVideoBitrate( + const FieldTrialsView& field_trials, + VideoCodecType type); + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_MIN_VIDEO_BITRATE_EXPERIMENT_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/normalize_simulcast_size_experiment.h b/pkg/apm/webrtc/rtc_base/experiments/normalize_simulcast_size_experiment.h new file mode 100644 index 00000000..79eada10 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/normalize_simulcast_size_experiment.h @@ -0,0 +1,28 @@ +/* + * 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 RTC_BASE_EXPERIMENTS_NORMALIZE_SIMULCAST_SIZE_EXPERIMENT_H_ +#define RTC_BASE_EXPERIMENTS_NORMALIZE_SIMULCAST_SIZE_EXPERIMENT_H_ + +#include + +#include "api/field_trials_view.h" + +namespace webrtc { +class NormalizeSimulcastSizeExperiment { + public: + // Returns the base two exponent from field trial. + static std::optional GetBase2Exponent( + const FieldTrialsView& field_trials); +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_NORMALIZE_SIMULCAST_SIZE_EXPERIMENT_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/quality_scaler_settings.h b/pkg/apm/webrtc/rtc_base/experiments/quality_scaler_settings.h new file mode 100644 index 00000000..428f18f0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/quality_scaler_settings.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_ +#define RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_ + +#include + +#include "api/field_trials_view.h" +#include "rtc_base/experiments/field_trial_parser.h" + +namespace webrtc { + +class QualityScalerSettings final { + public: + explicit QualityScalerSettings(const FieldTrialsView& field_trials); + + std::optional SamplingPeriodMs() const; + std::optional AverageQpWindow() const; + std::optional MinFrames() const; + std::optional InitialScaleFactor() const; + std::optional ScaleFactor() const; + std::optional InitialBitrateIntervalMs() const; + std::optional InitialBitrateFactor() const; + + private: + FieldTrialOptional sampling_period_ms_; + FieldTrialOptional average_qp_window_; + FieldTrialOptional min_frames_; + FieldTrialOptional initial_scale_factor_; + FieldTrialOptional scale_factor_; + FieldTrialOptional initial_bitrate_interval_ms_; + FieldTrialOptional initial_bitrate_factor_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/quality_scaling_experiment.h b/pkg/apm/webrtc/rtc_base/experiments/quality_scaling_experiment.h new file mode 100644 index 00000000..0d197c14 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/quality_scaling_experiment.h @@ -0,0 +1,62 @@ +/* + * 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 RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_ +#define RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_ + +#include + +#include "api/field_trials_view.h" +#include "api/video_codecs/video_encoder.h" + +namespace webrtc { +class QualityScalingExperiment { + public: + struct Settings { + int vp8_low; // VP8: low QP threshold. + int vp8_high; // VP8: high QP threshold. + int vp9_low; // VP9: low QP threshold. + int vp9_high; // VP9: high QP threshold. + int h264_low; // H264: low QP threshold. + int h264_high; // H264: high QP threshold. + int generic_low; // Generic: low QP threshold. + int generic_high; // Generic: high QP threshold. + float alpha_high; // `alpha_` for ExpFilter used when checking high QP. + float alpha_low; // `alpha_` for ExpFilter used when checking low QP. + int drop; // >0 sets `use_all_drop_reasons` to true. + }; + + // Used by QualityScaler. + struct Config { + float alpha_high = 0.9995f; + float alpha_low = 0.9999f; + // If set, all type of dropped frames are used. + // Otherwise only dropped frames by MediaOptimization are used. + bool use_all_drop_reasons = false; + }; + + // Returns true if the experiment is enabled. + static bool Enabled(const FieldTrialsView& field_trials); + + // Returns settings from field trial. + static std::optional ParseSettings( + const FieldTrialsView& field_trials); + + // Returns QpThresholds for the `codec_type`. + static std::optional GetQpThresholds( + VideoCodecType codec_type, + const FieldTrialsView& field_trials); + + // Returns parsed values. If the parsing fails, default values are returned. + static Config GetConfig(const FieldTrialsView& field_trials); +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/rate_control_settings.h b/pkg/apm/webrtc/rtc_base/experiments/rate_control_settings.h new file mode 100644 index 00000000..d48da1c4 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/rate_control_settings.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_EXPERIMENTS_RATE_CONTROL_SETTINGS_H_ +#define RTC_BASE_EXPERIMENTS_RATE_CONTROL_SETTINGS_H_ + +#include + +#include "api/field_trials_view.h" +#include "api/units/data_size.h" +#include "api/video_codecs/video_codec.h" +#include "rtc_base/experiments/struct_parameters_parser.h" +#include "video/config/video_encoder_config.h" + +namespace webrtc { + +struct CongestionWindowConfig { + static constexpr char kKey[] = "WebRTC-CongestionWindow"; + std::optional queue_size_ms; + std::optional min_bitrate_bps; + std::optional initial_data_window; + bool drop_frame_only = false; + std::unique_ptr Parser(); + static CongestionWindowConfig Parse(absl::string_view config); +}; + +struct VideoRateControlConfig { + static constexpr char kKey[] = "WebRTC-VideoRateControl"; + std::optional pacing_factor; + bool alr_probing = false; + std::optional vp8_qp_max; + std::optional vp8_min_pixels; + bool trust_vp8 = true; + bool trust_vp9 = true; + bool bitrate_adjuster = true; + bool adjuster_use_headroom = true; + bool vp8_s0_boost = false; + bool vp8_base_heavy_tl3_alloc = false; + + std::unique_ptr Parser(); +}; + +class RateControlSettings final { + public: + explicit RateControlSettings(const FieldTrialsView& key_value_config); + RateControlSettings(RateControlSettings&&); + ~RateControlSettings(); + + // When CongestionWindowPushback is enabled, the pacer is oblivious to + // the congestion window. The relation between outstanding data and + // the congestion window affects encoder allocations directly. + bool UseCongestionWindow() const; + int64_t GetCongestionWindowAdditionalTimeMs() const; + bool UseCongestionWindowPushback() const; + bool UseCongestionWindowDropFrameOnly() const; + uint32_t CongestionWindowMinPushbackTargetBitrateBps() const; + std::optional CongestionWindowInitialDataWindow() const; + + std::optional GetPacingFactor() const; + bool UseAlrProbing() const; + + std::optional LibvpxVp8QpMax() const; + std::optional LibvpxVp8MinPixels() const; + bool LibvpxVp8TrustedRateController() const; + bool Vp8BoostBaseLayerQuality() const; + bool Vp8DynamicRateSettings() const; + bool LibvpxVp9TrustedRateController() const; + bool Vp9DynamicRateSettings() const; + + bool Vp8BaseHeavyTl3RateAllocation() const; + + bool UseEncoderBitrateAdjuster() const; + bool BitrateAdjusterCanUseNetworkHeadroom() const; + + private: + CongestionWindowConfig congestion_window_config_; + VideoRateControlConfig video_config_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_RATE_CONTROL_SETTINGS_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/stable_target_rate_experiment.h b/pkg/apm/webrtc/rtc_base/experiments/stable_target_rate_experiment.h new file mode 100644 index 00000000..ced3eb8b --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/stable_target_rate_experiment.h @@ -0,0 +1,37 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_ +#define RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_ + +#include "api/field_trials_view.h" +#include "rtc_base/experiments/field_trial_parser.h" + +namespace webrtc { + +class StableTargetRateExperiment { + public: + explicit StableTargetRateExperiment(const FieldTrialsView& field_trials); + StableTargetRateExperiment(const StableTargetRateExperiment&); + StableTargetRateExperiment(StableTargetRateExperiment&&); + + bool IsEnabled() const; + double GetVideoHysteresisFactor() const; + double GetScreenshareHysteresisFactor() const; + + private: + FieldTrialParameter enabled_; + FieldTrialParameter video_hysteresis_factor_; + FieldTrialParameter screenshare_hysteresis_factor_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_ diff --git a/pkg/apm/webrtc/rtc_base/experiments/struct_parameters_parser.h b/pkg/apm/webrtc/rtc_base/experiments/struct_parameters_parser.h new file mode 100644 index 00000000..3d0b05bd --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/experiments/struct_parameters_parser.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_EXPERIMENTS_STRUCT_PARAMETERS_PARSER_H_ +#define RTC_BASE_EXPERIMENTS_STRUCT_PARAMETERS_PARSER_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "rtc_base/experiments/field_trial_parser.h" +#include "rtc_base/experiments/field_trial_units.h" +#include "rtc_base/string_encode.h" + +namespace webrtc { +namespace struct_parser_impl { +struct TypedMemberParser { + public: + bool (*parse)(absl::string_view src, void* target); + void (*encode)(const void* src, std::string* target); +}; + +struct MemberParameter { + const char* key; + void* member_ptr; + TypedMemberParser parser; +}; + +template +class TypedParser { + public: + static bool Parse(absl::string_view src, void* target); + static void Encode(const void* src, std::string* target); +}; + +// Instantiated in cc file to avoid duplication during compile. Add additional +// parsers as needed. Generally, try to use these suggested types even if the +// context where the value is used might require a different type. For instance, +// a size_t representing a packet size should use an int parameter as there's no +// need to support packet sizes larger than INT32_MAX. +extern template class TypedParser; +extern template class TypedParser; +extern template class TypedParser; +extern template class TypedParser; +extern template class TypedParser>; +extern template class TypedParser>; +extern template class TypedParser>; + +extern template class TypedParser; +extern template class TypedParser; +extern template class TypedParser; +extern template class TypedParser>; +extern template class TypedParser>; +extern template class TypedParser>; + +template +void AddMembers(MemberParameter* out, const char* key, T* member) { + *out = MemberParameter{ + key, member, + TypedMemberParser{&TypedParser::Parse, &TypedParser::Encode}}; +} + +template +void AddMembers(MemberParameter* out, + const char* key, + T* member, + Args... args) { + AddMembers(out, key, member); + AddMembers(++out, args...); +} +} // namespace struct_parser_impl + +class StructParametersParser { + public: + template + static std::unique_ptr Create(const char* first_key, + T* first_member, + Args... args) { + std::vector members( + sizeof...(args) / 2 + 1); + struct_parser_impl::AddMembers(&members.front(), std::move(first_key), + first_member, args...); + return absl::WrapUnique(new StructParametersParser(std::move(members))); + } + + void Parse(absl::string_view src); + std::string Encode() const; + + private: + explicit StructParametersParser( + std::vector members); + + std::vector members_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_STRUCT_PARAMETERS_PARSER_H_ diff --git a/pkg/apm/webrtc/rtc_base/file_rotating_stream.h b/pkg/apm/webrtc/rtc_base/file_rotating_stream.h new file mode 100644 index 00000000..de122efc --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/file_rotating_stream.h @@ -0,0 +1,183 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_FILE_ROTATING_STREAM_H_ +#define RTC_BASE_FILE_ROTATING_STREAM_H_ + +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/system/file_wrapper.h" + +namespace webrtc { + +// FileRotatingStream writes to a file in the directory specified in the +// constructor. It rotates the files once the current file is full. The +// individual file size and the number of files used is configurable in the +// constructor. Open() must be called before using this stream. +class FileRotatingStream { + public: + // Use this constructor for writing to a directory. Files in the directory + // matching the prefix will be deleted on open. + FileRotatingStream(absl::string_view dir_path, + absl::string_view file_prefix, + size_t max_file_size, + size_t num_files); + + virtual ~FileRotatingStream(); + + FileRotatingStream(const FileRotatingStream&) = delete; + FileRotatingStream& operator=(const FileRotatingStream&) = delete; + + bool IsOpen() const; + + bool Write(const void* data, size_t data_len); + bool Flush(); + void Close(); + + // Opens the appropriate file(s). Call this before using the stream. + bool Open(); + + // Disabling buffering causes writes to block until disk is updated. This is + // enabled by default for performance. + bool DisableBuffering(); + + // Below two methods are public for testing only. + + // Returns the path used for the i-th newest file, where the 0th file is the + // newest file. The file may or may not exist, this is just used for + // formatting. Index must be less than GetNumFiles(). + std::string GetFilePath(size_t index) const; + + // Returns the number of files that will used by this stream. + size_t GetNumFiles() const { return file_names_.size(); } + + protected: + void SetMaxFileSize(size_t size) { max_file_size_ = size; } + + size_t GetRotationIndex() const { return rotation_index_; } + + void SetRotationIndex(size_t index) { rotation_index_ = index; } + + virtual void OnRotation() {} + + private: + bool OpenCurrentFile(); + void CloseCurrentFile(); + + // Rotates the files by creating a new current file, renaming the + // existing files, and deleting the oldest one. e.g. + // file_0 -> file_1 + // file_1 -> file_2 + // file_2 -> delete + // create new file_0 + void RotateFiles(); + + // Private version of GetFilePath. + std::string GetFilePath(size_t index, size_t num_files) const; + + const std::string dir_path_; + const std::string file_prefix_; + + // File we're currently writing to. + FileWrapper file_; + // Convenience storage for file names so we don't generate them over and over. + std::vector file_names_; + size_t max_file_size_; + size_t current_file_index_; + // The rotation index indicates the index of the file that will be + // deleted first on rotation. Indices lower than this index will be rotated. + size_t rotation_index_; + // Number of bytes written to current file. We need this because with + // buffering the file size read from disk might not be accurate. + size_t current_bytes_written_; + bool disable_buffering_; +}; + +// CallSessionFileRotatingStream is meant to be used in situations where we will +// have limited disk space. Its purpose is to write logs up to a +// maximum size. Once the maximum size is exceeded, logs from the middle are +// deleted whereas logs from the beginning and end are preserved. The reason for +// this is because we anticipate that in WebRTC the beginning and end of the +// logs are most useful for call diagnostics. +// +// This implementation simply writes to a single file until +// `max_total_log_size` / 2 bytes are written to it, and subsequently writes to +// a set of rotating files. We do this by inheriting FileRotatingStream and +// setting the appropriate internal variables so that we don't delete the last +// (earliest) file on rotate, and that that file's size is bigger. +// +// Open() must be called before using this stream. + +// To read the logs produced by this class, one can use the companion class +// CallSessionFileRotatingStreamReader. +class CallSessionFileRotatingStream : public FileRotatingStream { + public: + // Use this constructor for writing to a directory. Files in the directory + // matching what's used by the stream will be deleted. `max_total_log_size` + // must be at least 4. + CallSessionFileRotatingStream(absl::string_view dir_path, + size_t max_total_log_size); + ~CallSessionFileRotatingStream() override {} + + CallSessionFileRotatingStream(const CallSessionFileRotatingStream&) = delete; + CallSessionFileRotatingStream& operator=( + const CallSessionFileRotatingStream&) = delete; + + protected: + void OnRotation() override; + + private: + static size_t GetRotatingLogSize(size_t max_total_log_size); + static size_t GetNumRotatingLogFiles(size_t max_total_log_size); + static const size_t kRotatingLogFileDefaultSize; + + const size_t max_total_log_size_; + size_t num_rotations_; +}; + +// This is a convenience class, to read all files produced by a +// FileRotatingStream, all in one go. Typical use calls GetSize and ReadData +// only once. The list of file names to read is based on the contents of the log +// directory at construction time. +class FileRotatingStreamReader { + public: + FileRotatingStreamReader(absl::string_view dir_path, + absl::string_view file_prefix); + ~FileRotatingStreamReader(); + size_t GetSize() const; + size_t ReadAll(void* buffer, size_t size) const; + + private: + std::vector file_names_; +}; + +class CallSessionFileRotatingStreamReader : public FileRotatingStreamReader { + public: + CallSessionFileRotatingStreamReader(absl::string_view dir_path); +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::CallSessionFileRotatingStream; +using ::webrtc::CallSessionFileRotatingStreamReader; +using ::webrtc::FileRotatingStream; +using ::webrtc::FileRotatingStreamReader; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_FILE_ROTATING_STREAM_H_ diff --git a/pkg/apm/webrtc/rtc_base/firewall_socket_server.h b/pkg/apm/webrtc/rtc_base/firewall_socket_server.h new file mode 100644 index 00000000..1aa61359 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/firewall_socket_server.h @@ -0,0 +1,146 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_FIREWALL_SOCKET_SERVER_H_ +#define RTC_BASE_FIREWALL_SOCKET_SERVER_H_ + +#include + +#include "rtc_base/ip_address.h" +#include "rtc_base/socket.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/socket_server.h" +#include "rtc_base/synchronization/mutex.h" + +namespace webrtc { + +class FirewallManager; + +// This SocketServer shim simulates a rule-based firewall server. + +enum FirewallProtocol { FP_UDP, FP_TCP, FP_ANY }; +enum FirewallDirection { FD_IN, FD_OUT, FD_ANY }; + +class FirewallSocketServer : public SocketServer { + public: + FirewallSocketServer(SocketServer* server, + FirewallManager* manager = nullptr, + bool should_delete_server = false); + ~FirewallSocketServer() override; + + SocketServer* socketserver() const { return server_; } + void set_socketserver(SocketServer* server) { + if (server_ && should_delete_server_) { + delete server_; + server_ = nullptr; + should_delete_server_ = false; + } + server_ = server; + } + + // Settings to control whether CreateSocket or Socket::Listen succeed. + void set_udp_sockets_enabled(bool enabled) { udp_sockets_enabled_ = enabled; } + void set_tcp_sockets_enabled(bool enabled) { tcp_sockets_enabled_ = enabled; } + bool tcp_listen_enabled() const { return tcp_listen_enabled_; } + void set_tcp_listen_enabled(bool enabled) { tcp_listen_enabled_ = enabled; } + + // Rules govern the behavior of Connect/Accept/Send/Recv attempts. + void AddRule(bool allow, + FirewallProtocol p = FP_ANY, + FirewallDirection d = FD_ANY, + const SocketAddress& addr = SocketAddress()); + void AddRule(bool allow, + FirewallProtocol p, + const SocketAddress& src, + const SocketAddress& dst); + void ClearRules(); + + bool Check(FirewallProtocol p, + const SocketAddress& src, + const SocketAddress& dst); + + // Set the IP addresses for which Bind will fail. By default this list is + // empty. This can be used to simulate a real OS that refuses to bind to + // addresses under various circumstances. + // + // No matter how many addresses are added (including INADDR_ANY), the server + // will still allow creating outgoing TCP connections, since they don't + // require explicitly binding a socket. + void SetUnbindableIps(const std::vector& unbindable_ips); + bool IsBindableIp(const IPAddress& ip); + + Socket* CreateSocket(int family, int type) override; + + void SetMessageQueue(Thread* queue) override; + bool Wait(TimeDelta max_wait_duration, bool process_io) override; + void WakeUp() override; + + Socket* WrapSocket(Socket* sock, int type); + + private: + SocketServer* server_; + FirewallManager* manager_; + Mutex mutex_; + struct Rule { + bool allow; + FirewallProtocol p; + FirewallDirection d; + SocketAddress src; + SocketAddress dst; + }; + std::vector rules_; + std::vector unbindable_ips_; + bool should_delete_server_; + bool udp_sockets_enabled_; + bool tcp_sockets_enabled_; + bool tcp_listen_enabled_; +}; + +// FirewallManager allows you to manage firewalls in multiple threads together + +class FirewallManager { + public: + FirewallManager(); + ~FirewallManager(); + + void AddServer(FirewallSocketServer* server); + void RemoveServer(FirewallSocketServer* server); + + void AddRule(bool allow, + FirewallProtocol p = FP_ANY, + FirewallDirection d = FD_ANY, + const SocketAddress& addr = SocketAddress()); + void ClearRules(); + + private: + Mutex mutex_; + std::vector servers_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::FD_ANY; +using ::webrtc::FD_IN; +using ::webrtc::FD_OUT; +using ::webrtc::FirewallDirection; +using ::webrtc::FirewallManager; +using ::webrtc::FirewallProtocol; +using ::webrtc::FirewallSocketServer; +using ::webrtc::FP_ANY; +using ::webrtc::FP_TCP; +using ::webrtc::FP_UDP; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_FIREWALL_SOCKET_SERVER_H_ diff --git a/pkg/apm/webrtc/rtc_base/frequency_tracker.h b/pkg/apm/webrtc/rtc_base/frequency_tracker.h new file mode 100644 index 00000000..6039c53a --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/frequency_tracker.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_FREQUENCY_TRACKER_H_ +#define RTC_BASE_FREQUENCY_TRACKER_H_ + +#include +#include + +#include + +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "rtc_base/rate_statistics.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { +// Class to estimate frequency (e.g. frame rate) over running window. +// Timestamps used in Update() and Rate() must never decrease for two +// consecutive calls. +// This class is thread unsafe. +class RTC_EXPORT FrequencyTracker { + public: + explicit FrequencyTracker(TimeDelta window_size); + + FrequencyTracker(const FrequencyTracker&) = default; + FrequencyTracker(FrequencyTracker&&) = default; + FrequencyTracker& operator=(const FrequencyTracker&) = delete; + FrequencyTracker& operator=(FrequencyTracker&&) = delete; + + ~FrequencyTracker() = default; + + // Reset instance to original state. + void Reset() { impl_.Reset(); } + + // Update rate with a new data point, moving averaging window as needed. + void Update(int64_t count, Timestamp now); + void Update(Timestamp now) { Update(1, now); } + + // Returns rate, moving averaging window as needed. + // Returns nullopt when rate can't be measured. + std::optional Rate(Timestamp now) const; + + private: + RateStatistics impl_; +}; +} // namespace webrtc + +#endif // RTC_BASE_FREQUENCY_TRACKER_H_ diff --git a/pkg/apm/webrtc/rtc_base/gtest_prod_util.h b/pkg/apm/webrtc/rtc_base/gtest_prod_util.h new file mode 100644 index 00000000..9a0c77b5 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/gtest_prod_util.h @@ -0,0 +1,7 @@ +#ifndef RTC_BASE_GTEST_PROD_UTIL_H_ +#define RTC_BASE_GTEST_PROD_UTIL_H_ +#define FRIEND_TEST(test_case_name, test_name) \ + friend class test_case_name##_##test_name##_Test +#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \ + FRIEND_TEST(test_case_name, test_name) +#endif diff --git a/pkg/apm/webrtc/rtc_base/gunit.h b/pkg/apm/webrtc/rtc_base/gunit.h new file mode 100644 index 00000000..5d259b39 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/gunit.h @@ -0,0 +1,51 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_GUNIT_H_ +#define RTC_BASE_GUNIT_H_ + +#include "absl/strings/string_view.h" +#include "rtc_base/fake_clock.h" +#include "rtc_base/logging.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" + +// Wait until "ex" is true, or "timeout" expires. +#define WAIT(ex, timeout) \ + for (int64_t wait_start = ::webrtc::SystemTimeMillis(); \ + !(ex) && ::webrtc::SystemTimeMillis() < wait_start + (timeout);) { \ + ::webrtc::Thread::Current()->ProcessMessages(0); \ + ::webrtc::Thread::Current()->SleepMs(1); \ + } + +// This returns the result of the test in res, so that we don't re-evaluate +// the expression in the XXXX_WAIT macros below, since that causes problems +// when the expression is only true the first time you check it. +#define WAIT_(ex, timeout, res) \ + do { \ + int64_t wait_start = ::webrtc::SystemTimeMillis(); \ + res = (ex) && true; \ + while (!res && ::webrtc::SystemTimeMillis() < wait_start + (timeout)) { \ + ::webrtc::Thread::Current()->ProcessMessages(0); \ + ::webrtc::Thread::Current()->SleepMs(1); \ + res = (ex) && true; \ + } \ + } while (0) + +// Wait until "ex" is true, or "timeout" expires, using fake clock where +// messages are processed every millisecond. +// TODO(pthatcher): Allow tests to control how many milliseconds to advance. +#define SIMULATED_WAIT(ex, timeout, clock) \ + for (int64_t wait_start = ::webrtc::TimeMillis(); \ + !(ex) && ::webrtc::TimeMillis() < wait_start + (timeout);) { \ + (clock).AdvanceTime(webrtc::TimeDelta::Millis(1)); \ + } + +#endif // RTC_BASE_GUNIT_H_ diff --git a/pkg/apm/webrtc/rtc_base/ifaddrs_android.h b/pkg/apm/webrtc/rtc_base/ifaddrs_android.h new file mode 100644 index 00000000..82d7369a --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ifaddrs_android.h @@ -0,0 +1,38 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_IFADDRS_ANDROID_H_ +#define RTC_BASE_IFADDRS_ANDROID_H_ + +#include +#include // no-presubmit-check + +// Implementation of getifaddrs for Android. +// Fills out a list of ifaddr structs (see below) which contain information +// about every network interface available on the host. +// See 'man getifaddrs' on Linux or OS X (nb: it is not a POSIX function). +struct ifaddrs { + struct ifaddrs* ifa_next; + char* ifa_name; + unsigned int ifa_flags; + struct sockaddr* ifa_addr; + struct sockaddr* ifa_netmask; + // Real ifaddrs has broadcast, point to point and data members. + // We don't need them (yet?). +}; + +namespace webrtc { + +int getifaddrs(struct ifaddrs** result); +void freeifaddrs(struct ifaddrs* addrs); + +} // namespace webrtc + +#endif // RTC_BASE_IFADDRS_ANDROID_H_ diff --git a/pkg/apm/webrtc/rtc_base/ifaddrs_converter.h b/pkg/apm/webrtc/rtc_base/ifaddrs_converter.h new file mode 100644 index 00000000..6e8cd63c --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ifaddrs_converter.h @@ -0,0 +1,56 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_IFADDRS_CONVERTER_H_ +#define RTC_BASE_IFADDRS_CONVERTER_H_ + +// IWYU pragma: begin_exports +#if defined(WEBRTC_ANDROID) +#include "rtc_base/ifaddrs_android.h" +#elif defined(WEBRTC_POSIX) +#include +#endif // WEBRTC_ANDROID +// IWYU pragma: end_exports + +#include "rtc_base/ip_address.h" + +namespace webrtc { + +// This class converts native interface addresses to our internal IPAddress +// class. Subclasses should override ConvertNativeToIPAttributes to implement +// the different ways of retrieving IPv6 attributes for various POSIX platforms. +class IfAddrsConverter { + public: + IfAddrsConverter(); + virtual ~IfAddrsConverter(); + virtual bool ConvertIfAddrsToIPAddress(const struct ifaddrs* interface, + InterfaceAddress* ipaddress, + IPAddress* mask); + + protected: + virtual bool ConvertNativeAttributesToIPAttributes( + const struct ifaddrs* interface, + int* ip_attributes); +}; + +IfAddrsConverter* CreateIfAddrsConverter(); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::CreateIfAddrsConverter; +using ::webrtc::IfAddrsConverter; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_IFADDRS_CONVERTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/ignore_wundef.h b/pkg/apm/webrtc/rtc_base/ignore_wundef.h new file mode 100644 index 00000000..15640964 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ignore_wundef.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_IGNORE_WUNDEF_H_ +#define RTC_BASE_IGNORE_WUNDEF_H_ + +// If a header file uses #if on possibly undefined macros (and it's for some +// reason not possible to just fix the header file), include it like this: +// +// RTC_PUSH_IGNORING_WUNDEF() +// #include "misbehaving_header.h" +// RTC_POP_IGNORING_WUNDEF() +// +// This will cause the compiler to not emit -Wundef warnings for that file. + +#ifdef __clang__ +#define RTC_PUSH_IGNORING_WUNDEF() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wundef\"") +#define RTC_POP_IGNORING_WUNDEF() _Pragma("clang diagnostic pop") +#else +#define RTC_PUSH_IGNORING_WUNDEF() +#define RTC_POP_IGNORING_WUNDEF() +#endif // __clang__ + +#endif // RTC_BASE_IGNORE_WUNDEF_H_ diff --git a/pkg/apm/webrtc/rtc_base/internal/default_socket_server.h b/pkg/apm/webrtc/rtc_base/internal/default_socket_server.h new file mode 100644 index 00000000..173db0b2 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/internal/default_socket_server.h @@ -0,0 +1,32 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_INTERNAL_DEFAULT_SOCKET_SERVER_H_ +#define RTC_BASE_INTERNAL_DEFAULT_SOCKET_SERVER_H_ + +#include + +#include "rtc_base/socket_server.h" + +namespace webrtc { + +std::unique_ptr CreateDefaultSocketServer(); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::CreateDefaultSocketServer; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_INTERNAL_DEFAULT_SOCKET_SERVER_H_ diff --git a/pkg/apm/webrtc/rtc_base/ip_address.h b/pkg/apm/webrtc/rtc_base/ip_address.h new file mode 100644 index 00000000..83074cc5 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ip_address.h @@ -0,0 +1,235 @@ +/* + * 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 RTC_BASE_IP_ADDRESS_H_ +#define RTC_BASE_IP_ADDRESS_H_ + +#include +#if defined(WEBRTC_POSIX) +#include +#include +#include // IWYU pragma: export + +#include "absl/strings/string_view.h" +#endif +#if defined(WEBRTC_WIN) +#include +#endif +#include + +#include + +#include "rtc_base/byte_order.h" +#if defined(WEBRTC_WIN) +#include "rtc_base/win32.h" +#endif +#include "absl/strings/string_view.h" +#include "rtc_base/net_helpers.h" +#include "rtc_base/system/rtc_export.h" +namespace webrtc { + +enum IPv6AddressFlag { + IPV6_ADDRESS_FLAG_NONE = 0x00, + + // Temporary address is dynamic by nature and will not carry MAC + // address. + IPV6_ADDRESS_FLAG_TEMPORARY = 1 << 0, + + // Temporary address could become deprecated once the preferred + // lifetime is reached. It is still valid but just shouldn't be used + // to create new connection. + IPV6_ADDRESS_FLAG_DEPRECATED = 1 << 1, +}; + +// Version-agnostic IP address class, wraps a union of in_addr and in6_addr. +class RTC_EXPORT IPAddress { + public: + IPAddress() : family_(AF_UNSPEC) { ::memset(&u_, 0, sizeof(u_)); } + + explicit IPAddress(const in_addr& ip4) : family_(AF_INET) { + memset(&u_, 0, sizeof(u_)); + u_.ip4 = ip4; + } + + explicit IPAddress(const in6_addr& ip6) : family_(AF_INET6) { u_.ip6 = ip6; } + + explicit IPAddress(uint32_t ip_in_host_byte_order) : family_(AF_INET) { + memset(&u_, 0, sizeof(u_)); + u_.ip4.s_addr = webrtc::HostToNetwork32(ip_in_host_byte_order); + } + + IPAddress(const IPAddress& other) : family_(other.family_) { + ::memcpy(&u_, &other.u_, sizeof(u_)); + } + + virtual ~IPAddress() {} + + const IPAddress& operator=(const IPAddress& other) { + family_ = other.family_; + ::memcpy(&u_, &other.u_, sizeof(u_)); + return *this; + } + + bool operator==(const IPAddress& other) const; + bool operator!=(const IPAddress& other) const; + bool operator<(const IPAddress& other) const; + bool operator>(const IPAddress& other) const; + + int family() const { return family_; } + in_addr ipv4_address() const; + in6_addr ipv6_address() const; + + // Returns the number of bytes needed to store the raw address. + size_t Size() const; + + // Wraps inet_ntop. + std::string ToString() const; + + // Same as ToString but anonymizes it by hiding the last part. + std::string ToSensitiveString() const; + + // Returns an unmapped address from a possibly-mapped address. + // Returns the same address if this isn't a mapped address. + IPAddress Normalized() const; + + // Returns this address as an IPv6 address. + // Maps v4 addresses (as ::ffff:a.b.c.d), returns v6 addresses unchanged. + IPAddress AsIPv6Address() const; + + // For socketaddress' benefit. Returns the IP in host byte order. + uint32_t v4AddressAsHostOrderInteger() const; + + // Get the network layer overhead per packet based on the IP address family. + int overhead() const; + + // Whether this is an unspecified IP address. + bool IsNil() const; + + private: + int family_; + union { + in_addr ip4; + in6_addr ip6; + } u_; +}; + +// IP class which could represent IPv6 address flags which is only +// meaningful in IPv6 case. +class RTC_EXPORT InterfaceAddress : public IPAddress { + public: + InterfaceAddress() : ipv6_flags_(IPV6_ADDRESS_FLAG_NONE) {} + + explicit InterfaceAddress(IPAddress ip) + : IPAddress(ip), ipv6_flags_(IPV6_ADDRESS_FLAG_NONE) {} + + InterfaceAddress(IPAddress addr, int ipv6_flags) + : IPAddress(addr), ipv6_flags_(ipv6_flags) {} + + InterfaceAddress(const in6_addr& ip6, int ipv6_flags) + : IPAddress(ip6), ipv6_flags_(ipv6_flags) {} + + InterfaceAddress(const InterfaceAddress& other) = default; + const InterfaceAddress& operator=(const InterfaceAddress& other); + + bool operator==(const InterfaceAddress& other) const; + bool operator!=(const InterfaceAddress& other) const; + + int ipv6_flags() const { return ipv6_flags_; } + + std::string ToString() const; + + private: + int ipv6_flags_; +}; + +bool IPFromAddrInfo(struct addrinfo* info, IPAddress* out); +RTC_EXPORT bool IPFromString(absl::string_view str, IPAddress* out); +RTC_EXPORT bool IPFromString(absl::string_view str, + int flags, + InterfaceAddress* out); +bool IPIsAny(const IPAddress& ip); +RTC_EXPORT bool IPIsLoopback(const IPAddress& ip); +RTC_EXPORT bool IPIsLinkLocal(const IPAddress& ip); +// Identify a private network address like "192.168.111.222" +// (see https://en.wikipedia.org/wiki/Private_network ) +bool IPIsPrivateNetwork(const IPAddress& ip); +// Identify a shared network address like "100.72.16.122" +// (see RFC6598) +bool IPIsSharedNetwork(const IPAddress& ip); +// Identify if an IP is "private", that is a loopback +// or an address belonging to a link-local, a private network or a shared +// network. +RTC_EXPORT bool IPIsPrivate(const IPAddress& ip); +RTC_EXPORT bool IPIsUnspec(const IPAddress& ip); +size_t HashIP(const IPAddress& ip); + +// These are only really applicable for IPv6 addresses. +bool IPIs6Bone(const IPAddress& ip); +bool IPIs6To4(const IPAddress& ip); +RTC_EXPORT bool IPIsMacBased(const IPAddress& ip); +bool IPIsSiteLocal(const IPAddress& ip); +bool IPIsTeredo(const IPAddress& ip); +bool IPIsULA(const IPAddress& ip); +bool IPIsV4Compatibility(const IPAddress& ip); +bool IPIsV4Mapped(const IPAddress& ip); + +// Returns the precedence value for this IP as given in RFC3484. +int IPAddressPrecedence(const IPAddress& ip); + +// Returns 'ip' truncated to be 'length' bits long. +RTC_EXPORT IPAddress TruncateIP(const IPAddress& ip, int length); + +IPAddress GetLoopbackIP(int family); +IPAddress GetAnyIP(int family); + +// Returns the number of contiguously set bits, counting from the MSB in network +// byte order, in this IPAddress. Bits after the first 0 encountered are not +// counted. +int CountIPMaskBits(const IPAddress& mask); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::CountIPMaskBits; +using ::webrtc::GetAnyIP; +using ::webrtc::GetLoopbackIP; +using ::webrtc::HashIP; +using ::webrtc::InterfaceAddress; +using ::webrtc::IPAddress; +using ::webrtc::IPAddressPrecedence; +using ::webrtc::IPFromAddrInfo; +using ::webrtc::IPFromString; +using ::webrtc::IPIs6Bone; +using ::webrtc::IPIs6To4; +using ::webrtc::IPIsAny; +using ::webrtc::IPIsLinkLocal; +using ::webrtc::IPIsLoopback; +using ::webrtc::IPIsMacBased; +using ::webrtc::IPIsPrivate; +using ::webrtc::IPIsPrivateNetwork; +using ::webrtc::IPIsSharedNetwork; +using ::webrtc::IPIsSiteLocal; +using ::webrtc::IPIsTeredo; +using ::webrtc::IPIsULA; +using ::webrtc::IPIsUnspec; +using ::webrtc::IPIsV4Compatibility; +using ::webrtc::IPIsV4Mapped; +using ::webrtc::IPV6_ADDRESS_FLAG_DEPRECATED; +using ::webrtc::IPV6_ADDRESS_FLAG_NONE; +using ::webrtc::IPV6_ADDRESS_FLAG_TEMPORARY; +using ::webrtc::IPv6AddressFlag; +using ::webrtc::TruncateIP; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_IP_ADDRESS_H_ diff --git a/pkg/apm/webrtc/rtc_base/log_sinks.h b/pkg/apm/webrtc/rtc_base/log_sinks.h new file mode 100644 index 00000000..a1b4b872 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/log_sinks.h @@ -0,0 +1,89 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_LOG_SINKS_H_ +#define RTC_BASE_LOG_SINKS_H_ + +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/file_rotating_stream.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +// Log sink that uses a FileRotatingStream to write to disk. +// Init() must be called before adding this sink. +class FileRotatingLogSink : public LogSink { + public: + // `num_log_files` must be greater than 1 and `max_log_size` must be greater + // than 0. + FileRotatingLogSink(absl::string_view log_dir_path, + absl::string_view log_prefix, + size_t max_log_size, + size_t num_log_files); + ~FileRotatingLogSink() override; + + FileRotatingLogSink(const FileRotatingLogSink&) = delete; + FileRotatingLogSink& operator=(const FileRotatingLogSink&) = delete; + + // Writes the message to the current file. It will spill over to the next + // file if needed. + void OnLogMessage(const std::string& message) override; + void OnLogMessage(absl::string_view message) override; + void OnLogMessage(const std::string& message, + LoggingSeverity sev, + const char* tag) override; + void OnLogMessage(absl::string_view message, + LoggingSeverity sev, + const char* tag) override; + + // Deletes any existing files in the directory and creates a new log file. + virtual bool Init(); + + // Disables buffering on the underlying stream. + bool DisableBuffering(); + + protected: + explicit FileRotatingLogSink(FileRotatingStream* stream); + + private: + std::unique_ptr stream_; +}; + +// Log sink that uses a CallSessionFileRotatingStream to write to disk. +// Init() must be called before adding this sink. +class CallSessionFileRotatingLogSink : public FileRotatingLogSink { + public: + CallSessionFileRotatingLogSink(absl::string_view log_dir_path, + size_t max_total_log_size); + ~CallSessionFileRotatingLogSink() override; + + CallSessionFileRotatingLogSink(const CallSessionFileRotatingLogSink&) = + delete; + CallSessionFileRotatingLogSink& operator=( + const CallSessionFileRotatingLogSink&) = delete; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::CallSessionFileRotatingLogSink; +using ::webrtc::FileRotatingLogSink; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_LOG_SINKS_H_ diff --git a/pkg/apm/webrtc/rtc_base/logging.cc b/pkg/apm/webrtc/rtc_base/logging.cc new file mode 100644 index 00000000..1a07e964 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/logging.cc @@ -0,0 +1,587 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/logging.h" + +#include + +#include +#include +#include + +#if RTC_LOG_ENABLED() + +#if defined(WEBRTC_WIN) +#include +#if _MSC_VER < 1900 +#define snprintf _snprintf +#endif +#undef ERROR // wingdi.h +#endif + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include +#elif defined(WEBRTC_ANDROID) +#include + +// Android has a 1024 limit on log inputs. We use 60 chars as an +// approx for the header/tag portion. +// See android/system/core/liblog/logd_write.c +static const int kMaxLogLineSize = 1024 - 60; +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) || WEBRTC_ANDROID + +#include +#include +#include + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/strings/string_view.h" +#include "api/units/timestamp.h" +#include "rtc_base/checks.h" +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/string_encode.h" +#include "rtc_base/string_utils.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { +namespace { + +// By default, release builds don't log, debug builds at info level +#if !defined(NDEBUG) +constexpr LoggingSeverity kDefaultLoggingSeverity = LS_INFO; +#else +constexpr LoggingSeverity kDefaultLoggingSeverity = LS_NONE; +#endif + +// Note: `g_min_sev` and `g_dbg_sev` can be changed while running. +LoggingSeverity g_min_sev = kDefaultLoggingSeverity; +LoggingSeverity g_dbg_sev = kDefaultLoggingSeverity; + +// Return the filename portion of the string (that following the last slash). +const char* FilenameFromPath(const char* file) { + const char* end1 = ::strrchr(file, '/'); + const char* end2 = ::strrchr(file, '\\'); + if (!end1 && !end2) + return file; + else + return (end1 > end2) ? end1 + 1 : end2 + 1; +} + +// Global lock for log subsystem, only needed to serialize access to streams_. +Mutex& GetLoggingLock() { + static Mutex& mutex = *new Mutex(); + return mutex; +} + +} // namespace + +std::string LogLineRef::DefaultLogLine() const { + StringBuilder log_output; + if (timestamp_ != Timestamp::MinusInfinity()) { + // TODO(kwiberg): Switch to absl::StrFormat, if binary size is ok. + char timestamp[50]; // Maximum string length of an int64_t is 20. + int len = + snprintf(timestamp, sizeof(timestamp), "[%03" PRId64 ":%03" PRId64 "]", + timestamp_.ms() / 1000, timestamp_.ms() % 1000); + RTC_DCHECK_LT(len, sizeof(timestamp)); + log_output << timestamp; + } + if (thread_id_.has_value()) { + log_output << "[" << *thread_id_ << "] "; + } + if (!filename_.empty()) { +#if defined(WEBRTC_ANDROID) + log_output << "(line " << line_ << "): "; +#else + log_output << "(" << filename_ << ":" << line_ << "): "; +#endif + } + log_output << message_; + return log_output.Release(); +} + +///////////////////////////////////////////////////////////////////////////// +// LogMessage +///////////////////////////////////////////////////////////////////////////// + +bool LogMessage::log_to_stderr_ = true; + +// The list of logging streams currently configured. +// Note: we explicitly do not clean this up, because of the uncertain ordering +// of destructors at program exit. Let the person who sets the stream trigger +// cleanup by setting to null, or let it leak (safe at program exit). +ABSL_CONST_INIT LogSink* LogMessage::streams_ RTC_GUARDED_BY(GetLoggingLock()) = + nullptr; +ABSL_CONST_INIT std::atomic LogMessage::streams_empty_ = {true}; + +// Boolean options default to false. +ABSL_CONST_INIT bool LogMessage::log_thread_ = false; +ABSL_CONST_INIT bool LogMessage::log_timestamp_ = false; + +LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev) + : LogMessage(file, line, sev, ERRCTX_NONE, 0) {} + +LogMessage::LogMessage(const char* file, + int line, + LoggingSeverity sev, + LogErrorContext err_ctx, + int err) { + log_line_.set_severity(sev); + if (log_timestamp_) { + int64_t log_start_time = LogStartTime(); + // Use SystemTimeMillis so that even if tests use fake clocks, the timestamp + // in log messages represents the real system time. + int64_t time = TimeDiff(SystemTimeMillis(), log_start_time); + // Also ensure WallClockStartTime is initialized, so that it matches + // LogStartTime. + WallClockStartTime(); + log_line_.set_timestamp(Timestamp::Millis(time)); + } + + if (log_thread_) { + log_line_.set_thread_id(CurrentThreadId()); + } + + if (file != nullptr) { + log_line_.set_filename(FilenameFromPath(file)); + log_line_.set_line(line); +#if defined(WEBRTC_ANDROID) + log_line_.set_tag(log_line_.filename()); +#endif + } + + if (err_ctx != ERRCTX_NONE) { + char tmp_buf[1024]; + SimpleStringBuilder tmp(tmp_buf); + tmp.AppendFormat("[0x%08X]", err); + switch (err_ctx) { + case ERRCTX_ERRNO: + tmp << " " << strerror(err); + break; +#ifdef WEBRTC_WIN + case ERRCTX_HRESULT: { + char msgbuf[256]; + DWORD flags = + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + if (DWORD len = FormatMessageA( + flags, nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), nullptr)) { + while ((len > 0) && + isspace(static_cast(msgbuf[len - 1]))) { + msgbuf[--len] = 0; + } + tmp << " " << msgbuf; + } + break; + } +#endif // WEBRTC_WIN + default: + break; + } + extra_ = tmp.str(); + } +} + +#if defined(WEBRTC_ANDROID) +LogMessage::LogMessage(const char* file, + int line, + LoggingSeverity sev, + const char* tag) + : LogMessage(file, line, sev, ERRCTX_NONE, /*err=*/0) { + log_line_.set_tag(tag); + print_stream_ << tag << ": "; +} +#endif + +LogMessage::~LogMessage() { + FinishPrintStream(); + + log_line_.set_message(print_stream_.Release()); + + if (log_line_.severity() >= g_dbg_sev) { + OutputToDebug(log_line_); + } + + MutexLock lock(&GetLoggingLock()); + for (LogSink* entry = streams_; entry != nullptr; entry = entry->next_) { + if (log_line_.severity() >= entry->min_severity_) { + entry->OnLogMessage(log_line_); + } + } +} + +void LogMessage::AddTag([[maybe_unused]] const char* tag) { +#ifdef WEBRTC_ANDROID + log_line_.set_tag(tag); +#endif +} + +StringBuilder& LogMessage::stream() { + return print_stream_; +} + +int LogMessage::GetMinLogSeverity() { + return g_min_sev; +} + +LoggingSeverity LogMessage::GetLogToDebug() { + return g_dbg_sev; +} +int64_t LogMessage::LogStartTime() { + static const int64_t g_start = SystemTimeMillis(); + return g_start; +} + +uint32_t LogMessage::WallClockStartTime() { + static const uint32_t g_start_wallclock = time(nullptr); + return g_start_wallclock; +} + +void LogMessage::LogThreads(bool on) { + log_thread_ = on; +} + +void LogMessage::LogTimestamps(bool on) { + log_timestamp_ = on; +} + +void LogMessage::LogToDebug(LoggingSeverity min_sev) { + g_dbg_sev = min_sev; + MutexLock lock(&GetLoggingLock()); + UpdateMinLogSeverity(); +} + +void LogMessage::SetLogToStderr(bool log_to_stderr) { + log_to_stderr_ = log_to_stderr; +} + +int LogMessage::GetLogToStream(LogSink* stream) { + MutexLock lock(&GetLoggingLock()); + LoggingSeverity sev = LS_NONE; + for (LogSink* entry = streams_; entry != nullptr; entry = entry->next_) { + if (stream == nullptr || stream == entry) { + sev = std::min(sev, entry->min_severity_); + } + } + return sev; +} + +void LogMessage::AddLogToStream(LogSink* stream, LoggingSeverity min_sev) { + MutexLock lock(&GetLoggingLock()); + stream->min_severity_ = min_sev; + stream->next_ = streams_; + streams_ = stream; + streams_empty_.store(false, std::memory_order_relaxed); + UpdateMinLogSeverity(); +} + +void LogMessage::RemoveLogToStream(LogSink* stream) { + MutexLock lock(&GetLoggingLock()); + for (LogSink** entry = &streams_; *entry != nullptr; + entry = &(*entry)->next_) { + if (*entry == stream) { + *entry = (*entry)->next_; + break; + } + } + streams_empty_.store(streams_ == nullptr, std::memory_order_relaxed); + UpdateMinLogSeverity(); +} + +void LogMessage::ConfigureLogging(absl::string_view params) { + LoggingSeverity current_level = LS_VERBOSE; + LoggingSeverity debug_level = GetLogToDebug(); + + std::vector tokens; + tokenize(params, ' ', &tokens); + + for (const std::string& token : tokens) { + if (token.empty()) + continue; + + // Logging features + if (token == "tstamp") { + LogTimestamps(); + } else if (token == "thread") { + LogThreads(); + + // Logging levels + } else if (token == "verbose") { + current_level = LS_VERBOSE; + } else if (token == "info") { + current_level = LS_INFO; + } else if (token == "warning") { + current_level = LS_WARNING; + } else if (token == "error") { + current_level = LS_ERROR; + } else if (token == "none") { + current_level = LS_NONE; + + // Logging targets + } else if (token == "debug") { + debug_level = current_level; + } + } + +#if defined(WEBRTC_WIN) && !defined(WINUWP) + if ((LS_NONE != debug_level) && !::IsDebuggerPresent()) { + // First, attempt to attach to our parent's console... so if you invoke + // from the command line, we'll see the output there. Otherwise, create + // our own console window. + // Note: These methods fail if a console already exists, which is fine. + if (!AttachConsole(ATTACH_PARENT_PROCESS)) + ::AllocConsole(); + } +#endif // defined(WEBRTC_WIN) && !defined(WINUWP) + + LogToDebug(debug_level); +} + +void LogMessage::UpdateMinLogSeverity() + RTC_EXCLUSIVE_LOCKS_REQUIRED(GetLoggingLock()) { + LoggingSeverity min_sev = g_dbg_sev; + for (LogSink* entry = streams_; entry != nullptr; entry = entry->next_) { + min_sev = std::min(min_sev, entry->min_severity_); + } + g_min_sev = min_sev; +} + +void LogMessage::OutputToDebug(const LogLineRef& log_line) { + std::string msg_str = log_line.DefaultLogLine(); + bool log_to_stderr = log_to_stderr_; +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG) + // On the Mac, all stderr output goes to the Console log and causes clutter. + // So in opt builds, don't log to stderr unless the user specifically sets + // a preference to do so. + CFStringRef domain = CFBundleGetIdentifier(CFBundleGetMainBundle()); + if (domain != nullptr) { + Boolean exists_and_is_valid; + Boolean should_log = CFPreferencesGetAppBooleanValue( + CFSTR("logToStdErr"), domain, &exists_and_is_valid); + // If the key doesn't exist or is invalid or is false, we will not log to + // stderr. + log_to_stderr = exists_and_is_valid && should_log; + } +#endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG) + +#if defined(WEBRTC_WIN) + // Always log to the debugger. + // Perhaps stderr should be controlled by a preference, as on Mac? + OutputDebugStringA(msg_str.c_str()); + if (log_to_stderr) { + // This handles dynamically allocated consoles, too. + if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) { + log_to_stderr = false; + DWORD written = 0; + ::WriteFile(error_handle, msg_str.c_str(), + static_cast(msg_str.size()), &written, 0); + } + } +#endif // WEBRTC_WIN + +#if defined(WEBRTC_ANDROID) + // Android's logging facility uses severity to log messages but we + // need to map libjingle's severity levels to Android ones first. + // Also write to stderr which maybe available to executable started + // from the shell. + int prio; + switch (log_line.severity()) { + case LS_VERBOSE: + prio = ANDROID_LOG_VERBOSE; + break; + case LS_INFO: + prio = ANDROID_LOG_INFO; + break; + case LS_WARNING: + prio = ANDROID_LOG_WARN; + break; + case LS_ERROR: + prio = ANDROID_LOG_ERROR; + break; + default: + prio = ANDROID_LOG_UNKNOWN; + } + + int size = msg_str.size(); + int current_line = 0; + int idx = 0; + const int max_lines = size / kMaxLogLineSize + 1; + if (max_lines == 1) { + __android_log_print(prio, log_line.tag().data(), "%.*s", size, + msg_str.c_str()); + } else { + while (size > 0) { + const int len = std::min(size, kMaxLogLineSize); + // Use the size of the string in the format (msg may have \0 in the + // middle). + __android_log_print(prio, log_line.tag().data(), "[%d/%d] %.*s", + current_line + 1, max_lines, len, + msg_str.c_str() + idx); + idx += len; + size -= len; + ++current_line; + } + } +#endif // WEBRTC_ANDROID + if (log_to_stderr) { + fprintf(stderr, "%s", msg_str.c_str()); + fflush(stderr); + } +} + +// static +bool LogMessage::IsNoop(LoggingSeverity severity) { + if (severity >= g_dbg_sev || severity >= g_min_sev) + return false; + return streams_empty_.load(std::memory_order_relaxed); +} + +void LogMessage::FinishPrintStream() { + if (!extra_.empty()) + print_stream_ << " : " << extra_; + print_stream_ << "\n"; +} + +namespace webrtc_logging_impl { + +void Log(const LogArgType* fmt, ...) { + va_list args; + va_start(args, fmt); + + LogMetadataErr meta; + const char* tag = nullptr; + switch (*fmt) { + case LogArgType::kLogMetadata: { + meta = {va_arg(args, LogMetadata), ERRCTX_NONE, 0}; + break; + } + case LogArgType::kLogMetadataErr: { + meta = va_arg(args, LogMetadataErr); + break; + } +#ifdef WEBRTC_ANDROID + case LogArgType::kLogMetadataTag: { + const LogMetadataTag tag_meta = va_arg(args, LogMetadataTag); + meta = {{nullptr, 0, tag_meta.severity}, ERRCTX_NONE, 0}; + tag = tag_meta.tag; + break; + } +#endif + default: { + RTC_DCHECK_NOTREACHED(); + va_end(args); + return; + } + } + + LogMessage log_message(meta.meta.File(), meta.meta.Line(), + meta.meta.Severity(), meta.err_ctx, meta.err); + if (tag) { + log_message.AddTag(tag); + } + + for (++fmt; *fmt != LogArgType::kEnd; ++fmt) { + switch (*fmt) { + case LogArgType::kInt: + log_message.stream() << va_arg(args, int); + break; + case LogArgType::kLong: + log_message.stream() << va_arg(args, long); + break; + case LogArgType::kLongLong: + log_message.stream() << va_arg(args, long long); + break; + case LogArgType::kUInt: + log_message.stream() << va_arg(args, unsigned); + break; + case LogArgType::kULong: + log_message.stream() << va_arg(args, unsigned long); + break; + case LogArgType::kULongLong: + log_message.stream() << va_arg(args, unsigned long long); + break; + case LogArgType::kDouble: + log_message.stream() << va_arg(args, double); + break; + case LogArgType::kCharP: { + const char* s = va_arg(args, const char*); + log_message.stream() << (s ? s : "(null)"); + break; + } + case LogArgType::kStdString: + log_message.stream() << *va_arg(args, const std::string*); + break; + case LogArgType::kStringView: + log_message.stream() << *va_arg(args, const absl::string_view*); + break; + case LogArgType::kVoidP: + log_message.stream() + << ToHex(reinterpret_cast(va_arg(args, const void*))); + break; + default: + RTC_DCHECK_NOTREACHED(); + va_end(args); + return; + } + } + + va_end(args); +} + +} // namespace webrtc_logging_impl +} // namespace webrtc +#endif + +namespace webrtc { +// Default implementation, override is recomended. +void LogSink::OnLogMessage(const LogLineRef& log_line) { +#if defined(WEBRTC_ANDROID) + OnLogMessage(log_line.DefaultLogLine(), log_line.severity(), + log_line.tag().data()); +#else + OnLogMessage(log_line.DefaultLogLine(), log_line.severity()); +#endif +} + +// Inefficient default implementation, override is recommended. +void LogSink::OnLogMessage(const std::string& msg, + LoggingSeverity severity, + const char* tag) { + OnLogMessage(tag + (": " + msg), severity); +} + +void LogSink::OnLogMessage(const std::string& msg, + LoggingSeverity /* severity */) { + OnLogMessage(msg); +} + +// Inefficient default implementation, override is recommended. +void LogSink::OnLogMessage(absl::string_view msg, + LoggingSeverity severity, + const char* tag) { + OnLogMessage(tag + (": " + std::string(msg)), severity); +} + +void LogSink::OnLogMessage(absl::string_view msg, + LoggingSeverity /* severity */) { + OnLogMessage(msg); +} + +void LogSink::OnLogMessage(absl::string_view msg) { + OnLogMessage(std::string(msg)); +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/logging.h b/pkg/apm/webrtc/rtc_base/logging.h new file mode 100644 index 00000000..b011acbe --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/logging.h @@ -0,0 +1,749 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// RTC_LOG(...) an ostream target that can be used to send formatted +// output to a variety of logging targets, such as debugger console, stderr, +// or any LogSink. +// The severity level passed as the first argument to the logging +// functions is used as a filter, to limit the verbosity of the logging. +// Static members of LogMessage documented below are used to control the +// verbosity and target of the output. +// There are several variations on the RTC_LOG macro which facilitate logging +// of common error conditions, detailed below. + +// RTC_LOG(sev) logs the given stream at severity "sev", which must be a +// compile-time constant of the LoggingSeverity type, without the namespace +// prefix. +// RTC_LOG_IF(sev, condition) logs the given stream at severity "sev" if +// "condition" is true. +// RTC_LOG_V(sev) Like RTC_LOG(), but sev is a run-time variable of the +// LoggingSeverity type (basically, it just doesn't prepend the namespace). +// RTC_LOG_F(sev) Like RTC_LOG(), but includes the name of the current function. +// RTC_LOG_IF_F(sev, condition), Like RTC_LOG_IF(), but includes the name of +// the current function. +// RTC_LOG_T(sev) Like RTC_LOG(), but includes the this pointer. +// RTC_LOG_T_F(sev) Like RTC_LOG_F(), but includes the this pointer. +// RTC_LOG_GLE(sev [, mod]) attempt to add a string description of the +// HRESULT returned by GetLastError. +// RTC_LOG_ERRNO(sev) attempts to add a string description of an errno-derived +// error. errno and associated facilities exist on both Windows and POSIX, +// but on Windows they only apply to the C/C++ runtime. +// RTC_LOG_ERR(sev) is an alias for the platform's normal error system, i.e. +// _GLE on Windows and _ERRNO on POSIX. +// (The above three also all have _EX versions that let you specify the error +// code, rather than using the last one.) +// RTC_LOG_E(sev, ctx, err, ...) logs a detailed error interpreted using the +// specified context. +// RTC_LOG_CHECK_LEVEL(sev) (and RTC_LOG_CHECK_LEVEL_V(sev)) can be used as a +// test before performing expensive or sensitive operations whose sole +// purpose is to output logging data at the desired level. + +#ifndef RTC_BASE_LOGGING_H_ +#define RTC_BASE_LOGGING_H_ + +#include + +#include +#include +#include // no-presubmit-check TODO(webrtc:8982) +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/strings/has_absl_stringify.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "api/units/timestamp.h" +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/system/inline.h" +#include "rtc_base/type_traits.h" + +#if !defined(NDEBUG) || defined(DLOG_ALWAYS_ON) +#define RTC_DLOG_IS_ON 1 +#else +#define RTC_DLOG_IS_ON 0 +#endif + +#if defined(RTC_DISABLE_LOGGING) +#define RTC_LOG_ENABLED() 0 +#else +#define RTC_LOG_ENABLED() 1 +#endif + +namespace webrtc { + +////////////////////////////////////////////////////////////////////// +// The meanings of the levels are: +// LS_VERBOSE: This level is for data which we do not want to appear in the +// normal debug log, but should appear in diagnostic logs. +// LS_INFO: Chatty level used in debugging for all sorts of things, the default +// in debug builds. +// LS_WARNING: Something that may warrant investigation. +// LS_ERROR: Something that should not have occurred. +// LS_NONE: Don't log. +enum LoggingSeverity { + LS_VERBOSE, + LS_INFO, + LS_WARNING, + LS_ERROR, + LS_NONE, +}; + +// LogErrorContext assists in interpreting the meaning of an error value. +enum LogErrorContext { + ERRCTX_NONE, + ERRCTX_ERRNO, // System-local errno + ERRCTX_HRESULT, // Windows HRESULT + + // Abbreviations for LOG_E macro + ERRCTX_EN = ERRCTX_ERRNO, // LOG_E(sev, EN, x) + ERRCTX_HR = ERRCTX_HRESULT, // LOG_E(sev, HR, x) +}; + +class LogMessage; + +// LogLineRef encapsulates all the information required to generate a log line. +// It is used both internally to LogMessage but also as a parameter to +// LogSink::OnLogMessage, allowing custom LogSinks to format the log in +// the most flexible way. +class LogLineRef { + public: + absl::string_view message() const { return message_; } + absl::string_view filename() const { return filename_; } + int line() const { return line_; } + std::optional thread_id() const { return thread_id_; } + Timestamp timestamp() const { return timestamp_; } + absl::string_view tag() const { return tag_; } + LoggingSeverity severity() const { return severity_; } + +#if RTC_LOG_ENABLED() + std::string DefaultLogLine() const; +#else + std::string DefaultLogLine() const { return ""; } +#endif + + private: + friend class LogMessage; + void set_message(std::string message) { message_ = std::move(message); } + void set_filename(absl::string_view filename) { filename_ = filename; } + void set_line(int line) { line_ = line; } + void set_thread_id(std::optional thread_id) { + thread_id_ = thread_id; + } + void set_timestamp(Timestamp timestamp) { timestamp_ = timestamp; } + void set_tag(absl::string_view tag) { tag_ = tag; } + void set_severity(LoggingSeverity severity) { severity_ = severity; } + + std::string message_; + absl::string_view filename_; + int line_ = 0; + std::optional thread_id_; + Timestamp timestamp_ = Timestamp::MinusInfinity(); + // The default Android debug output tag. + absl::string_view tag_ = "libjingle"; + // The severity level of this message + LoggingSeverity severity_; +}; + +// Virtual sink interface that can receive log messages. +class LogSink { + public: + LogSink() {} + virtual ~LogSink() {} + virtual void OnLogMessage(const std::string& msg, + LoggingSeverity severity, + const char* tag); + virtual void OnLogMessage(const std::string& message, + LoggingSeverity severity); + virtual void OnLogMessage(const std::string& message) = 0; + + virtual void OnLogMessage(absl::string_view msg, + LoggingSeverity severity, + const char* tag); + virtual void OnLogMessage(absl::string_view message, + LoggingSeverity severity); + virtual void OnLogMessage(absl::string_view message); + virtual void OnLogMessage(const LogLineRef& line); + + private: + friend class LogMessage; +#if RTC_LOG_ENABLED() + // Members for LogMessage class to keep linked list of the registered sinks. + LogSink* next_ = nullptr; + LoggingSeverity min_severity_; +#endif +}; + +namespace webrtc_logging_impl { + +class LogMetadata { + public: + LogMetadata(const char* file, int line, LoggingSeverity severity) + : file_(file), + line_and_sev_(static_cast(line) << 3 | severity) {} + LogMetadata() = default; + + const char* File() const { return file_; } + int Line() const { return line_and_sev_ >> 3; } + LoggingSeverity Severity() const { + return static_cast(line_and_sev_ & 0x7); + } + + private: + const char* file_; + + // Line number and severity, the former in the most significant 29 bits, the + // latter in the least significant 3 bits. (This is an optimization; since + // both numbers are usually compile-time constants, this way we can load them + // both with a single instruction.) + uint32_t line_and_sev_; +}; +static_assert(std::is_trivial::value, ""); + +struct LogMetadataErr { + LogMetadata meta; + LogErrorContext err_ctx; + int err; +}; + +#ifdef WEBRTC_ANDROID +struct LogMetadataTag { + LoggingSeverity severity; + const char* tag; +}; +#endif + +enum class LogArgType : int8_t { + kEnd = 0, + kInt, + kLong, + kLongLong, + kUInt, + kULong, + kULongLong, + kDouble, + kLongDouble, + kCharP, + kStdString, + kStringView, + kVoidP, + kLogMetadata, + kLogMetadataErr, +#ifdef WEBRTC_ANDROID + kLogMetadataTag, +#endif +}; + +// Wrapper for log arguments. Only ever make values of this type with the +// MakeVal() functions. +template +struct Val { + static constexpr LogArgType Type() { return N; } + T GetVal() const { return val; } + T val; +}; + +// Case for when we need to construct a temp string and then print that. +// (We can't use Val +// because we need somewhere to store the temp string.) +struct ToStringVal { + static constexpr LogArgType Type() { return LogArgType::kStdString; } + const std::string* GetVal() const { return &val; } + std::string val; +}; + +inline Val MakeVal(int x) { + return {x}; +} +inline Val MakeVal(long x) { + return {x}; +} +inline Val MakeVal(long long x) { + return {x}; +} +inline Val MakeVal(unsigned int x) { + return {x}; +} +inline Val MakeVal(unsigned long x) { + return {x}; +} +inline Val MakeVal( + unsigned long long x) { + return {x}; +} + +inline Val MakeVal(double x) { + return {x}; +} + +inline Val MakeVal(long double x) { + return {x}; +} + +inline Val MakeVal(const char* x) { + return {x}; +} +inline Val MakeVal( + const std::string& x) { + return {&x}; +} +inline Val MakeVal( + const absl::string_view& x) { + return {&x}; +} + +inline Val MakeVal(const void* x) { + return {x}; +} + +inline Val MakeVal( + const LogMetadata& x) { + return {x}; +} +inline Val MakeVal( + const LogMetadataErr& x) { + return {x}; +} + +// The enum class types are not implicitly convertible to arithmetic types. +template ::value && + !absl::HasAbslStringify::value && + !std::is_arithmetic::value>* = nullptr> +inline decltype(MakeVal(std::declval>())) MakeVal( + T x) { + return {static_cast>(x)}; +} + +#ifdef WEBRTC_ANDROID +inline Val MakeVal( + const LogMetadataTag& x) { + return {x}; +} +#endif + +template ::value>* = nullptr> +ToStringVal MakeVal(const T& x) { + return {absl::StrCat(x)}; +} + +// Handle arbitrary types other than the above by falling back to stringstream. +// TODO(bugs.webrtc.org/9278): Get rid of this overload when callers don't need +// it anymore. No in-tree caller does, but some external callers still do. +template , + std::enable_if_t::value && // + !std::is_same::value && // + !std::is_same::value && // + !absl::HasAbslStringify::value && +#ifdef WEBRTC_ANDROID + !std::is_same::value && // +#endif + !std::is_same::value>* = nullptr> +ToStringVal MakeVal(const T& x) { + std::ostringstream os; // no-presubmit-check TODO(webrtc:8982) + os << x; + return {os.str()}; +} + +#if RTC_LOG_ENABLED() +void Log(const LogArgType* fmt, ...); +#else +inline void Log(const LogArgType* fmt, ...) { + // Do nothing, shouldn't be invoked +} +#endif + +// Ephemeral type that represents the result of the logging << operator. +template +class LogStreamer; + +// Base case: Before the first << argument. +template <> +class LogStreamer<> final { + public: + template ()))> + RTC_FORCE_INLINE LogStreamer operator<<(const U& arg) const { + return LogStreamer(MakeVal(arg), this); + } + + template + RTC_FORCE_INLINE static void Call(const Us&... args) { + static constexpr LogArgType t[] = {Us::Type()..., LogArgType::kEnd}; + Log(t, args.GetVal()...); + } +}; + +// Inductive case: We've already seen at least one << argument. The most recent +// one had type `T`, and the earlier ones had types `Ts`. +template +class LogStreamer final { + public: + RTC_FORCE_INLINE LogStreamer(T arg, const LogStreamer* prior) + : arg_(arg), prior_(prior) {} + + template ()))> + RTC_FORCE_INLINE LogStreamer operator<<(const U& arg) const { + return LogStreamer(MakeVal(arg), this); + } + + template + RTC_FORCE_INLINE void Call(const Us&... args) const { + prior_->Call(arg_, args...); + } + + private: + // The most recent argument. + T arg_; + + // Earlier arguments. + const LogStreamer* prior_; +}; + +class LogCall final { + public: + // This can be any binary operator with precedence lower than <<. + // We return bool here to be able properly remove logging if + // RTC_DISABLE_LOGGING is defined. + template + RTC_FORCE_INLINE bool operator&(const LogStreamer& streamer) { + streamer.Call(); + return true; + } +}; + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". +class LogMessageVoidify { + public: + LogMessageVoidify() = default; + // This has to be an operator with a precedence lower than << but + // higher than ?: + template + void operator&(LogStreamer&& /* streamer */) {} +}; + +} // namespace webrtc_logging_impl + +// Direct use of this class is deprecated; please use the logging macros +// instead. +// TODO(bugs.webrtc.org/9278): Move this class to an unnamed namespace in the +// .cc file. +class LogMessage { + public: + // Same as the above, but using a compile-time constant for the logging + // severity. This saves space at the call site, since passing an empty struct + // is generally the same as not passing an argument at all. + template + RTC_NO_INLINE LogMessage(const char* file, + int line, + std::integral_constant) + : LogMessage(file, line, S) {} + +#if RTC_LOG_ENABLED() + LogMessage(const char* file, int line, LoggingSeverity sev); + LogMessage(const char* file, + int line, + LoggingSeverity sev, + LogErrorContext err_ctx, + int err); +#if defined(WEBRTC_ANDROID) + LogMessage(const char* file, int line, LoggingSeverity sev, const char* tag); +#endif + ~LogMessage(); + + LogMessage(const LogMessage&) = delete; + LogMessage& operator=(const LogMessage&) = delete; + + void AddTag(const char* tag); + StringBuilder& stream(); + // Returns the time at which this function was called for the first time. + // The time will be used as the logging start time. + // If this is not called externally, the LogMessage ctor also calls it, in + // which case the logging start time will be the time of the first LogMessage + // instance is created. + static int64_t LogStartTime(); + // Returns the wall clock equivalent of `LogStartTime`, in seconds from the + // epoch. + static uint32_t WallClockStartTime(); + // LogThreads: Display the thread identifier of the current thread + static void LogThreads(bool on = true); + // LogTimestamps: Display the elapsed time of the program + static void LogTimestamps(bool on = true); + // These are the available logging channels + // Debug: Debug console on Windows, otherwise stderr + static void LogToDebug(LoggingSeverity min_sev); + static LoggingSeverity GetLogToDebug(); + // Sets whether logs will be directed to stderr in debug mode. + static void SetLogToStderr(bool log_to_stderr); + // Stream: Any non-blocking stream interface. + // Installs the `stream` to collect logs with severtiy `min_sev` or higher. + // `stream` must live until deinstalled by RemoveLogToStream. + // If `stream` is the first stream added to the system, we might miss some + // early concurrent log statement happening from another thread happening near + // this instant. + static void AddLogToStream(LogSink* stream, LoggingSeverity min_sev); + // Removes the specified stream, without destroying it. When the method + // has completed, it's guaranteed that `stream` will receive no more logging + // calls. + static void RemoveLogToStream(LogSink* stream); + // Returns the severity for the specified stream, of if none is specified, + // the minimum stream severity. + static int GetLogToStream(LogSink* stream = nullptr); + // Testing against MinLogSeverity allows code to avoid potentially expensive + // logging operations by pre-checking the logging level. + static int GetMinLogSeverity(); + // Parses the provided parameter stream to configure the options above. + // Useful for configuring logging from the command line. + static void ConfigureLogging(absl::string_view params); + // Checks the current global debug severity and if the `streams_` collection + // is empty. If `severity` is smaller than the global severity and if the + // `streams_` collection is empty, the LogMessage will be considered a noop + // LogMessage. + static bool IsNoop(LoggingSeverity severity); + // Version of IsNoop that uses fewer instructions at the call site, since the + // caller doesn't have to pass an argument. + template + RTC_NO_INLINE static bool IsNoop() { + return IsNoop(S); + } +#else + // Next methods do nothing; no one will call these functions. + LogMessage(const char* file, int line, LoggingSeverity sev) {} + LogMessage(const char* file, + int line, + LoggingSeverity sev, + LogErrorContext err_ctx, + int err) {} +#if defined(WEBRTC_ANDROID) + LogMessage(const char* file, int line, LoggingSeverity sev, const char* tag) { + } +#endif + ~LogMessage() = default; + + inline void AddTag(const char* tag) {} + inline StringBuilder& stream() { return print_stream_; } + inline static int64_t LogStartTime() { return 0; } + inline static uint32_t WallClockStartTime() { return 0; } + inline static void LogThreads(bool on = true) {} + inline static void LogTimestamps(bool on = true) {} + inline static void LogToDebug(LoggingSeverity min_sev) {} + inline static LoggingSeverity GetLogToDebug() { + return LoggingSeverity::LS_INFO; + } + inline static void SetLogToStderr(bool log_to_stderr) {} + inline static void AddLogToStream(LogSink* stream, LoggingSeverity min_sev) {} + inline static void RemoveLogToStream(LogSink* stream) {} + inline static int GetLogToStream(LogSink* stream = nullptr) { return 0; } + inline static int GetMinLogSeverity() { return 0; } + inline static void ConfigureLogging(absl::string_view params) {} + static constexpr bool IsNoop(LoggingSeverity severity) { return true; } + template + static constexpr bool IsNoop() { + return IsNoop(S); + } +#endif // RTC_LOG_ENABLED() + + private: + friend class LogMessageForTesting; + +#if RTC_LOG_ENABLED() + // Updates min_sev_ appropriately when debug sinks change. + static void UpdateMinLogSeverity(); + + // This writes out the actual log messages. + static void OutputToDebug(const LogLineRef& log_line_ref); + + // Called from the dtor (or from a test) to append optional extra error + // information to the log stream and a newline character. + void FinishPrintStream(); + + LogLineRef log_line_; + + // String data generated in the constructor, that should be appended to + // the message before output. + std::string extra_; + + // The output streams and their associated severities + static LogSink* streams_; + + // Holds true with high probability if `streams_` is empty, false with high + // probability otherwise. Operated on with std::memory_order_relaxed because + // it's ok to lose or log some additional statements near the instant streams + // are added/removed. + static std::atomic streams_empty_; + + // Flags for formatting options and their potential values. + static bool log_thread_; + static bool log_timestamp_; + + // Determines if logs will be directed to stderr in debug mode. + static bool log_to_stderr_; +#else // RTC_LOG_ENABLED() + // Next methods do nothing; no one will call these functions. + inline static void UpdateMinLogSeverity() {} +#if defined(WEBRTC_ANDROID) + inline static void OutputToDebug(absl::string_view filename, + int line, + absl::string_view msg, + LoggingSeverity severity, + const char* tag) {} +#else + inline static void OutputToDebug(absl::string_view filename, + int line, + absl::string_view msg, + LoggingSeverity severity) {} +#endif // defined(WEBRTC_ANDROID) + inline void FinishPrintStream() {} +#endif // RTC_LOG_ENABLED() + + // The stringbuilder that buffers the formatted message before output + StringBuilder print_stream_; +}; + +////////////////////////////////////////////////////////////////////// +// Logging Helpers +////////////////////////////////////////////////////////////////////// + +#define RTC_LOG_FILE_LINE(sev, file, line) \ + ::webrtc::webrtc_logging_impl::LogCall() & \ + ::webrtc::webrtc_logging_impl::LogStreamer<>() \ + << ::webrtc::webrtc_logging_impl::LogMetadata(file, line, sev) + +#define RTC_LOG(sev) \ + !::webrtc::LogMessage::IsNoop<::webrtc::sev>() && \ + RTC_LOG_FILE_LINE(::webrtc::sev, __FILE__, __LINE__) + +#define RTC_LOG_IF(sev, condition) \ + !::webrtc::LogMessage::IsNoop<::webrtc::sev>() && (condition) && \ + RTC_LOG_FILE_LINE(::webrtc::sev, __FILE__, __LINE__) + +// The _V version is for when a variable is passed in. +#define RTC_LOG_V(sev) \ + !::webrtc::LogMessage::IsNoop(sev) && \ + RTC_LOG_FILE_LINE(sev, __FILE__, __LINE__) + +// The _F version prefixes the message with the current function name. +#if (defined(__GNUC__) && !defined(NDEBUG)) || defined(WANT_PRETTY_LOG_F) +#define RTC_LOG_F(sev) RTC_LOG(sev) << __PRETTY_FUNCTION__ << ": " +#define RTC_LOG_IF_F(sev, condition) \ + RTC_LOG_IF(sev, condition) << __PRETTY_FUNCTION__ << ": " +#define RTC_LOG_T_F(sev) \ + RTC_LOG(sev) << this << ": " << __PRETTY_FUNCTION__ << ": " +#else +#define RTC_LOG_F(sev) RTC_LOG(sev) << __FUNCTION__ << ": " +#define RTC_LOG_IF_F(sev, condition) \ + RTC_LOG_IF(sev, condition) << __FUNCTION__ << ": " +#define RTC_LOG_T_F(sev) RTC_LOG(sev) << this << ": " << __FUNCTION__ << ": " +#endif + +#define RTC_LOG_CHECK_LEVEL(sev) ::webrtc::LogCheckLevel(::webrtc::sev) +#define RTC_LOG_CHECK_LEVEL_V(sev) ::webrtc::LogCheckLevel(sev) + +inline bool LogCheckLevel(LoggingSeverity sev) { + return (LogMessage::GetMinLogSeverity() <= sev); +} + +#define RTC_LOG_E(sev, ctx, err) \ + !::webrtc::LogMessage::IsNoop<::webrtc::sev>() && \ + ::webrtc::webrtc_logging_impl::LogCall() & \ + ::webrtc::webrtc_logging_impl::LogStreamer<>() \ + << ::webrtc::webrtc_logging_impl::LogMetadataErr { \ + {__FILE__, __LINE__, ::webrtc::sev}, ::webrtc::ERRCTX_##ctx, (err) \ + } + +#define RTC_LOG_T(sev) RTC_LOG(sev) << this << ": " + +#define RTC_LOG_ERRNO_EX(sev, err) RTC_LOG_E(sev, ERRNO, err) +#define RTC_LOG_ERRNO(sev) RTC_LOG_ERRNO_EX(sev, errno) + +#if defined(WEBRTC_WIN) +#define RTC_LOG_GLE_EX(sev, err) RTC_LOG_E(sev, HRESULT, err) +#define RTC_LOG_GLE(sev) RTC_LOG_GLE_EX(sev, static_cast(GetLastError())) +#define RTC_LOG_ERR_EX(sev, err) RTC_LOG_GLE_EX(sev, err) +#define RTC_LOG_ERR(sev) RTC_LOG_GLE(sev) +#elif defined(__native_client__) && __native_client__ +#define RTC_LOG_ERR_EX(sev, err) RTC_LOG(sev) +#define RTC_LOG_ERR(sev) RTC_LOG(sev) +#elif defined(WEBRTC_POSIX) +#define RTC_LOG_ERR_EX(sev, err) RTC_LOG_ERRNO_EX(sev, err) +#define RTC_LOG_ERR(sev) RTC_LOG_ERRNO(sev) +#endif // WEBRTC_WIN + +#ifdef WEBRTC_ANDROID + +namespace webrtc_logging_impl { +// TODO(kwiberg): Replace these with absl::string_view. +inline const char* AdaptString(const char* str) { + return str; +} +inline const char* AdaptString(const std::string& str) { + return str.c_str(); +} +} // namespace webrtc_logging_impl + +#define RTC_LOG_TAG(sev, tag) \ + !::webrtc::LogMessage::IsNoop(sev) && \ + ::webrtc::webrtc_logging_impl::LogCall() & \ + ::webrtc::webrtc_logging_impl::LogStreamer<>() \ + << ::webrtc::webrtc_logging_impl::LogMetadataTag { \ + sev, ::webrtc::webrtc_logging_impl::AdaptString(tag) \ + } + +#else + +// DEPRECATED. This macro is only intended for Android. +#define RTC_LOG_TAG(sev, tag) RTC_LOG_V(sev) + +#endif + +// The RTC_DLOG macros are equivalent to their RTC_LOG counterparts except that +// they only generate code in debug builds. +#if RTC_DLOG_IS_ON +#define RTC_DLOG(sev) RTC_LOG(sev) +#define RTC_DLOG_IF(sev, condition) RTC_LOG_IF(sev, condition) +#define RTC_DLOG_V(sev) RTC_LOG_V(sev) +#define RTC_DLOG_F(sev) RTC_LOG_F(sev) +#define RTC_DLOG_IF_F(sev, condition) RTC_LOG_IF_F(sev, condition) +#else +#define RTC_DLOG_EAT_STREAM_PARAMS() \ + while (false) \ + ::webrtc::webrtc_logging_impl::LogMessageVoidify() & \ + (::webrtc::webrtc_logging_impl::LogStreamer<>()) +#define RTC_DLOG(sev) RTC_DLOG_EAT_STREAM_PARAMS() +#define RTC_DLOG_IF(sev, condition) RTC_DLOG_EAT_STREAM_PARAMS() +#define RTC_DLOG_V(sev) RTC_DLOG_EAT_STREAM_PARAMS() +#define RTC_DLOG_F(sev) RTC_DLOG_EAT_STREAM_PARAMS() +#define RTC_DLOG_IF_F(sev, condition) RTC_DLOG_EAT_STREAM_PARAMS() +#endif + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::LoggingSeverity; +using ::webrtc::LogLineRef; +using ::webrtc::LogMessage; +using ::webrtc::LogSink; +using ::webrtc::LS_ERROR; +using ::webrtc::LS_INFO; +using ::webrtc::LS_NONE; +using ::webrtc::LS_VERBOSE; +using ::webrtc::LS_WARNING; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_LOGGING_H_ diff --git a/pkg/apm/webrtc/rtc_base/mdns_responder_interface.h b/pkg/apm/webrtc/rtc_base/mdns_responder_interface.h new file mode 100644 index 00000000..fb4c1c94 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/mdns_responder_interface.h @@ -0,0 +1,51 @@ +/* + * 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 RTC_BASE_MDNS_RESPONDER_INTERFACE_H_ +#define RTC_BASE_MDNS_RESPONDER_INTERFACE_H_ + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/ip_address.h" + +namespace webrtc { + +// Defines an mDNS responder that can be used in ICE candidate gathering, where +// the local IP addresses of host candidates are replaced by mDNS hostnames. +class MdnsResponderInterface { + public: + using NameCreatedCallback = + std::function; + using NameRemovedCallback = std::function; + + MdnsResponderInterface() = default; + virtual ~MdnsResponderInterface() = default; + + // Asynchronously creates and returns a new name via `callback` for `addr` if + // there is no name mapped to it by this responder, and initializes the + // reference count of this name to one. Otherwise the existing name mapped to + // `addr` is returned and its reference count is incremented by one. + virtual void CreateNameForAddress(const IPAddress& addr, + NameCreatedCallback callback) = 0; + // Decrements the reference count of the mapped name of `addr`, if + // there is a map created previously via CreateNameForAddress; asynchronously + // removes the association between `addr` and its mapped name, and returns + // true via `callback` if the decremented reference count reaches zero. + // Otherwise no operation is done and false is returned via `callback` + // asynchronously. + virtual void RemoveNameForAddress(const IPAddress& addr, + NameRemovedCallback callback) = 0; +}; + +} // namespace webrtc + +#endif // RTC_BASE_MDNS_RESPONDER_INTERFACE_H_ diff --git a/pkg/apm/webrtc/rtc_base/memory/aligned_malloc.cc b/pkg/apm/webrtc/rtc_base/memory/aligned_malloc.cc new file mode 100644 index 00000000..7add0793 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/memory/aligned_malloc.cc @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/memory/aligned_malloc.h" + +#include // for free, malloc +#include // for memcpy + +#include "rtc_base/checks.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +// Reference on memory alignment: +// http://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me +namespace webrtc { + +uintptr_t GetRightAlign(uintptr_t start_pos, size_t alignment) { + // The pointer should be aligned with `alignment` bytes. The - 1 guarantees + // that it is aligned towards the closest higher (right) address. + return (start_pos + alignment - 1) & ~(alignment - 1); +} + +// Alignment must be an integer power of two. +bool ValidAlignment(size_t alignment) { + if (!alignment) { + return false; + } + return (alignment & (alignment - 1)) == 0; +} + +void* GetRightAlign(const void* pointer, size_t alignment) { + if (!pointer) { + return NULL; + } + if (!ValidAlignment(alignment)) { + return NULL; + } + uintptr_t start_pos = reinterpret_cast(pointer); + return reinterpret_cast(GetRightAlign(start_pos, alignment)); +} + +void* AlignedMalloc(size_t size, size_t alignment) { + if (size == 0) { + return NULL; + } + if (!ValidAlignment(alignment)) { + return NULL; + } + + // The memory is aligned towards the lowest address that so only + // alignment - 1 bytes needs to be allocated. + // A pointer to the start of the memory must be stored so that it can be + // retreived for deletion, ergo the sizeof(uintptr_t). + void* memory_pointer = malloc(size + sizeof(uintptr_t) + alignment - 1); + RTC_CHECK(memory_pointer) << "Couldn't allocate memory in AlignedMalloc"; + + // Aligning after the sizeof(uintptr_t) bytes will leave room for the header + // in the same memory block. + uintptr_t align_start_pos = reinterpret_cast(memory_pointer); + align_start_pos += sizeof(uintptr_t); + uintptr_t aligned_pos = GetRightAlign(align_start_pos, alignment); + void* aligned_pointer = reinterpret_cast(aligned_pos); + + // Store the address to the beginning of the memory just before the aligned + // memory. + uintptr_t header_pos = aligned_pos - sizeof(uintptr_t); + void* header_pointer = reinterpret_cast(header_pos); + uintptr_t memory_start = reinterpret_cast(memory_pointer); + memcpy(header_pointer, &memory_start, sizeof(uintptr_t)); + + return aligned_pointer; +} + +void AlignedFree(void* mem_block) { + if (mem_block == NULL) { + return; + } + uintptr_t aligned_pos = reinterpret_cast(mem_block); + uintptr_t header_pos = aligned_pos - sizeof(uintptr_t); + + // Read out the address of the AlignedMemory struct from the header. + uintptr_t memory_start_pos = *reinterpret_cast(header_pos); + void* memory_start = reinterpret_cast(memory_start_pos); + free(memory_start); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/memory/aligned_malloc.h b/pkg/apm/webrtc/rtc_base/memory/aligned_malloc.h new file mode 100644 index 00000000..1c7d3038 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/memory/aligned_malloc.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_MEMORY_ALIGNED_MALLOC_H_ +#define RTC_BASE_MEMORY_ALIGNED_MALLOC_H_ + +// The functions declared here +// 1) Allocates block of aligned memory. +// 2) Re-calculates a pointer such that it is aligned to a higher or equal +// address. +// Note: alignment must be a power of two. The alignment is in bytes. + +#include + +namespace webrtc { + +// Returns a pointer to the first boundry of `alignment` bytes following the +// address of `ptr`. +// Note that there is no guarantee that the memory in question is available. +// `ptr` has no requirements other than it can't be NULL. +void* GetRightAlign(const void* ptr, size_t alignment); + +// Allocates memory of `size` bytes aligned on an `alignment` boundry. +// The return value is a pointer to the memory. Note that the memory must +// be de-allocated using AlignedFree. +void* AlignedMalloc(size_t size, size_t alignment); +// De-allocates memory created using the AlignedMalloc() API. +void AlignedFree(void* mem_block); + +// Templated versions to facilitate usage of aligned malloc without casting +// to and from void*. +template +T* GetRightAlign(const T* ptr, size_t alignment) { + return reinterpret_cast( + GetRightAlign(reinterpret_cast(ptr), alignment)); +} +template +T* AlignedMalloc(size_t size, size_t alignment) { + return reinterpret_cast(AlignedMalloc(size, alignment)); +} + +// Deleter for use with unique_ptr. E.g., use as +// std::unique_ptr foo; +struct AlignedFreeDeleter { + inline void operator()(void* ptr) const { AlignedFree(ptr); } +}; + +} // namespace webrtc + +#endif // RTC_BASE_MEMORY_ALIGNED_MALLOC_H_ diff --git a/pkg/apm/webrtc/rtc_base/memory/always_valid_pointer.h b/pkg/apm/webrtc/rtc_base/memory/always_valid_pointer.h new file mode 100644 index 00000000..4e68104b --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/memory/always_valid_pointer.h @@ -0,0 +1,248 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_MEMORY_ALWAYS_VALID_POINTER_H_ +#define RTC_BASE_MEMORY_ALWAYS_VALID_POINTER_H_ + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +// This template allows the instantiation of a pointer to Interface in such a +// way that if it is passed a null pointer, an object of class Default will be +// created, which will be deallocated when the pointer is deleted. +template +class AlwaysValidPointer { + public: + explicit AlwaysValidPointer(Interface* pointer) + : owned_instance_(pointer ? nullptr : std::make_unique()), + pointer_(pointer ? pointer : owned_instance_.get()) { + RTC_DCHECK(pointer_); + } + + template ::value), + bool>::type = true> + AlwaysValidPointer(Interface* pointer, Arg arg) + : owned_instance_(pointer ? nullptr + : std::make_unique(std::move(arg))), + pointer_(pointer ? pointer : owned_instance_.get()) { + RTC_DCHECK(pointer_); + } + + // Multiple arguments + template + AlwaysValidPointer(Interface* pointer, Arg1 arg1, Args... args) + : owned_instance_(pointer + ? nullptr + : std::make_unique(std::move(arg1), + std::move(args...))), + pointer_(pointer ? pointer : owned_instance_.get()) { + RTC_DCHECK(pointer_); + } + + // Create a pointer by + // a) using |pointer|, without taking ownership + // b) calling |function| and taking ownership of the result + template ::value, + bool>::type = true> + AlwaysValidPointer(Interface* pointer, Func function) + : owned_instance_(pointer ? nullptr : function()), + pointer_(owned_instance_ ? owned_instance_.get() : pointer) { + RTC_DCHECK(pointer_); + } + + // Create a pointer by + // a) taking over ownership of |instance| + // b) or fallback to |pointer|, without taking ownership. + // c) or Default. + AlwaysValidPointer(std::unique_ptr&& instance, Interface* pointer) + : owned_instance_( + instance + ? std::move(instance) + : (pointer == nullptr ? std::make_unique() : nullptr)), + pointer_(owned_instance_ ? owned_instance_.get() : pointer) { + RTC_DCHECK(pointer_); + } + + // Create a pointer by + // a) taking over ownership of |instance| + // b) or fallback to |pointer|, without taking ownership. + // c) or Default (with forwarded args). + template + AlwaysValidPointer(std::unique_ptr&& instance, + Interface* pointer, + Args... args) + : owned_instance_( + instance ? std::move(instance) + : (pointer == nullptr + ? std::make_unique(std::move(args...)) + : nullptr)), + pointer_(owned_instance_ ? owned_instance_.get() : pointer) { + RTC_DCHECK(pointer_); + } + + Interface* get() { return pointer_; } + Interface* operator->() { return pointer_; } + Interface& operator*() { return *pointer_; } + + Interface* get() const { return pointer_; } + Interface* operator->() const { return pointer_; } + Interface& operator*() const { return *pointer_; } + + private: + const std::unique_ptr owned_instance_; + Interface* const pointer_; +}; + +// This class is similar to AlwaysValidPointer, but it does not create +// a default object and crashes if none of the input pointers are non-null. +template +class AlwaysValidPointerNoDefault { + public: + explicit AlwaysValidPointerNoDefault(Interface* pointer) : pointer_(pointer) { + RTC_CHECK(pointer_); + } + + // Create a pointer by + // a) taking over ownership of |instance| + // b) or fallback to |pointer|, without taking ownership. + // At least one of the arguments must be non-null. + explicit AlwaysValidPointerNoDefault(std::unique_ptr instance, + Interface* pointer = nullptr) + : owned_instance_(std::move(instance)), + pointer_(owned_instance_ ? owned_instance_.get() : pointer) { + RTC_CHECK(pointer_); + } + + Interface* get() { return pointer_; } + Interface* operator->() { return pointer_; } + Interface& operator*() { return *pointer_; } + + Interface* get() const { return pointer_; } + Interface* operator->() const { return pointer_; } + Interface& operator*() const { return *pointer_; } + + private: + const std::unique_ptr owned_instance_; + Interface* const pointer_; +}; + +template +bool operator==(const AlwaysValidPointer& a, + const AlwaysValidPointer& b) { + return a.get() == b.get(); +} + +template +bool operator!=(const AlwaysValidPointer& a, + const AlwaysValidPointer& b) { + return !(a == b); +} + +template +bool operator==(const AlwaysValidPointer& a, std::nullptr_t) { + return a.get() == nullptr; +} + +template +bool operator!=(const AlwaysValidPointer& a, std::nullptr_t) { + return !(a == nullptr); +} + +template +bool operator==(std::nullptr_t, const AlwaysValidPointer& a) { + return a.get() == nullptr; +} + +template +bool operator!=(std::nullptr_t, const AlwaysValidPointer& a) { + return !(a == nullptr); +} + +template +bool operator==(const AlwaysValidPointerNoDefault& a, + const AlwaysValidPointerNoDefault& b) { + return a.get() == b.get(); +} + +template +bool operator!=(const AlwaysValidPointerNoDefault& a, + const AlwaysValidPointerNoDefault& b) { + return !(a == b); +} + +template +bool operator==(const AlwaysValidPointerNoDefault& a, std::nullptr_t) { + return a.get() == nullptr; +} + +template +bool operator!=(const AlwaysValidPointerNoDefault& a, std::nullptr_t) { + return !(a == nullptr); +} + +template +bool operator==(std::nullptr_t, const AlwaysValidPointerNoDefault& a) { + return a.get() == nullptr; +} + +template +bool operator!=(std::nullptr_t, const AlwaysValidPointerNoDefault& a) { + return !(a == nullptr); +} + +// Comparison with raw pointer. +template +bool operator==(const AlwaysValidPointer& a, const V* b) { + return a.get() == b; +} + +template +bool operator!=(const AlwaysValidPointer& a, const V* b) { + return !(a == b); +} + +template +bool operator==(const T* a, const AlwaysValidPointer& b) { + return a == b.get(); +} + +template +bool operator!=(const T* a, const AlwaysValidPointer& b) { + return !(a == b); +} + +template +bool operator==(const AlwaysValidPointerNoDefault& a, const U* b) { + return a.get() == b; +} + +template +bool operator!=(const AlwaysValidPointerNoDefault& a, const U* b) { + return !(a == b); +} + +template +bool operator==(const T* a, const AlwaysValidPointerNoDefault& b) { + return a == b.get(); +} + +template +bool operator!=(const T* a, const AlwaysValidPointerNoDefault& b) { + return !(a == b); +} + +} // namespace webrtc + +#endif // RTC_BASE_MEMORY_ALWAYS_VALID_POINTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/memory/fifo_buffer.h b/pkg/apm/webrtc/rtc_base/memory/fifo_buffer.h new file mode 100644 index 00000000..a36717d1 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/memory/fifo_buffer.h @@ -0,0 +1,132 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_MEMORY_FIFO_BUFFER_H_ +#define RTC_BASE_MEMORY_FIFO_BUFFER_H_ + +#include +#include +#include + +#include "api/array_view.h" +#include "api/sequence_checker.h" +#include "api/task_queue/pending_task_safety_flag.h" +#include "rtc_base/stream.h" +#include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +// FifoBuffer allows for efficient, thread-safe buffering of data between +// writer and reader. +class FifoBuffer final : public StreamInterface { + public: + // Creates a FIFO buffer with the specified capacity. + explicit FifoBuffer(size_t length); + // Creates a FIFO buffer with the specified capacity and owner + FifoBuffer(size_t length, Thread* owner); + ~FifoBuffer() override; + + FifoBuffer(const FifoBuffer&) = delete; + FifoBuffer& operator=(const FifoBuffer&) = delete; + + // Gets the amount of data currently readable from the buffer. + bool GetBuffered(size_t* data_len) const; + + // StreamInterface methods + StreamState GetState() const override; + StreamResult Read(ArrayView buffer, + size_t& bytes_read, + int& error) override; + StreamResult Write(ArrayView buffer, + size_t& bytes_written, + int& error) override; + void Close() override; + + // Seek to a byte offset from the beginning of the stream. Returns false if + // the stream does not support seeking, or cannot seek to the specified + // position. + bool SetPosition(size_t position); + + // Get the byte offset of the current position from the start of the stream. + // Returns false if the position is not known. + bool GetPosition(size_t* position) const; + + // Seek to the start of the stream. + bool Rewind() { return SetPosition(0); } + + // GetReadData returns a pointer to a buffer which is owned by the stream. + // The buffer contains data_len bytes. null is returned if no data is + // available, or if the method fails. If the caller processes the data, it + // must call ConsumeReadData with the number of processed bytes. GetReadData + // does not require a matching call to ConsumeReadData if the data is not + // processed. Read and ConsumeReadData invalidate the buffer returned by + // GetReadData. + const void* GetReadData(size_t* data_len); + void ConsumeReadData(size_t used); + // GetWriteBuffer returns a pointer to a buffer which is owned by the stream. + // The buffer has a capacity of buf_len bytes. null is returned if there is + // no buffer available, or if the method fails. The call may write data to + // the buffer, and then call ConsumeWriteBuffer with the number of bytes + // written. GetWriteBuffer does not require a matching call to + // ConsumeWriteData if no data is written. Write and + // ConsumeWriteData invalidate the buffer returned by GetWriteBuffer. + void* GetWriteBuffer(size_t* buf_len); + void ConsumeWriteBuffer(size_t used); + + private: + void PostEvent(int events, int err) { + RTC_DCHECK_RUN_ON(owner_); + owner_->PostTask( + webrtc::SafeTask(task_safety_.flag(), [this, events, err]() { + RTC_DCHECK_RUN_ON(&callback_sequence_); + FireEvent(events, err); + })); + } + + // Helper method that implements Read. Caller must acquire a lock + // when calling this method. + StreamResult ReadLocked(void* buffer, size_t bytes, size_t* bytes_read) + RTC_EXCLUSIVE_LOCKS_REQUIRED(callback_sequence_); + + // Helper method that implements Write. Caller must acquire a lock + // when calling this method. + StreamResult WriteLocked(const void* buffer, + size_t bytes, + size_t* bytes_written) + RTC_EXCLUSIVE_LOCKS_REQUIRED(callback_sequence_); + + ScopedTaskSafety task_safety_; + + // keeps the opened/closed state of the stream + StreamState state_ RTC_GUARDED_BY(callback_sequence_); + // the allocated buffer + std::unique_ptr buffer_ RTC_GUARDED_BY(callback_sequence_); + // size of the allocated buffer + const size_t buffer_length_; + // amount of readable data in the buffer + size_t data_length_ RTC_GUARDED_BY(callback_sequence_); + // offset to the readable data + size_t read_position_ RTC_GUARDED_BY(callback_sequence_); + // stream callbacks are dispatched on this thread + Thread* const owner_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::FifoBuffer; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_MEMORY_FIFO_BUFFER_H_ diff --git a/pkg/apm/webrtc/rtc_base/memory/memory.go b/pkg/apm/webrtc/rtc_base/memory/memory.go new file mode 100644 index 00000000..660110d0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/memory/memory.go @@ -0,0 +1,10 @@ +//go:build console + +package memory + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/rtc_base/memory_stream.h b/pkg/apm/webrtc/rtc_base/memory_stream.h new file mode 100644 index 00000000..c01d5902 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/memory_stream.h @@ -0,0 +1,70 @@ +/* + * 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 RTC_BASE_MEMORY_STREAM_H_ +#define RTC_BASE_MEMORY_STREAM_H_ + +#include + +#include + +#include "api/array_view.h" +#include "rtc_base/stream.h" + +namespace webrtc { + +// MemoryStream dynamically resizes to accomodate written data. + +class MemoryStream final : public StreamInterface { + public: + MemoryStream(); + ~MemoryStream() override; + + StreamState GetState() const override; + StreamResult Read(ArrayView buffer, + size_t& bytes_read, + int& error) override; + StreamResult Write(ArrayView buffer, + size_t& bytes_written, + int& error) override; + void Close() override; + bool GetSize(size_t* size) const; + bool ReserveSize(size_t size); + + bool SetPosition(size_t position); + bool GetPosition(size_t* position) const; + void Rewind(); + + char* GetBuffer() { return buffer_; } + const char* GetBuffer() const { return buffer_; } + + void SetData(const void* data, size_t length); + + private: + StreamResult DoReserve(size_t size, int* error); + + // Invariant: 0 <= seek_position <= data_length_ <= buffer_length_ + char* buffer_ = nullptr; + size_t buffer_length_ = 0; + size_t data_length_ = 0; + size_t seek_position_ = 0; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::MemoryStream; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_MEMORY_STREAM_H_ diff --git a/pkg/apm/webrtc/rtc_base/memory_usage.h b/pkg/apm/webrtc/rtc_base/memory_usage.h new file mode 100644 index 00000000..bd1b5ea4 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/memory_usage.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_MEMORY_USAGE_H_ +#define RTC_BASE_MEMORY_USAGE_H_ + +#include + +namespace webrtc { + +// Returns current memory used by the process in bytes (working set size on +// Windows and resident set size on other platforms). +// Returns -1 on failure. +int64_t GetProcessResidentSizeBytes(); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::GetProcessResidentSizeBytes; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_MEMORY_USAGE_H_ diff --git a/pkg/apm/webrtc/rtc_base/message_digest.h b/pkg/apm/webrtc/rtc_base/message_digest.h new file mode 100644 index 00000000..22a86c7a --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/message_digest.h @@ -0,0 +1,152 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_MESSAGE_DIGEST_H_ +#define RTC_BASE_MESSAGE_DIGEST_H_ + +#include + +#include + +#include "absl/strings/string_view.h" + +namespace webrtc { + +// Definitions for the digest algorithms. +extern const char DIGEST_MD5[]; +extern const char DIGEST_SHA_1[]; +extern const char DIGEST_SHA_224[]; +extern const char DIGEST_SHA_256[]; +extern const char DIGEST_SHA_384[]; +extern const char DIGEST_SHA_512[]; + +// A general class for computing hashes. +class MessageDigest { + public: + enum { kMaxSize = 64 }; // Maximum known size (SHA-512) + virtual ~MessageDigest() {} + // Returns the digest output size (e.g. 16 bytes for MD5). + virtual size_t Size() const = 0; + // Updates the digest with `len` bytes from `buf`. + virtual void Update(const void* buf, size_t len) = 0; + // Outputs the digest value to `buf` with length `len`. + // Returns the number of bytes written, i.e., Size(). + virtual size_t Finish(void* buf, size_t len) = 0; +}; + +// A factory class for creating digest objects. +class MessageDigestFactory { + public: + static MessageDigest* Create(absl::string_view alg); +}; + +// A check that an algorithm is in a list of approved digest algorithms +// from RFC 4572 (FIPS 180). +bool IsFips180DigestAlgorithm(absl::string_view alg); + +// Functions to create hashes. + +// Computes the hash of `in_len` bytes of `input`, using the `digest` hash +// implementation, and outputs the hash to the buffer `output`, which is +// `out_len` bytes long. Returns the number of bytes written to `output` if +// successful, or 0 if `out_len` was too small. +size_t ComputeDigest(MessageDigest* digest, + const void* input, + size_t in_len, + void* output, + size_t out_len); +// Like the previous function, but creates a digest implementation based on +// the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns 0 if there is no +// digest with the given name. +size_t ComputeDigest(absl::string_view alg, + const void* input, + size_t in_len, + void* output, + size_t out_len); +// Computes the hash of `input` using the `digest` hash implementation, and +// returns it as a hex-encoded string. +std::string ComputeDigest(MessageDigest* digest, absl::string_view input); +// Like the previous function, but creates a digest implementation based on +// the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns empty string if +// there is no digest with the given name. +std::string ComputeDigest(absl::string_view alg, absl::string_view input); +// Like the previous function, but returns an explicit result code. +bool ComputeDigest(absl::string_view alg, + absl::string_view input, + std::string* output); + +// Shorthand way to compute a hex-encoded hash using MD5. +inline std::string MD5(absl::string_view input) { + return ComputeDigest(DIGEST_MD5, input); +} + +// Functions to compute RFC 2104 HMACs. + +// Computes the HMAC of `in_len` bytes of `input`, using the `digest` hash +// implementation and `key_len` bytes of `key` to key the HMAC, and outputs +// the HMAC to the buffer `output`, which is `out_len` bytes long. Returns the +// number of bytes written to `output` if successful, or 0 if `out_len` was too +// small. +size_t ComputeHmac(MessageDigest* digest, + const void* key, + size_t key_len, + const void* input, + size_t in_len, + void* output, + size_t out_len); +// Like the previous function, but creates a digest implementation based on +// the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns 0 if there is no +// digest with the given name. +size_t ComputeHmac(absl::string_view alg, + const void* key, + size_t key_len, + const void* input, + size_t in_len, + void* output, + size_t out_len); +// Computes the HMAC of `input` using the `digest` hash implementation and `key` +// to key the HMAC, and returns it as a hex-encoded string. +std::string ComputeHmac(MessageDigest* digest, + absl::string_view key, + absl::string_view input); +// Like the previous function, but creates a digest implementation based on +// the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns empty string if +// there is no digest with the given name. +std::string ComputeHmac(absl::string_view alg, + absl::string_view key, + absl::string_view input); +// Like the previous function, but returns an explicit result code. +bool ComputeHmac(absl::string_view alg, + absl::string_view key, + absl::string_view input, + std::string* output); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::ComputeDigest; +using ::webrtc::ComputeHmac; +using ::webrtc::DIGEST_MD5; +using ::webrtc::DIGEST_SHA_1; +using ::webrtc::DIGEST_SHA_224; +using ::webrtc::DIGEST_SHA_256; +using ::webrtc::DIGEST_SHA_384; +using ::webrtc::DIGEST_SHA_512; +using ::webrtc::IsFips180DigestAlgorithm; +using ::webrtc::MD5; +using ::webrtc::MessageDigest; +using ::webrtc::MessageDigestFactory; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_MESSAGE_DIGEST_H_ diff --git a/pkg/apm/webrtc/rtc_base/net_helper.h b/pkg/apm/webrtc/rtc_base/net_helper.h new file mode 100644 index 00000000..95d22603 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/net_helper.h @@ -0,0 +1,48 @@ +/* + * 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 RTC_BASE_NET_HELPER_H_ +#define RTC_BASE_NET_HELPER_H_ + + +#include "absl/strings/string_view.h" +#include "rtc_base/system/rtc_export.h" + +// This header contains helper functions and constants used by different types +// of transports. +namespace webrtc { + +RTC_EXPORT extern const char UDP_PROTOCOL_NAME[]; +RTC_EXPORT extern const char TCP_PROTOCOL_NAME[]; +extern const char SSLTCP_PROTOCOL_NAME[]; +extern const char TLS_PROTOCOL_NAME[]; + +constexpr int kTcpHeaderSize = 20; +constexpr int kUdpHeaderSize = 8; + +// Get the transport layer overhead per packet based on the protocol. +int GetProtocolOverhead(absl::string_view protocol); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace cricket { +using ::webrtc::GetProtocolOverhead; +using ::webrtc::kTcpHeaderSize; +using ::webrtc::kUdpHeaderSize; +using ::webrtc::SSLTCP_PROTOCOL_NAME; +using ::webrtc::TCP_PROTOCOL_NAME; +using ::webrtc::TLS_PROTOCOL_NAME; +using ::webrtc::UDP_PROTOCOL_NAME; +} // namespace cricket +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NET_HELPER_H_ diff --git a/pkg/apm/webrtc/rtc_base/net_helpers.h b/pkg/apm/webrtc/rtc_base/net_helpers.h new file mode 100644 index 00000000..d27c263e --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/net_helpers.h @@ -0,0 +1,42 @@ +/* + * Copyright 2008 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NET_HELPERS_H_ +#define RTC_BASE_NET_HELPERS_H_ + +#if defined(WEBRTC_POSIX) +#include // IWYU pragma: export +#elif WEBRTC_WIN +#include // NOLINT + +#include "rtc_base/win32.h" +#endif + +#include "absl/strings/string_view.h" + +namespace webrtc { + +// rtc namespaced wrappers for inet_ntop and inet_pton so we can avoid +// the windows-native versions of these. +const char* inet_ntop(int af, const void* src, char* dst, socklen_t size); +int inet_pton(int af, absl::string_view src, void* dst); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::inet_ntop; +using ::webrtc::inet_pton; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NET_HELPERS_H_ diff --git a/pkg/apm/webrtc/rtc_base/network.h b/pkg/apm/webrtc/rtc_base/network.h new file mode 100644 index 00000000..f8ccfc75 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/network.h @@ -0,0 +1,600 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NETWORK_H_ +#define RTC_BASE_NETWORK_H_ + +#include + +#include +#include +#include +#include +#include + +#include "absl/base/nullability.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/environment/environment.h" +#include "api/field_trials_view.h" +#include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "api/task_queue/pending_task_safety_flag.h" +#include "rtc_base/checks.h" +#include "rtc_base/ip_address.h" +#include "rtc_base/mdns_responder_interface.h" +#include "rtc_base/network_constants.h" +#include "rtc_base/network_monitor.h" +#include "rtc_base/network_monitor_factory.h" +#include "rtc_base/socket_factory.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" + +#if defined(WEBRTC_POSIX) +#include "rtc_base/ifaddrs_converter.h" +struct ifaddrs; +#endif // defined(WEBRTC_POSIX) + +namespace webrtc { + +extern const char kPublicIPv4Host[]; +extern const char kPublicIPv6Host[]; + +class Network; + +// By default, ignore loopback interfaces on the host. +const int kDefaultNetworkIgnoreMask = webrtc::ADAPTER_TYPE_LOOPBACK; + +namespace webrtc_network_internal { +bool CompareNetworks(const std::unique_ptr& a, + const std::unique_ptr& b); +} // namespace webrtc_network_internal + +// Makes a string key for this network. Used in the network manager's maps. +// Network objects are keyed on interface name, network prefix and the +// length of that prefix. +std::string MakeNetworkKey(absl::string_view name, + const IPAddress& prefix, + int prefix_length); + +// Utility function that attempts to determine an adapter type by an interface +// name (e.g., "wlan0"). Can be used by NetworkManager subclasses when other +// mechanisms fail to determine the type. +RTC_EXPORT AdapterType GetAdapterTypeFromName(absl::string_view network_name); +RTC_EXPORT AdapterType GetAdapterTypeFromName(absl::string_view network_name); + +class DefaultLocalAddressProvider { + public: + virtual ~DefaultLocalAddressProvider() = default; + + // The default local address is the local address used in multi-homed endpoint + // when the any address (0.0.0.0 or ::) is used as the local address. It's + // important to check the return value as a IP family may not be enabled. + virtual bool GetDefaultLocalAddress(int family, IPAddress* ipaddr) const = 0; +}; + +class MdnsResponderProvider { + public: + virtual ~MdnsResponderProvider() = default; + + // Returns the mDNS responder that can be used to obfuscate the local IP + // addresses of ICE host candidates by mDNS hostnames. + // + // The provider MUST outlive the mDNS responder. + virtual MdnsResponderInterface* GetMdnsResponder() const = 0; +}; + +// Network/mask in CIDR representation. +class NetworkMask { + public: + NetworkMask(const IPAddress& addr, int prefix_length) + : address_(addr), prefix_length_(prefix_length) {} + + const IPAddress& address() const { return address_; } + int prefix_length() const { return prefix_length_; } + + bool operator==(const NetworkMask& o) const { + return address_ == o.address_ && prefix_length_ == o.prefix_length_; + } + + private: + IPAddress address_; + // Length of valid bits in address_ (for ipv4 valid range is 0-32) + int prefix_length_; +}; + +// Generic network manager interface. It provides list of local +// networks. +// +// Every method of NetworkManager (including the destructor) must be called on +// the same thread, except for the constructor which may be called on any +// thread. +// +// This allows constructing a NetworkManager subclass on one thread and +// passing it into an object that uses it on a different thread. +class RTC_EXPORT NetworkManager : public DefaultLocalAddressProvider, + public MdnsResponderProvider { + public: + // This enum indicates whether adapter enumeration is allowed. + enum EnumerationPermission { + ENUMERATION_ALLOWED, // Adapter enumeration is allowed. Getting 0 network + // from GetNetworks means that there is no network + // available. + ENUMERATION_BLOCKED, // Adapter enumeration is disabled. + // GetAnyAddressNetworks() should be used instead. + }; + + // Called when network list is updated. + sigslot::signal0<> SignalNetworksChanged; + + // Indicates a failure when getting list of network interfaces. + sigslot::signal0<> SignalError; + + // This should be called on the NetworkManager's thread before the + // NetworkManager is used. Subclasses may override this if necessary. + virtual void Initialize() {} + + // Start/Stop monitoring of network interfaces + // list. SignalNetworksChanged or SignalError is emitted immediately + // after StartUpdating() is called. After that SignalNetworksChanged + // is emitted whenever list of networks changes. + virtual void StartUpdating() = 0; + virtual void StopUpdating() = 0; + + // Returns the current list of networks available on this machine. + // StartUpdating() must be called before this method is called. + // It makes sure that repeated calls return the same object for a + // given network, so that quality is tracked appropriately. Does not + // include ignored networks. + // The returned vector of Network* is valid as long as the NetworkManager is + // alive. + virtual std::vector GetNetworks() const = 0; + + // Returns the current permission state of GetNetworks(). + virtual EnumerationPermission enumeration_permission() const; + + // "AnyAddressNetwork" is a network which only contains single "any address" + // IP address. (i.e. INADDR_ANY for IPv4 or in6addr_any for IPv6). This is + // useful as binding to such interfaces allow default routing behavior like + // http traffic. + // + // This method appends the "any address" networks to the list, such that this + // can optionally be called after GetNetworks. + virtual std::vector GetAnyAddressNetworks() = 0; + + // Dumps the current list of networks in the network manager. + virtual void DumpNetworks() {} + bool GetDefaultLocalAddress(int family, IPAddress* ipaddr) const override; + + struct Stats { + int ipv4_network_count; + int ipv6_network_count; + Stats() { + ipv4_network_count = 0; + ipv6_network_count = 0; + } + }; + + // MdnsResponderProvider interface. + MdnsResponderInterface* GetMdnsResponder() const override; + + virtual void set_vpn_list(const std::vector& /* vpn */) {} +}; + +// Represents a Unix-type network interface, with a name and single address. +class RTC_EXPORT Network { + public: + Network(absl::string_view name, + absl::string_view description, + const IPAddress& prefix, + int prefix_length) + : Network(name, + description, + prefix, + prefix_length, + webrtc::ADAPTER_TYPE_UNKNOWN) {} + + Network(absl::string_view name, + absl::string_view description, + const IPAddress& prefix, + int prefix_length, + AdapterType type); + + Network(const Network&); + ~Network(); + + // This signal is fired whenever type() or underlying_type_for_vpn() changes. + // Mutable, to support connecting on the const Network passed to webrtc::Port + // constructor. + mutable sigslot::signal1 SignalTypeChanged; + + // This signal is fired whenever network preference changes. + sigslot::signal1 SignalNetworkPreferenceChanged; + + const DefaultLocalAddressProvider* default_local_address_provider() const { + return default_local_address_provider_; + } + void set_default_local_address_provider( + const DefaultLocalAddressProvider* provider) { + default_local_address_provider_ = provider; + } + + void set_mdns_responder_provider(const MdnsResponderProvider* provider) { + mdns_responder_provider_ = provider; + } + + // Returns the name of the interface this network is associated with. + const std::string& name() const { return name_; } + + // Returns the OS-assigned name for this network. This is useful for + // debugging but should not be sent over the wire (for privacy reasons). + const std::string& description() const { return description_; } + + // Returns the prefix for this network. + const IPAddress& prefix() const { return prefix_; } + // Returns the length, in bits, of this network's prefix. + int prefix_length() const { return prefix_length_; } + + // Returns the family for the network prefix. + int family() const { return prefix_.family(); } + + // `key_` has unique value per network interface. Used in sorting network + // interfaces. Key is derived from interface name and it's prefix. + std::string key() const { return key_; } + + // Returns the Network's current idea of the 'best' IP it has. + // Or return an unset IP if this network has no active addresses. + // Here is the rule on how we mark the IPv6 address as ignorable for WebRTC. + // 1) return all global temporary dynamic and non-deprecated ones. + // 2) if #1 not available, return global ones. + // 3) if #2 not available, return local link ones. + // 4) if #3 not available, use ULA ipv6 as last resort. (ULA stands for + // unique local address, which is not route-able in open internet but might + // be useful for a close WebRTC deployment. + + // TODO(guoweis): rule #3 actually won't happen at current + // implementation. The reason being that ULA address starting with + // 0xfc 0r 0xfd will be grouped into its own Network. The result of + // that is WebRTC will have one extra Network to generate candidates + // but the lack of rule #3 shouldn't prevent turning on IPv6 since + // ULA should only be tried in a close deployment anyway. + + // Note that when not specifying any flag, it's treated as case global + // IPv6 address + IPAddress GetBestIP() const; + + // Adds an active IP address to this network. Does not check for duplicates. + void AddIP(const InterfaceAddress& ip) { ips_.push_back(ip); } + void AddIP(const IPAddress& ip) { ips_.push_back(InterfaceAddress(ip)); } + + // Sets the network's IP address list. Returns true if new IP addresses were + // detected. Passing true to already_changed skips this check. + bool SetIPs(const std::vector& ips, bool already_changed); + // Get the list of IP Addresses associated with this network. + const std::vector& GetIPs() const { return ips_; } + // Clear the network's list of addresses. + void ClearIPs() { ips_.clear(); } + // Returns the mDNS responder that can be used to obfuscate the local IP + // addresses of host candidates by mDNS names in ICE gathering. After a + // name-address mapping is created by the mDNS responder, queries for the + // created name will be resolved by the responder. + MdnsResponderInterface* GetMdnsResponder() const; + + // Returns the scope-id of the network's address. + // Should only be relevant for link-local IPv6 addresses. + int scope_id() const { return scope_id_; } + void set_scope_id(int id) { scope_id_ = id; } + + // Indicates whether this network should be ignored, perhaps because + // the IP is 0, or the interface is one we know is invalid. + bool ignored() const { return ignored_; } + void set_ignored(bool ignored) { ignored_ = ignored; } + + AdapterType type() const { return type_; } + // When type() is ADAPTER_TYPE_VPN, this returns the type of the underlying + // network interface used by the VPN, typically the preferred network type + // (see for example, the method setUnderlyingNetworks(android.net.Network[]) + // on https://developer.android.com/reference/android/net/VpnService.html). + // When this information is unavailable from the OS, ADAPTER_TYPE_UNKNOWN is + // returned. + AdapterType underlying_type_for_vpn() const { + return underlying_type_for_vpn_; + } + void set_type(AdapterType type) { + if (type_ == type) { + return; + } + type_ = type; + if (type != webrtc::ADAPTER_TYPE_VPN) { + underlying_type_for_vpn_ = webrtc::ADAPTER_TYPE_UNKNOWN; + } + SignalTypeChanged(this); + } + + void set_underlying_type_for_vpn(AdapterType type) { + if (underlying_type_for_vpn_ == type) { + return; + } + underlying_type_for_vpn_ = type; + SignalTypeChanged(this); + } + + bool IsVpn() const { return type_ == webrtc::ADAPTER_TYPE_VPN; } + + bool IsCellular() const { return IsCellular(type_); } + + static bool IsCellular(AdapterType type) { + switch (type) { + case webrtc::ADAPTER_TYPE_CELLULAR: + case webrtc::ADAPTER_TYPE_CELLULAR_2G: + case webrtc::ADAPTER_TYPE_CELLULAR_3G: + case webrtc::ADAPTER_TYPE_CELLULAR_4G: + case webrtc::ADAPTER_TYPE_CELLULAR_5G: + return true; + default: + return false; + } + } + + // Note: This function is called "rarely". + // Twice per Network in BasicPortAllocator if + // PORTALLOCATOR_DISABLE_COSTLY_NETWORKS. Once in Port::Construct() (and when + // Port::OnNetworkTypeChanged is called). + uint16_t GetCost(const FieldTrialsView& field_trials) const; + + // A unique id assigned by the network manager, which may be signaled + // to the remote side in the candidate. + uint16_t id() const { return id_; } + void set_id(uint16_t id) { id_ = id; } + + int preference() const { return preference_; } + void set_preference(int preference) { preference_ = preference; } + + // When we enumerate networks and find a previously-seen network is missing, + // we do not remove it (because it may be used elsewhere). Instead, we mark + // it inactive, so that we can detect network changes properly. + bool active() const { return active_; } + void set_active(bool active) { + if (active_ != active) { + active_ = active; + } + } + + // Property set by operating system/firmware that has information + // about connection strength to e.g WIFI router or CELL base towers. + NetworkPreference network_preference() const { return network_preference_; } + void set_network_preference(NetworkPreference val) { + if (network_preference_ == val) { + return; + } + network_preference_ = val; + SignalNetworkPreferenceChanged(this); + } + + static std::pair GuessAdapterFromNetworkCost( + int network_cost); + + // Debugging description of this network + std::string ToString() const; + + private: + const DefaultLocalAddressProvider* default_local_address_provider_ = nullptr; + const MdnsResponderProvider* mdns_responder_provider_ = nullptr; + std::string name_; + std::string description_; + IPAddress prefix_; + int prefix_length_; + std::string key_; + std::vector ips_; + int scope_id_; + bool ignored_; + AdapterType type_; + AdapterType underlying_type_for_vpn_ = webrtc::ADAPTER_TYPE_UNKNOWN; + int preference_; + bool active_ = true; + uint16_t id_ = 0; + NetworkPreference network_preference_ = NetworkPreference::NEUTRAL; + + friend class NetworkManager; +}; + +// Base class for NetworkManager implementations. +class RTC_EXPORT NetworkManagerBase : public NetworkManager { + public: + NetworkManagerBase(); + + std::vector GetNetworks() const override; + std::vector GetAnyAddressNetworks() override; + + EnumerationPermission enumeration_permission() const override; + + bool GetDefaultLocalAddress(int family, IPAddress* ipaddr) const override; + + // Check if MAC address in |bytes| is one of the pre-defined + // MAC addresses for know VPNs. + static bool IsVpnMacAddress(ArrayView address); + + protected: + // Updates `networks_` with the networks listed in `list`. If + // `networks_map_` already has a Network object for a network listed + // in the `list` then it is reused. Accept ownership of the Network + // objects in the `list`. `changed` will be set to true if there is + // any change in the network list. + void MergeNetworkList(std::vector> list, + bool* changed); + + // `stats` will be populated even if |*changed| is false. + void MergeNetworkList(std::vector> list, + bool* changed, + NetworkManager::Stats* stats); + + void set_enumeration_permission(EnumerationPermission state) { + enumeration_permission_ = state; + } + + void set_default_local_addresses(const IPAddress& ipv4, + const IPAddress& ipv6); + + Network* GetNetworkFromAddress(const IPAddress& ip) const; + + // To enable subclasses to get the networks list, without interfering with + // refactoring of the interface GetNetworks method. + const std::vector& GetNetworksInternal() const { return networks_; } + + std::unique_ptr CreateNetwork(absl::string_view name, + absl::string_view description, + const IPAddress& prefix, + int prefix_length, + AdapterType type) const; + + private: + friend class NetworkTest; + EnumerationPermission enumeration_permission_; + + std::vector networks_; + + std::map> networks_map_; + + std::unique_ptr ipv4_any_address_network_; + std::unique_ptr ipv6_any_address_network_; + + IPAddress default_local_ipv4_address_; + IPAddress default_local_ipv6_address_; + // We use 16 bits to save the bandwidth consumption when sending the network + // id over the Internet. It is OK that the 16-bit integer overflows to get a + // network id 0 because we only compare the network ids in the old and the new + // best connections in the transport channel. + uint16_t next_available_network_id_ = 1; +}; + +// Basic implementation of the NetworkManager interface that gets list +// of networks using OS APIs. +class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, + public NetworkBinderInterface, + public sigslot::has_slots<> { + public: + BasicNetworkManager( + const Environment& env, + SocketFactory* absl_nonnull socket_factory, + NetworkMonitorFactory* absl_nullable network_monitor_factory = nullptr); + + ~BasicNetworkManager() override; + + void StartUpdating() override; + void StopUpdating() override; + + void DumpNetworks() override; + + bool started() { return start_count_ > 0; } + + // Sets the network ignore list, which is empty by default. Any network on the + // ignore list will be filtered from network enumeration results. + // Should be called only before initialization. + void set_network_ignore_list(const std::vector& list) { + RTC_DCHECK(thread_ == nullptr); + network_ignore_list_ = list; + } + + // Set a list of manually configured VPN's. + void set_vpn_list(const std::vector& vpn) override; + + // Check if |prefix| is configured as VPN. + bool IsConfiguredVpn(IPAddress prefix, int prefix_length) const; + + // Bind a socket to interface that ip address belong to. + // Implementation look up interface name and calls + // BindSocketToNetwork on NetworkMonitor. + // The interface name is needed as e.g ipv4 over ipv6 addresses + // are not exposed using Android functions, but it is possible + // bind an ipv4 address to the interface. + NetworkBindingResult BindSocketToNetwork(int socket_fd, + const IPAddress& address) override; + + protected: +#if defined(WEBRTC_POSIX) + // Separated from CreateNetworks for tests. + void ConvertIfAddrs(ifaddrs* interfaces, + IfAddrsConverter* converter, + bool include_ignored, + std::vector>* networks) const + RTC_RUN_ON(thread_); + NetworkMonitorInterface::InterfaceInfo GetInterfaceInfo( + struct ifaddrs* cursor) const RTC_RUN_ON(thread_); +#endif // defined(WEBRTC_POSIX) + + // Creates a network object for each network available on the machine. + bool CreateNetworks(bool include_ignored, + std::vector>* networks) const + RTC_RUN_ON(thread_); + + // Determines if a network should be ignored. This should only be determined + // based on the network's property instead of any individual IP. + bool IsIgnoredNetwork(const Network& network) const RTC_RUN_ON(thread_); + + // This function connects a UDP socket to a public address and returns the + // local address associated it. Since it binds to the "any" address + // internally, it returns the default local address on a multi-homed endpoint. + IPAddress QueryDefaultLocalAddress(int family) const RTC_RUN_ON(thread_); + + private: + friend class NetworkTest; + + // Creates a network monitor and listens for network updates. + void StartNetworkMonitor() RTC_RUN_ON(thread_); + // Stops and removes the network monitor. + void StopNetworkMonitor() RTC_RUN_ON(thread_); + // Called when it receives updates from the network monitor. + void OnNetworksChanged(); + + // Updates the networks and reschedules the next update. + void UpdateNetworksContinually() RTC_RUN_ON(thread_); + // Only updates the networks; does not reschedule the next update. + void UpdateNetworksOnce() RTC_RUN_ON(thread_); + + const Environment env_; + Thread* thread_ = nullptr; + bool sent_first_update_ = true; + int start_count_ = 0; + std::vector network_ignore_list_; + NetworkMonitorFactory* absl_nullable const network_monitor_factory_; + SocketFactory* absl_nonnull const socket_factory_; + std::unique_ptr network_monitor_ + RTC_GUARDED_BY(thread_); + bool allow_mac_based_ipv6_ RTC_GUARDED_BY(thread_) = false; + bool bind_using_ifname_ RTC_GUARDED_BY(thread_) = false; + + std::vector vpn_; + scoped_refptr task_safety_flag_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::BasicNetworkManager; +using ::webrtc::DefaultLocalAddressProvider; +using ::webrtc::GetAdapterTypeFromName; +using ::webrtc::kDefaultNetworkIgnoreMask; +using ::webrtc::kPublicIPv4Host; +using ::webrtc::kPublicIPv6Host; +using ::webrtc::MakeNetworkKey; +using ::webrtc::MdnsResponderProvider; +using ::webrtc::Network; +using ::webrtc::NetworkManager; +using ::webrtc::NetworkManagerBase; +using ::webrtc::NetworkMask; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NETWORK_H_ diff --git a/pkg/apm/webrtc/rtc_base/network/ecn_marking.h b/pkg/apm/webrtc/rtc_base/network/ecn_marking.h new file mode 100644 index 00000000..d181eb7d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/network/ecn_marking.h @@ -0,0 +1,22 @@ +/* + * Copyright 2024 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_NETWORK_ECN_MARKING_H_ +#define RTC_BASE_NETWORK_ECN_MARKING_H_ + +// // TODO: bugs.webrtc.org/42225697 - delete this file. +#include "api/transport/ecn_marking.h" + +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::EcnMarking; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NETWORK_ECN_MARKING_H_ diff --git a/pkg/apm/webrtc/rtc_base/network/received_packet.h b/pkg/apm/webrtc/rtc_base/network/received_packet.h new file mode 100644 index 00000000..5de54cd1 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/network/received_packet.h @@ -0,0 +1,98 @@ +/* + * 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 RTC_BASE_NETWORK_RECEIVED_PACKET_H_ +#define RTC_BASE_NETWORK_RECEIVED_PACKET_H_ + +#include +#include +#include + +#include "api/array_view.h" +#include "api/transport/ecn_marking.h" +#include "api/units/timestamp.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// ReceivedPacket represent a received IP packet. +// It contains a payload and metadata. +// ReceivedPacket itself does not put constraints on what payload contains. For +// example it may contains STUN, SCTP, SRTP, RTP, RTCP.... etc. +class RTC_EXPORT ReceivedIpPacket { + public: + enum DecryptionInfo { + kNotDecrypted, // Payload has not yet been decrypted or encryption is not + // used. + kDtlsDecrypted, // Payload has been Dtls decrypted + kSrtpEncrypted // Payload is SRTP encrypted. + }; + + // Caller must keep memory pointed to by payload and address valid for the + // lifetime of this ReceivedPacket. + ReceivedIpPacket(ArrayView payload, + const webrtc::SocketAddress& source_address, + std::optional arrival_time = std::nullopt, + EcnMarking ecn = EcnMarking::kNotEct, + DecryptionInfo decryption = kNotDecrypted); + + ReceivedIpPacket CopyAndSet(DecryptionInfo decryption_info) const; + + // Address/port of the packet sender. + const webrtc::SocketAddress& source_address() const { + return source_address_; + } + ArrayView payload() const { return payload_; } + + // Timestamp when this packet was received. Not available on all socket + // implementations. + std::optional arrival_time() const { + return arrival_time_; + } + + // L4S Explicit Congestion Notification. + EcnMarking ecn() const { return ecn_; } + + const DecryptionInfo& decryption_info() const { return decryption_info_; } + + static ReceivedIpPacket CreateFromLegacy( + const char* data, + size_t size, + int64_t packet_time_us, + const webrtc::SocketAddress& addr = webrtc::SocketAddress()) { + return CreateFromLegacy(reinterpret_cast(data), size, + packet_time_us, addr); + } + + static ReceivedIpPacket CreateFromLegacy( + const uint8_t* data, + size_t size, + int64_t packet_time_us, + const webrtc::SocketAddress& = webrtc::SocketAddress()); + + private: + ArrayView payload_; + std::optional arrival_time_; + const webrtc::SocketAddress& source_address_; + EcnMarking ecn_; + DecryptionInfo decryption_info_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ReceivedPacket = ::webrtc::ReceivedIpPacket; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NETWORK_RECEIVED_PACKET_H_ diff --git a/pkg/apm/webrtc/rtc_base/network/sent_packet.h b/pkg/apm/webrtc/rtc_base/network/sent_packet.h new file mode 100644 index 00000000..805b442d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/network/sent_packet.h @@ -0,0 +1,84 @@ +/* + * 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 RTC_BASE_NETWORK_SENT_PACKET_H_ +#define RTC_BASE_NETWORK_SENT_PACKET_H_ + +#include +#include + +#include + +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +enum class PacketType { + kUnknown, + kData, + kIceConnectivityCheck, + kIceConnectivityCheckResponse, + kStunMessage, + kTurnMessage, +}; + +enum class PacketInfoProtocolType { + kUnknown, + kUdp, + kTcp, + kSsltcp, + kTls, +}; + +struct RTC_EXPORT PacketInfo { + PacketInfo(); + PacketInfo(const PacketInfo& info); + ~PacketInfo(); + + bool included_in_feedback = false; + bool included_in_allocation = false; + // `is_media` is true if this is an audio or video packet, excluding + // retransmissions. + bool is_media = false; + PacketType packet_type = PacketType::kUnknown; + PacketInfoProtocolType protocol = PacketInfoProtocolType::kUnknown; + // A unique id assigned by the network manager, and std::nullopt if not set. + std::optional network_id; + size_t packet_size_bytes = 0; + size_t turn_overhead_bytes = 0; + size_t ip_overhead_bytes = 0; +}; + +struct RTC_EXPORT SentPacketInfo { + SentPacketInfo(); + SentPacketInfo(int64_t packet_id, int64_t send_time_ms); + SentPacketInfo(int64_t packet_id, + int64_t send_time_ms, + const PacketInfo& info); + + int64_t packet_id = -1; + int64_t send_time_ms = -1; + PacketInfo info; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::PacketInfo; +using SentPacket = ::webrtc::SentPacketInfo; +using ::webrtc::PacketInfoProtocolType; +using ::webrtc::PacketType; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NETWORK_SENT_PACKET_H_ diff --git a/pkg/apm/webrtc/rtc_base/network_constants.h b/pkg/apm/webrtc/rtc_base/network_constants.h new file mode 100644 index 00000000..884141c4 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/network_constants.h @@ -0,0 +1,104 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NETWORK_CONSTANTS_H_ +#define RTC_BASE_NETWORK_CONSTANTS_H_ + +#include + +#include + +namespace webrtc { + +constexpr uint16_t kNetworkCostMax = 999; +constexpr uint16_t kNetworkCostCellular2G = 980; +constexpr uint16_t kNetworkCostCellular3G = 910; +constexpr uint16_t kNetworkCostCellular = 900; +constexpr uint16_t kNetworkCostCellular4G = 500; +constexpr uint16_t kNetworkCostCellular5G = 250; +constexpr uint16_t kNetworkCostUnknown = 50; +constexpr uint16_t kNetworkCostLow = 10; +constexpr uint16_t kNetworkCostMin = 0; + +// Add 1 to network cost of underlying network type +// so that e.g a "plain" WIFI is prefered over a VPN over WIFI +// everything else being equal. +constexpr uint16_t kNetworkCostVpn = 1; + +// alias +constexpr uint16_t kNetworkCostHigh = kNetworkCostCellular; + +enum AdapterType { + // This enum resembles the one in Chromium net::ConnectionType. + ADAPTER_TYPE_UNKNOWN = 0, + ADAPTER_TYPE_ETHERNET = 1 << 0, + ADAPTER_TYPE_WIFI = 1 << 1, + ADAPTER_TYPE_CELLULAR = 1 << 2, // This is CELLULAR of unknown type. + ADAPTER_TYPE_VPN = 1 << 3, + ADAPTER_TYPE_LOOPBACK = 1 << 4, + // ADAPTER_TYPE_ANY is used for a network, which only contains a single "any + // address" IP address (INADDR_ANY for IPv4 or in6addr_any for IPv6), and can + // use any/all network interfaces. Whereas ADAPTER_TYPE_UNKNOWN is used + // when the network uses a specific interface/IP, but its interface type can + // not be determined or not fit in this enum. + ADAPTER_TYPE_ANY = 1 << 5, + ADAPTER_TYPE_CELLULAR_2G = 1 << 6, + ADAPTER_TYPE_CELLULAR_3G = 1 << 7, + ADAPTER_TYPE_CELLULAR_4G = 1 << 8, + ADAPTER_TYPE_CELLULAR_5G = 1 << 9 +}; + +std::string AdapterTypeToString(AdapterType type); + +// Useful for testing! +constexpr AdapterType kAllAdapterTypes[] = { + ADAPTER_TYPE_UNKNOWN, ADAPTER_TYPE_ETHERNET, + ADAPTER_TYPE_WIFI, ADAPTER_TYPE_CELLULAR, + ADAPTER_TYPE_VPN, ADAPTER_TYPE_LOOPBACK, + ADAPTER_TYPE_ANY, ADAPTER_TYPE_CELLULAR_2G, + ADAPTER_TYPE_CELLULAR_3G, ADAPTER_TYPE_CELLULAR_4G, + ADAPTER_TYPE_CELLULAR_5G, +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::ADAPTER_TYPE_ANY; +using ::webrtc::ADAPTER_TYPE_CELLULAR; +using ::webrtc::ADAPTER_TYPE_CELLULAR_2G; +using ::webrtc::ADAPTER_TYPE_CELLULAR_3G; +using ::webrtc::ADAPTER_TYPE_CELLULAR_4G; +using ::webrtc::ADAPTER_TYPE_CELLULAR_5G; +using ::webrtc::ADAPTER_TYPE_ETHERNET; +using ::webrtc::ADAPTER_TYPE_LOOPBACK; +using ::webrtc::ADAPTER_TYPE_UNKNOWN; +using ::webrtc::ADAPTER_TYPE_VPN; +using ::webrtc::ADAPTER_TYPE_WIFI; +using ::webrtc::AdapterType; +using ::webrtc::AdapterTypeToString; +using ::webrtc::kAllAdapterTypes; +using ::webrtc::kNetworkCostCellular; +using ::webrtc::kNetworkCostCellular2G; +using ::webrtc::kNetworkCostCellular3G; +using ::webrtc::kNetworkCostCellular4G; +using ::webrtc::kNetworkCostCellular5G; +using ::webrtc::kNetworkCostHigh; +using ::webrtc::kNetworkCostLow; +using ::webrtc::kNetworkCostMax; +using ::webrtc::kNetworkCostMin; +using ::webrtc::kNetworkCostUnknown; +using ::webrtc::kNetworkCostVpn; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NETWORK_CONSTANTS_H_ diff --git a/pkg/apm/webrtc/rtc_base/network_monitor.h b/pkg/apm/webrtc/rtc_base/network_monitor.h new file mode 100644 index 00000000..f954a4db --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/network_monitor.h @@ -0,0 +1,150 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NETWORK_MONITOR_H_ +#define RTC_BASE_NETWORK_MONITOR_H_ + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/ip_address.h" +#include "rtc_base/network_constants.h" + +namespace webrtc { + +enum class NetworkBindingResult { + SUCCESS = 0, // No error + FAILURE = -1, // Generic error + NOT_IMPLEMENTED = -2, + ADDRESS_NOT_FOUND = -3, + NETWORK_CHANGED = -4 +}; + +// NetworkPreference property set by operating system/firmware that has +// information about connection strength to e.g WIFI router or CELL base towers. +// GENERATED_JAVA_ENUM_PACKAGE: org.webrtc +enum class NetworkPreference { + NEUTRAL = 0, + NOT_PREFERRED = -1, +}; + +const char* NetworkPreferenceToString(NetworkPreference preference); + +// This interface is set onto a socket server, +// where only the ip address is known at the time of binding. +class NetworkBinderInterface { + public: + // Binds a socket to the network that is attached to `address` so that all + // packets on the socket `socket_fd` will be sent via that network. + // This is needed because some operating systems (like Android) require a + // special bind call to put packets on a non-default network interface. + virtual NetworkBindingResult BindSocketToNetwork( + int socket_fd, + const IPAddress& address) = 0; + virtual ~NetworkBinderInterface() {} +}; + +/* + * Receives network-change events via `OnNetworksChanged` and signals the + * networks changed event. + * + * Threading consideration: + * It is expected that all upstream operations (from native to Java) are + * performed from the worker thread. This includes creating, starting and + * stopping the monitor. This avoids the potential race condition when creating + * the singleton Java NetworkMonitor class. Downstream operations can be from + * any thread, but this class will forward all the downstream operations onto + * the worker thread. + * + * Memory consideration: + * NetworkMonitor is owned by the caller (NetworkManager). The global network + * monitor factory is owned by the PeerConnectionFactory. + */ +// Generic network monitor interface. It starts and stops monitoring network +// changes, and fires the SignalNetworksChanged event when networks change. +class NetworkMonitorInterface { + public: + struct InterfaceInfo { + // The type of adapter if known. + AdapterType adapter_type; + + // Is ADAPTER_TYPE_UNKNOWN unless adapter_type == ADAPTER_TYPE_VPN. + AdapterType underlying_type_for_vpn = webrtc::ADAPTER_TYPE_UNKNOWN; + + // The OS/firmware specific preference of this interface. + NetworkPreference network_preference = NetworkPreference::NEUTRAL; + + // Is this interface available to use? WebRTC shouldn't attempt to use it if + // this returns false. + // + // It's possible for this status to change, in which case + // SignalNetworksChanged will be fired. + // + // The specific use case this was added for was a phone with two SIM + // cards, where attempting to use all interfaces returned from getifaddrs + // caused the connection to be dropped. + bool available = true; + }; + + NetworkMonitorInterface(); + virtual ~NetworkMonitorInterface(); + + virtual void Start() = 0; + virtual void Stop() = 0; + + // Get information about an interface. + // If the interface is not known, the return struct will have set + // `adapter_type` to ADAPTER_TYPE_UNKNOWN and `available` to false. + virtual InterfaceInfo GetInterfaceInfo(absl::string_view interface_name) = 0; + + // Does `this` NetworkMonitorInterface implement BindSocketToNetwork? + // Only Android returns true. + virtual bool SupportsBindSocketToNetwork() const { return false; } + + // Bind a socket to an interface specified by ip address and/or interface + // name. Only implemented on Android. + virtual NetworkBindingResult BindSocketToNetwork( + int /* socket_fd */, + const IPAddress& /* address */, + absl::string_view /* interface_name */) { + return NetworkBindingResult::NOT_IMPLEMENTED; + } + + void SetNetworksChangedCallback(std::function callback) { + networks_changed_callback_ = std::move(callback); + } + + protected: + void InvokeNetworksChangedCallback() { + if (networks_changed_callback_) { + networks_changed_callback_(); + } + } + + private: + std::function networks_changed_callback_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::NetworkBinderInterface; +using ::webrtc::NetworkBindingResult; +using ::webrtc::NetworkMonitorInterface; +using ::webrtc::NetworkPreference; +using ::webrtc::NetworkPreferenceToString; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NETWORK_MONITOR_H_ diff --git a/pkg/apm/webrtc/rtc_base/network_monitor_factory.h b/pkg/apm/webrtc/rtc_base/network_monitor_factory.h new file mode 100644 index 00000000..8cdcdb7b --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/network_monitor_factory.h @@ -0,0 +1,50 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NETWORK_MONITOR_FACTORY_H_ +#define RTC_BASE_NETWORK_MONITOR_FACTORY_H_ + +#include "rtc_base/network_monitor.h" +namespace webrtc { +class FieldTrialsView; +} // namespace webrtc + +namespace webrtc { + +// Forward declaring this so it's not part of the API surface; it's only +// expected to be used by Android/iOS SDK code. + +/* + * NetworkMonitorFactory creates NetworkMonitors. + * Note that CreateNetworkMonitor is expected to be called on the network + * thread with the returned object only being used on that thread thereafter. + */ +class NetworkMonitorFactory { + public: + virtual NetworkMonitorInterface* CreateNetworkMonitor( + const FieldTrialsView& field_trials) = 0; + + virtual ~NetworkMonitorFactory(); + + protected: + NetworkMonitorFactory(); +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::NetworkMonitorFactory; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NETWORK_MONITOR_FACTORY_H_ diff --git a/pkg/apm/webrtc/rtc_base/network_route.h b/pkg/apm/webrtc/rtc_base/network_route.h new file mode 100644 index 00000000..7f437cfb --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/network_route.h @@ -0,0 +1,105 @@ +/* + * Copyright 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. + */ + +#ifndef RTC_BASE_NETWORK_ROUTE_H_ +#define RTC_BASE_NETWORK_ROUTE_H_ + +#include + +#include + +#include "rtc_base/network_constants.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/system/inline.h" + +// TODO(honghaiz): Make a directory that describes the interfaces and structs +// the media code can rely on and the network code can implement, and both can +// depend on that, but not depend on each other. Then, move this file to that +// directory. +namespace webrtc { + +class RouteEndpoint { + public: + RouteEndpoint() {} // Used by tests. + RouteEndpoint(AdapterType adapter_type, + uint16_t adapter_id, + uint16_t network_id, + bool uses_turn) + : adapter_type_(adapter_type), + adapter_id_(adapter_id), + network_id_(network_id), + uses_turn_(uses_turn) {} + + RouteEndpoint(const RouteEndpoint&) = default; + RouteEndpoint& operator=(const RouteEndpoint&) = default; + + // Used by tests. + static RouteEndpoint CreateWithNetworkId(uint16_t network_id) { + return RouteEndpoint(webrtc::ADAPTER_TYPE_UNKNOWN, + /* adapter_id = */ 0, network_id, + /* uses_turn = */ false); + } + RouteEndpoint CreateWithTurn(bool uses_turn) const { + return RouteEndpoint(adapter_type_, adapter_id_, network_id_, uses_turn); + } + + AdapterType adapter_type() const { return adapter_type_; } + uint16_t adapter_id() const { return adapter_id_; } + uint16_t network_id() const { return network_id_; } + bool uses_turn() const { return uses_turn_; } + + bool operator==(const RouteEndpoint& other) const; + + private: + AdapterType adapter_type_ = webrtc::ADAPTER_TYPE_UNKNOWN; + uint16_t adapter_id_ = 0; + uint16_t network_id_ = 0; + bool uses_turn_ = false; +}; + +struct NetworkRoute { + bool connected = false; + RouteEndpoint local; + RouteEndpoint remote; + // Last packet id sent on the PREVIOUS route. + int last_sent_packet_id = -1; + // The overhead in bytes from IP layer and above. + // This is the maximum of any part of the route. + int packet_overhead = 0; + + RTC_NO_INLINE inline std::string DebugString() const { + StringBuilder oss; + oss << "[ connected: " << connected << " local: [ " << local.adapter_id() + << "/" << local.network_id() << " " + << webrtc::AdapterTypeToString(local.adapter_type()) + << " turn: " << local.uses_turn() << " ] remote: [ " + << remote.adapter_id() << "/" << remote.network_id() << " " + << webrtc::AdapterTypeToString(remote.adapter_type()) + << " turn: " << remote.uses_turn() + << " ] packet_overhead_bytes: " << packet_overhead << " ]"; + return oss.Release(); + } + + bool operator==(const NetworkRoute& other) const; + bool operator!=(const NetworkRoute& other) { return !operator==(other); } +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::NetworkRoute; +using ::webrtc::RouteEndpoint; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NETWORK_ROUTE_H_ diff --git a/pkg/apm/webrtc/rtc_base/null_socket_server.h b/pkg/apm/webrtc/rtc_base/null_socket_server.h new file mode 100644 index 00000000..747c6d01 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/null_socket_server.h @@ -0,0 +1,45 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NULL_SOCKET_SERVER_H_ +#define RTC_BASE_NULL_SOCKET_SERVER_H_ + +#include "rtc_base/event.h" +#include "rtc_base/socket.h" +#include "rtc_base/socket_server.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +class RTC_EXPORT NullSocketServer : public SocketServer { + public: + NullSocketServer(); + ~NullSocketServer() override; + + bool Wait(TimeDelta max_wait_duration, bool process_io) override; + void WakeUp() override; + + Socket* CreateSocket(int family, int type) override; + + private: + Event event_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::NullSocketServer; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NULL_SOCKET_SERVER_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/divide_round.h b/pkg/apm/webrtc/rtc_base/numerics/divide_round.h new file mode 100644 index 00000000..4143902b --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/divide_round.h @@ -0,0 +1,60 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NUMERICS_DIVIDE_ROUND_H_ +#define RTC_BASE_NUMERICS_DIVIDE_ROUND_H_ + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_compare.h" + +namespace webrtc { + +template +inline auto constexpr DivideRoundUp(Dividend dividend, Divisor divisor) { + static_assert(std::is_integral(), ""); + static_assert(std::is_integral(), ""); + RTC_DCHECK_GE(dividend, 0); + RTC_DCHECK_GT(divisor, 0); + + auto quotient = dividend / divisor; + auto remainder = dividend % divisor; + return quotient + (remainder > 0 ? 1 : 0); +} + +template +inline auto constexpr DivideRoundToNearest(Dividend dividend, Divisor divisor) { + static_assert(std::is_integral(), ""); + static_assert(std::is_integral(), ""); + RTC_DCHECK_GT(divisor, 0); + + if (dividend < Dividend{0}) { + auto half_of_divisor = divisor / 2; + auto quotient = dividend / divisor; + auto remainder = dividend % divisor; + if (SafeGt(-remainder, half_of_divisor)) { + --quotient; + } + return quotient; + } + + auto half_of_divisor = (divisor - 1) / 2; + auto quotient = dividend / divisor; + auto remainder = dividend % divisor; + if (SafeGt(remainder, half_of_divisor)) { + ++quotient; + } + return quotient; +} + +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_DIVIDE_ROUND_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/event_based_exponential_moving_average.cc b/pkg/apm/webrtc/rtc_base/numerics/event_based_exponential_moving_average.cc new file mode 100644 index 00000000..27b2066a --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/event_based_exponential_moving_average.cc @@ -0,0 +1,84 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/numerics/event_based_exponential_moving_average.h" + +#include +#include +#include + +#include "rtc_base/checks.h" + +namespace { + +// For a normal distributed value, the 95% double sided confidence interval is +// is 1.96 * stddev. +constexpr double ninetyfive_percent_confidence = 1.96; + +} // namespace + +namespace webrtc { + +// `half_time` specifies how much weight will be given to old samples, +// a sample gets exponentially less weight so that it's 50% +// after `half_time` time units has passed. +EventBasedExponentialMovingAverage::EventBasedExponentialMovingAverage( + int half_time) { + SetHalfTime(half_time); +} + +void EventBasedExponentialMovingAverage::SetHalfTime(int half_time) { + tau_ = static_cast(half_time) / log(2); + Reset(); +} + +void EventBasedExponentialMovingAverage::Reset() { + value_ = std::nan("uninit"); + sample_variance_ = std::numeric_limits::infinity(); + estimator_variance_ = 1; + last_observation_timestamp_.reset(); +} + +void EventBasedExponentialMovingAverage::AddSample(int64_t now, int sample) { + if (!last_observation_timestamp_.has_value()) { + value_ = sample; + } else { + // TODO(webrtc:11140): This should really be > (e.g not >=) + // but some pesky tests run with simulated clock and let + // samples arrive simultaneously! + RTC_DCHECK(now >= *last_observation_timestamp_); + // Variance gets computed after second sample. + int64_t age = now - *last_observation_timestamp_; + double e = exp(-age / tau_); + double alpha = e / (1 + e); + double one_minus_alpha = 1 - alpha; + double sample_diff = sample - value_; + value_ = one_minus_alpha * value_ + alpha * sample; + estimator_variance_ = + (one_minus_alpha * one_minus_alpha) * estimator_variance_ + + (alpha * alpha); + if (sample_variance_ == std::numeric_limits::infinity()) { + // First variance. + sample_variance_ = sample_diff * sample_diff; + } else { + double new_variance = one_minus_alpha * sample_variance_ + + alpha * sample_diff * sample_diff; + sample_variance_ = new_variance; + } + } + last_observation_timestamp_ = now; +} + +double EventBasedExponentialMovingAverage::GetConfidenceInterval() const { + return ninetyfive_percent_confidence * + sqrt(sample_variance_ * estimator_variance_); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/numerics/event_based_exponential_moving_average.h b/pkg/apm/webrtc/rtc_base/numerics/event_based_exponential_moving_average.h new file mode 100644 index 00000000..478263c2 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/event_based_exponential_moving_average.h @@ -0,0 +1,78 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NUMERICS_EVENT_BASED_EXPONENTIAL_MOVING_AVERAGE_H_ +#define RTC_BASE_NUMERICS_EVENT_BASED_EXPONENTIAL_MOVING_AVERAGE_H_ + +#include +#include +#include +#include + +namespace webrtc { + +/** + * This class implements exponential moving average for time series + * estimating both value, variance and variance of estimator based on + * https://en.wikipedia.org/w/index.php?title=Moving_average§ion=9#Application_to_measuring_computer_performance + * with the additions from nisse@ added to + * https://en.wikipedia.org/wiki/Talk:Moving_average. + * + * A sample gets exponentially less weight so that it's 50% + * after `half_time` time units. + */ +class EventBasedExponentialMovingAverage { + public: + // `half_time` specifies how much weight will be given to old samples, + // see example above. + explicit EventBasedExponentialMovingAverage(int half_time); + + void AddSample(int64_t now, int value); + + double GetAverage() const { return value_; } + double GetVariance() const { return sample_variance_; } + + // Compute 95% confidence interval assuming that + // - variance of samples are normal distributed. + // - variance of estimator is normal distributed. + // + // The returned values specifies the distance from the average, + // i.e if X = GetAverage(), m = GetConfidenceInterval() + // then a there is 95% likelihood that the observed variables is inside + // [ X +/- m ]. + double GetConfidenceInterval() const; + + // Reset + void Reset(); + + // Update the half_time. + // NOTE: resets estimate too. + void SetHalfTime(int half_time); + + private: + double tau_; + double value_ = std::nan("uninit"); + double sample_variance_ = std::numeric_limits::infinity(); + // This is the ratio between variance of the estimate and variance of samples. + double estimator_variance_ = 1; + std::optional last_observation_timestamp_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::EventBasedExponentialMovingAverage; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NUMERICS_EVENT_BASED_EXPONENTIAL_MOVING_AVERAGE_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/event_rate_counter.cc b/pkg/apm/webrtc/rtc_base/numerics/event_rate_counter.cc new file mode 100644 index 00000000..52c9de64 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/event_rate_counter.cc @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "rtc_base/numerics/event_rate_counter.h" + +#include +#include + +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" + +namespace webrtc { + +void EventRateCounter::AddEvent(Timestamp event_time) { + if (first_time_.IsFinite()) + interval_.AddSample(event_time - last_time_); + first_time_ = std::min(first_time_, event_time); + last_time_ = std::max(last_time_, event_time); + event_count_++; +} + +void EventRateCounter::AddEvents(EventRateCounter other) { + first_time_ = std::min(first_time_, other.first_time_); + last_time_ = std::max(last_time_, other.last_time_); + event_count_ += other.event_count_; + interval_.AddSamples(other.interval_); +} + +bool EventRateCounter::IsEmpty() const { + return first_time_ == last_time_; +} + +double EventRateCounter::Rate() const { + if (event_count_ == 0) + return 0; + if (event_count_ == 1) + return NAN; + return (event_count_ - 1) / (last_time_ - first_time_).seconds(); +} + +TimeDelta EventRateCounter::TotalDuration() const { + if (first_time_.IsInfinite()) { + return TimeDelta::Zero(); + } + return last_time_ - first_time_; +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/numerics/event_rate_counter.h b/pkg/apm/webrtc/rtc_base/numerics/event_rate_counter.h new file mode 100644 index 00000000..ef107ba5 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/event_rate_counter.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_NUMERICS_EVENT_RATE_COUNTER_H_ +#define RTC_BASE_NUMERICS_EVENT_RATE_COUNTER_H_ + +#include + +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "rtc_base/numerics/sample_stats.h" + +namespace webrtc { + +// Calculates statistics based on events. For example for computing frame rates. +// Note that it doesn't provide any running statistics or reset funcitonality, +// so it's mostly useful for end of call statistics. +class EventRateCounter { + public: + // Adds an event based on it's `event_time` for correct updates of the + // interval statistics, each event must be added past the previous events. + void AddEvent(Timestamp event_time); + // Adds the events from `other`. Note that the interval stats won't be + // recalculated, only merged, so this is not equivalent to if the events would + // have been added to the same counter from the start. + void AddEvents(EventRateCounter other); + bool IsEmpty() const; + // Average number of events per second. Defaults to 0 for no events and NAN + // for one event. + double Rate() const; + SampleStats& interval() { return interval_; } + TimeDelta TotalDuration() const; + int Count() const { return event_count_; } + + private: + Timestamp first_time_ = Timestamp::PlusInfinity(); + Timestamp last_time_ = Timestamp::MinusInfinity(); + int64_t event_count_ = 0; + SampleStats interval_; +}; +} // namespace webrtc +#endif // RTC_BASE_NUMERICS_EVENT_RATE_COUNTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/exp_filter.cc b/pkg/apm/webrtc/rtc_base/numerics/exp_filter.cc new file mode 100644 index 00000000..5dbca9bb --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/exp_filter.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/numerics/exp_filter.h" + +#include + +namespace webrtc { + +const float ExpFilter::kValueUndefined = -1.0f; + +void ExpFilter::Reset(float alpha) { + alpha_ = alpha; + filtered_ = kValueUndefined; +} + +float ExpFilter::Apply(float exp, float sample) { + if (filtered_ == kValueUndefined) { + // Initialize filtered value. + filtered_ = sample; + } else if (exp == 1.0) { + filtered_ = alpha_ * filtered_ + (1 - alpha_) * sample; + } else { + float alpha = std::pow(alpha_, exp); + filtered_ = alpha * filtered_ + (1 - alpha) * sample; + } + if (max_ != kValueUndefined && filtered_ > max_) { + filtered_ = max_; + } + return filtered_; +} + +void ExpFilter::UpdateBase(float alpha) { + alpha_ = alpha; +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/numerics/exp_filter.h b/pkg/apm/webrtc/rtc_base/numerics/exp_filter.h new file mode 100644 index 00000000..fe411c48 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/exp_filter.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NUMERICS_EXP_FILTER_H_ +#define RTC_BASE_NUMERICS_EXP_FILTER_H_ + +namespace webrtc { + +// This class can be used, for example, for smoothing the result of bandwidth +// estimation and packet loss estimation. + +class ExpFilter { + public: + static const float kValueUndefined; + + explicit ExpFilter(float alpha, float max = kValueUndefined) : max_(max) { + Reset(alpha); + } + + // Resets the filter to its initial state, and resets filter factor base to + // the given value `alpha`. + void Reset(float alpha); + + // Applies the filter with a given exponent on the provided sample: + // y(k) = min(alpha_^ exp * y(k-1) + (1 - alpha_^ exp) * sample, max_). + float Apply(float exp, float sample); + + // Returns current filtered value. + float filtered() const { return filtered_; } + + // Changes the filter factor base to the given value `alpha`. + void UpdateBase(float alpha); + + private: + float alpha_; // Filter factor base. + float filtered_; // Current filter output. + const float max_; +}; +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::ExpFilter; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NUMERICS_EXP_FILTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/histogram_percentile_counter.cc b/pkg/apm/webrtc/rtc_base/numerics/histogram_percentile_counter.cc new file mode 100644 index 00000000..d93d318a --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/histogram_percentile_counter.cc @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/numerics/histogram_percentile_counter.h" + +#include +#include +#include +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +HistogramPercentileCounter::HistogramPercentileCounter( + uint32_t long_tail_boundary) + : histogram_low_(size_t{long_tail_boundary}), + long_tail_boundary_(long_tail_boundary), + total_elements_(0), + total_elements_low_(0) {} + +HistogramPercentileCounter::~HistogramPercentileCounter() = default; + +void HistogramPercentileCounter::Add(const HistogramPercentileCounter& other) { + for (uint32_t value = 0; value < other.long_tail_boundary_; ++value) { + Add(value, other.histogram_low_[value]); + } + for (const auto& it : histogram_high_) { + Add(it.first, it.second); + } +} + +void HistogramPercentileCounter::Add(uint32_t value, size_t count) { + if (value < long_tail_boundary_) { + histogram_low_[value] += count; + total_elements_low_ += count; + } else { + histogram_high_[value] += count; + } + total_elements_ += count; +} + +void HistogramPercentileCounter::Add(uint32_t value) { + Add(value, 1); +} + +std::optional HistogramPercentileCounter::GetPercentile( + float fraction) { + RTC_CHECK_LE(fraction, 1.0); + RTC_CHECK_GE(fraction, 0.0); + if (total_elements_ == 0) + return std::nullopt; + size_t elements_to_skip = static_cast( + std::max(0.0f, std::ceil(total_elements_ * fraction) - 1)); + if (elements_to_skip >= total_elements_) + elements_to_skip = total_elements_ - 1; + if (elements_to_skip < total_elements_low_) { + for (uint32_t value = 0; value < long_tail_boundary_; ++value) { + if (elements_to_skip < histogram_low_[value]) + return value; + elements_to_skip -= histogram_low_[value]; + } + } else { + elements_to_skip -= total_elements_low_; + for (const auto& it : histogram_high_) { + if (elements_to_skip < it.second) + return it.first; + elements_to_skip -= it.second; + } + } + RTC_DCHECK_NOTREACHED(); + return std::nullopt; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/numerics/histogram_percentile_counter.h b/pkg/apm/webrtc/rtc_base/numerics/histogram_percentile_counter.h new file mode 100644 index 00000000..f1cb02e3 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/histogram_percentile_counter.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NUMERICS_HISTOGRAM_PERCENTILE_COUNTER_H_ +#define RTC_BASE_NUMERICS_HISTOGRAM_PERCENTILE_COUNTER_H_ + +#include +#include + +#include +#include +#include + +namespace webrtc { +// Calculates percentiles on the stream of data. Use `Add` methods to add new +// values. Use `GetPercentile` to get percentile of the currently added values. +class HistogramPercentileCounter { + public: + // Values below `long_tail_boundary` are stored as the histogram in an array. + // Values above - in a map. + explicit HistogramPercentileCounter(uint32_t long_tail_boundary); + ~HistogramPercentileCounter(); + void Add(uint32_t value); + void Add(uint32_t value, size_t count); + void Add(const HistogramPercentileCounter& other); + // Argument should be from 0 to 1. + std::optional GetPercentile(float fraction); + + private: + std::vector histogram_low_; + std::map histogram_high_; + const uint32_t long_tail_boundary_; + size_t total_elements_; + size_t total_elements_low_; +}; +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::HistogramPercentileCounter; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES +#endif // RTC_BASE_NUMERICS_HISTOGRAM_PERCENTILE_COUNTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/math_utils.h b/pkg/apm/webrtc/rtc_base/numerics/math_utils.h new file mode 100644 index 00000000..5482cec6 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/math_utils.h @@ -0,0 +1,75 @@ +/* + * Copyright 2005 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can 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_NUMERICS_MATH_UTILS_H_ +#define API_NUMERICS_MATH_UTILS_H_ + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace webrtc_impl { +// Given two numbers `x` and `y` such that x >= y, computes the difference +// x - y without causing undefined behavior due to signed overflow. +template +typename std::make_unsigned::type unsigned_difference(T x, T y) { + static_assert( + std::is_signed::value, + "Function unsigned_difference is only meaningful for signed types."); + RTC_DCHECK_GE(x, y); + typedef typename std::make_unsigned::type unsigned_type; + // int -> unsigned conversion repeatedly adds UINT_MAX + 1 until the number + // can be represented as an unsigned. Since we know that the actual + // difference x - y can be represented as an unsigned, it is sufficient to + // compute the difference modulo UINT_MAX + 1, i.e using unsigned arithmetic. + return static_cast(x) - static_cast(y); +} + +// Provide neutral element with respect to min(). +// Typically used as an initial value for running minimum. +template ::has_infinity>::type* = + nullptr> +constexpr T infinity_or_max() { + return std::numeric_limits::infinity(); +} + +template ::has_infinity>::type* = nullptr> +constexpr T infinity_or_max() { + // Fallback to max(). + return std::numeric_limits::max(); +} + +// Provide neutral element with respect to max(). +// Typically used as an initial value for running maximum. +template ::has_infinity>::type* = + nullptr> +constexpr T minus_infinity_or_min() { + static_assert(std::is_signed::value, "Unsupported. Please open a bug."); + return -std::numeric_limits::infinity(); +} + +template ::has_infinity>::type* = nullptr> +constexpr T minus_infinity_or_min() { + // Fallback to min(). + return std::numeric_limits::min(); +} + +} // namespace webrtc_impl +} // namespace webrtc + +#endif // API_NUMERICS_MATH_UTILS_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/mod_ops.h b/pkg/apm/webrtc/rtc_base/numerics/mod_ops.h new file mode 100644 index 00000000..65618b48 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/mod_ops.h @@ -0,0 +1,142 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_NUMERICS_MOD_OPS_H_ +#define RTC_BASE_NUMERICS_MOD_OPS_H_ + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +template // NOLINT +inline unsigned long Add(unsigned long a, unsigned long b) { // NOLINT + RTC_DCHECK_LT(a, M); + unsigned long t = M - b % M; // NOLINT + unsigned long res = a - t; // NOLINT + if (t > a) + return res + M; + return res; +} + +template // NOLINT +inline unsigned long Subtract(unsigned long a, unsigned long b) { // NOLINT + RTC_DCHECK_LT(a, M); + unsigned long sub = b % M; // NOLINT + if (a < sub) + return M - (sub - a); + return a - sub; +} + +// Calculates the forward difference between two wrapping numbers. +// +// Example: +// uint8_t x = 253; +// uint8_t y = 2; +// +// ForwardDiff(x, y) == 5 +// +// 252 253 254 255 0 1 2 3 +// ################################################# +// | | x | | | | | y | | +// ################################################# +// |----->----->----->----->-----> +// +// ForwardDiff(y, x) == 251 +// +// 252 253 254 255 0 1 2 3 +// ################################################# +// | | x | | | | | y | | +// ################################################# +// -->-----> |----->--- +// +// If M > 0 then wrapping occurs at M, if M == 0 then wrapping occurs at the +// largest value representable by T. +template +inline typename std::enable_if<(M > 0), T>::type ForwardDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + RTC_DCHECK_LT(a, M); + RTC_DCHECK_LT(b, M); + return a <= b ? b - a : M - (a - b); +} + +template +inline typename std::enable_if<(M == 0), T>::type ForwardDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return b - a; +} + +template +inline T ForwardDiff(T a, T b) { + return ForwardDiff(a, b); +} + +// Calculates the reverse difference between two wrapping numbers. +// +// Example: +// uint8_t x = 253; +// uint8_t y = 2; +// +// ReverseDiff(y, x) == 5 +// +// 252 253 254 255 0 1 2 3 +// ################################################# +// | | x | | | | | y | | +// ################################################# +// <-----<-----<-----<-----<-----| +// +// ReverseDiff(x, y) == 251 +// +// 252 253 254 255 0 1 2 3 +// ################################################# +// | | x | | | | | y | | +// ################################################# +// ---<-----| |<-----<-- +// +// If M > 0 then wrapping occurs at M, if M == 0 then wrapping occurs at the +// largest value representable by T. +template +inline typename std::enable_if<(M > 0), T>::type ReverseDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + RTC_DCHECK_LT(a, M); + RTC_DCHECK_LT(b, M); + return b <= a ? a - b : M - (b - a); +} + +template +inline typename std::enable_if<(M == 0), T>::type ReverseDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return a - b; +} + +template +inline T ReverseDiff(T a, T b) { + return ReverseDiff(a, b); +} + +// Calculates the minimum distance between to wrapping numbers. +// +// The minimum distance is defined as min(ForwardDiff(a, b), ReverseDiff(a, b)) +template +inline T MinDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return std::min(ForwardDiff(a, b), ReverseDiff(a, b)); +} + +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_MOD_OPS_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/moving_average.cc b/pkg/apm/webrtc/rtc_base/numerics/moving_average.cc new file mode 100644 index 00000000..17c5d9cd --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/moving_average.cc @@ -0,0 +1,63 @@ +/* + * 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 "rtc_base/numerics/moving_average.h" + +#include +#include +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +MovingAverage::MovingAverage(size_t window_size) : history_(window_size, 0) { + // Limit window size to avoid overflow. + RTC_DCHECK_LE(window_size, (int64_t{1} << 32) - 1); +} +MovingAverage::~MovingAverage() = default; + +void MovingAverage::AddSample(int sample) { + count_++; + size_t index = count_ % history_.size(); + if (count_ > history_.size()) + sum_ -= history_[index]; + sum_ += sample; + history_[index] = sample; +} + +std::optional MovingAverage::GetAverageRoundedDown() const { + if (count_ == 0) + return std::nullopt; + return sum_ / Size(); +} + +std::optional MovingAverage::GetAverageRoundedToClosest() const { + if (count_ == 0) + return std::nullopt; + return (sum_ + Size() / 2) / Size(); +} + +std::optional MovingAverage::GetUnroundedAverage() const { + if (count_ == 0) + return std::nullopt; + return sum_ / static_cast(Size()); +} + +void MovingAverage::Reset() { + count_ = 0; + sum_ = 0; +} + +size_t MovingAverage::Size() const { + return std::min(count_, history_.size()); +} +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/numerics/moving_average.h b/pkg/apm/webrtc/rtc_base/numerics/moving_average.h new file mode 100644 index 00000000..6011d0c2 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/moving_average.h @@ -0,0 +1,73 @@ +/* + * 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 RTC_BASE_NUMERICS_MOVING_AVERAGE_H_ +#define RTC_BASE_NUMERICS_MOVING_AVERAGE_H_ + +#include +#include + +#include +#include + +namespace webrtc { + +// Calculates average over fixed size window. If there are less than window +// size elements, calculates average of all inserted so far elements. +// +class MovingAverage { + public: + // Maximum supported window size is 2^32 - 1. + explicit MovingAverage(size_t window_size); + ~MovingAverage(); + // MovingAverage is neither copyable nor movable. + MovingAverage(const MovingAverage&) = delete; + MovingAverage& operator=(const MovingAverage&) = delete; + + // Adds new sample. If the window is full, the oldest element is pushed out. + void AddSample(int sample); + + // Returns rounded down average of last `window_size` elements or all + // elements if there are not enough of them. Returns nullopt if there were + // no elements added. + std::optional GetAverageRoundedDown() const; + + // Same as above but rounded to the closest integer. + std::optional GetAverageRoundedToClosest() const; + + // Returns unrounded average over the window. + std::optional GetUnroundedAverage() const; + + // Resets to the initial state before any elements were added. + void Reset(); + + // Returns number of elements in the window. + size_t Size() const; + + private: + // Total number of samples added to the class since last reset. + size_t count_ = 0; + // Sum of the samples in the moving window. + int64_t sum_ = 0; + // Circular buffer for all the samples in the moving window. + // Size is always `window_size` + std::vector history_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::MovingAverage; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES +#endif // RTC_BASE_NUMERICS_MOVING_AVERAGE_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/moving_max_counter.h b/pkg/apm/webrtc/rtc_base/numerics/moving_max_counter.h new file mode 100644 index 00000000..88557a19 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/moving_max_counter.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NUMERICS_MOVING_MAX_COUNTER_H_ +#define RTC_BASE_NUMERICS_MOVING_MAX_COUNTER_H_ + +#include + +#include +#include +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +// Implements moving max: can add samples to it and calculate maximum over some +// fixed moving window. +// +// Window size is configured at constructor. +// Samples can be added with `Add()` and max over current window is returned by +// `MovingMax`. `current_time_ms` in successive calls to Add and MovingMax +// should never decrease as if it's a wallclock time. +template +class MovingMaxCounter { + public: + explicit MovingMaxCounter(int64_t window_length_ms); + + MovingMaxCounter(const MovingMaxCounter&) = delete; + MovingMaxCounter& operator=(const MovingMaxCounter&) = delete; + + // Advances the current time, and adds a new sample. The new current time must + // be at least as large as the old current time. + void Add(const T& sample, int64_t current_time_ms); + // Advances the current time, and returns the maximum sample in the time + // window ending at the current time. The new current time must be at least as + // large as the old current time. + std::optional Max(int64_t current_time_ms); + void Reset(); + + private: + // Throws out obsolete samples. + void RollWindow(int64_t new_time_ms); + const int64_t window_length_ms_; + // This deque stores (timestamp, sample) pairs in chronological order; new + // pairs are only ever added at the end. However, because they can't affect + // the Max() calculation, pairs older than window_length_ms_ are discarded, + // and if an older pair has a sample that's smaller than that of a younger + // pair, the older pair is discarded. As a result, the sequence of timestamps + // is strictly increasing, and the sequence of samples is strictly decreasing. + std::deque> samples_; +#if RTC_DCHECK_IS_ON + int64_t last_call_time_ms_ = std::numeric_limits::min(); +#endif +}; + +template +MovingMaxCounter::MovingMaxCounter(int64_t window_length_ms) + : window_length_ms_(window_length_ms) {} + +template +void MovingMaxCounter::Add(const T& sample, int64_t current_time_ms) { + RollWindow(current_time_ms); + // Remove samples that will never be maximum in any window: newly added sample + // will always be in all windows the previous samples are. Thus, smaller or + // equal samples could be removed. This will maintain the invariant - deque + // contains strictly decreasing sequence of values. + while (!samples_.empty() && samples_.back().second <= sample) { + samples_.pop_back(); + } + // Add the new sample but only if there's no existing sample at the same time. + // Due to checks above, the already existing element will be larger, so the + // new sample will never be the maximum in any window. + if (samples_.empty() || samples_.back().first < current_time_ms) { + samples_.emplace_back(std::make_pair(current_time_ms, sample)); + } +} + +template +std::optional MovingMaxCounter::Max(int64_t current_time_ms) { + RollWindow(current_time_ms); + std::optional res; + if (!samples_.empty()) { + res.emplace(samples_.front().second); + } + return res; +} + +template +void MovingMaxCounter::Reset() { + samples_.clear(); +} + +template +void MovingMaxCounter::RollWindow(int64_t new_time_ms) { +#if RTC_DCHECK_IS_ON + RTC_DCHECK_GE(new_time_ms, last_call_time_ms_); + last_call_time_ms_ = new_time_ms; +#endif + const int64_t window_begin_ms = new_time_ms - window_length_ms_; + auto it = samples_.begin(); + while (it != samples_.end() && it->first < window_begin_ms) { + ++it; + } + samples_.erase(samples_.begin(), it); +} + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::MovingMaxCounter; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NUMERICS_MOVING_MAX_COUNTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/moving_percentile_filter.h b/pkg/apm/webrtc/rtc_base/numerics/moving_percentile_filter.h new file mode 100644 index 00000000..d68814a2 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/moving_percentile_filter.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NUMERICS_MOVING_PERCENTILE_FILTER_H_ +#define RTC_BASE_NUMERICS_MOVING_PERCENTILE_FILTER_H_ + +#include + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/percentile_filter.h" + +namespace webrtc { + +// Class to efficiently get moving percentile filter from a stream of samples. +template +class MovingPercentileFilter { + public: + // Construct filter. `percentile` defines what percentile to track and + // `window_size` is how many latest samples are stored for finding the + // percentile. `percentile` must be between 0.0 and 1.0 (inclusive) and + // `window_size` must be greater than 0. + MovingPercentileFilter(float percentile, size_t window_size); + + MovingPercentileFilter(const MovingPercentileFilter&) = delete; + MovingPercentileFilter& operator=(const MovingPercentileFilter&) = delete; + + // Insert a new sample. + void Insert(const T& value); + + // Removes all samples; + void Reset(); + + // Get percentile over the latest window. + T GetFilteredValue() const; + + // The number of samples that are currently stored. + size_t GetNumberOfSamplesStored() const; + + private: + PercentileFilter percentile_filter_; + std::list samples_; + size_t samples_stored_; + const size_t window_size_; +}; + +// Convenience type for the common median case. +template +class MovingMedianFilter : public MovingPercentileFilter { + public: + explicit MovingMedianFilter(size_t window_size) + : MovingPercentileFilter(0.5f, window_size) {} +}; + +template +MovingPercentileFilter::MovingPercentileFilter(float percentile, + size_t window_size) + : percentile_filter_(percentile), + samples_stored_(0), + window_size_(window_size) { + RTC_CHECK_GT(window_size, 0); +} + +template +void MovingPercentileFilter::Insert(const T& value) { + percentile_filter_.Insert(value); + samples_.emplace_back(value); + ++samples_stored_; + if (samples_stored_ > window_size_) { + percentile_filter_.Erase(samples_.front()); + samples_.pop_front(); + --samples_stored_; + } +} + +template +T MovingPercentileFilter::GetFilteredValue() const { + return percentile_filter_.GetPercentileValue(); +} + +template +void MovingPercentileFilter::Reset() { + percentile_filter_.Reset(); + samples_.clear(); + samples_stored_ = 0; +} + +template +size_t MovingPercentileFilter::GetNumberOfSamplesStored() const { + return samples_stored_; +} + +} // namespace webrtc +#endif // RTC_BASE_NUMERICS_MOVING_PERCENTILE_FILTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/numerics.go b/pkg/apm/webrtc/rtc_base/numerics/numerics.go new file mode 100644 index 00000000..e61a6f66 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/numerics.go @@ -0,0 +1,10 @@ +//go:build console + +package numerics + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/rtc_base/numerics/percentile_filter.h b/pkg/apm/webrtc/rtc_base/numerics/percentile_filter.h new file mode 100644 index 00000000..86b5fe10 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/percentile_filter.h @@ -0,0 +1,124 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_NUMERICS_PERCENTILE_FILTER_H_ +#define RTC_BASE_NUMERICS_PERCENTILE_FILTER_H_ + +#include + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +// Class to efficiently get the percentile value from a group of observations. +// The percentile is the value below which a given percentage of the +// observations fall. +template +class PercentileFilter { + public: + // Construct filter. `percentile` should be between 0 and 1. + explicit PercentileFilter(float percentile); + + // Insert one observation. The complexity of this operation is logarithmic in + // the size of the container. + void Insert(const T& value); + + // Remove one observation or return false if `value` doesn't exist in the + // container. The complexity of this operation is logarithmic in the size of + // the container. + bool Erase(const T& value); + + // Get the percentile value. The complexity of this operation is constant. + T GetPercentileValue() const; + + // Removes all the stored observations. + void Reset(); + + private: + // Update iterator and index to point at target percentile value. + void UpdatePercentileIterator(); + + const float percentile_; + std::multiset set_; + // Maintain iterator and index of current target percentile value. + typename std::multiset::iterator percentile_it_; + int64_t percentile_index_; +}; + +template +PercentileFilter::PercentileFilter(float percentile) + : percentile_(percentile), + percentile_it_(set_.begin()), + percentile_index_(0) { + RTC_CHECK_GE(percentile, 0.0f); + RTC_CHECK_LE(percentile, 1.0f); +} + +template +void PercentileFilter::Insert(const T& value) { + // Insert element at the upper bound. + set_.insert(value); + if (set_.size() == 1u) { + // First element inserted - initialize percentile iterator and index. + percentile_it_ = set_.begin(); + percentile_index_ = 0; + } else if (value < *percentile_it_) { + // If new element is before us, increment `percentile_index_`. + ++percentile_index_; + } + UpdatePercentileIterator(); +} + +template +bool PercentileFilter::Erase(const T& value) { + typename std::multiset::const_iterator it = set_.lower_bound(value); + // Ignore erase operation if the element is not present in the current set. + if (it == set_.end() || *it != value) + return false; + if (it == percentile_it_) { + // If same iterator, update to the following element. Index is not + // affected. + percentile_it_ = set_.erase(it); + } else { + set_.erase(it); + // If erased element was before us, decrement `percentile_index_`. + if (value <= *percentile_it_) + --percentile_index_; + } + UpdatePercentileIterator(); + return true; +} + +template +void PercentileFilter::UpdatePercentileIterator() { + if (set_.empty()) + return; + const int64_t index = static_cast(percentile_ * (set_.size() - 1)); + std::advance(percentile_it_, index - percentile_index_); + percentile_index_ = index; +} + +template +T PercentileFilter::GetPercentileValue() const { + return set_.empty() ? T() : *percentile_it_; +} + +template +void PercentileFilter::Reset() { + set_.clear(); + percentile_it_ = set_.begin(); + percentile_index_ = 0; +} +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_PERCENTILE_FILTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/rational.h b/pkg/apm/webrtc/rtc_base/numerics/rational.h new file mode 100644 index 00000000..32f0cb15 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/rational.h @@ -0,0 +1,28 @@ +/* + * Copyright 2024 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NUMERICS_RATIONAL_H_ +#define RTC_BASE_NUMERICS_RATIONAL_H_ + +namespace webrtc { + +// This is the worst implementation of a rational... +struct Rational { + int numerator; + int denominator; + + bool operator==(const Rational& other) const { + return numerator == other.numerator && denominator == other.denominator; + } +}; + +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_RATIONAL_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/running_statistics.h b/pkg/apm/webrtc/rtc_base/numerics/running_statistics.h new file mode 100644 index 00000000..c747c793 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/running_statistics.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_NUMERICS_RUNNING_STATISTICS_H_ +#define API_NUMERICS_RUNNING_STATISTICS_H_ + +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/math_utils.h" + +namespace webrtc { +namespace webrtc_impl { + +// tl;dr: Robust and efficient online computation of statistics, +// using Welford's method for variance. [1] +// +// This should be your go-to class if you ever need to compute +// min, max, mean, variance and standard deviation. +// If you need to get percentiles, please use webrtc::SamplesStatsCounter. +// +// Please note RemoveSample() won't affect min and max. +// If you want a full-fledged moving window over N last samples, +// please use webrtc::RollingAccumulator. +// +// The measures return std::nullopt if no samples were fed (Size() == 0), +// otherwise the returned optional is guaranteed to contain a value. +// +// [1] +// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm + +// The type T is a scalar which must be convertible to double. +// Rationale: we often need greater precision for measures +// than for the samples themselves. +template +class RunningStatistics { + public: + // Update stats //////////////////////////////////////////// + + // Add a value participating in the statistics in O(1) time. + void AddSample(T sample) { + max_ = std::max(max_, sample); + min_ = std::min(min_, sample); + sum_ += sample; + ++size_; + // Welford's incremental update. + const double delta = sample - mean_; + mean_ += delta / size_; + const double delta2 = sample - mean_; + cumul_ += delta * delta2; + } + + // Remove a previously added value in O(1) time. + // Nb: This doesn't affect min or max. + // Calling RemoveSample when Size()==0 is incorrect. + void RemoveSample(T sample) { + RTC_DCHECK_GT(Size(), 0); + // In production, just saturate at 0. + if (Size() == 0) { + return; + } + // Since samples order doesn't matter, this is the + // exact reciprocal of Welford's incremental update. + --size_; + const double delta = sample - mean_; + mean_ -= delta / size_; + const double delta2 = sample - mean_; + cumul_ -= delta * delta2; + } + + // Merge other stats, as if samples were added one by one, but in O(1). + void MergeStatistics(const RunningStatistics& other) { + if (other.size_ == 0) { + return; + } + max_ = std::max(max_, other.max_); + min_ = std::min(min_, other.min_); + const int64_t new_size = size_ + other.size_; + const double new_mean = + (mean_ * size_ + other.mean_ * other.size_) / new_size; + // Each cumulant must be corrected. + // * from: sum((x_i - mean_)²) + // * to: sum((x_i - new_mean)²) + auto delta = [new_mean](const RunningStatistics& stats) { + return stats.size_ * (new_mean * (new_mean - 2 * stats.mean_) + + stats.mean_ * stats.mean_); + }; + cumul_ = cumul_ + delta(*this) + other.cumul_ + delta(other); + mean_ = new_mean; + size_ = new_size; + } + + // Get Measures //////////////////////////////////////////// + + // Returns number of samples involved via AddSample() or MergeStatistics(), + // minus number of times RemoveSample() was called. + int64_t Size() const { return size_; } + + // Returns minimum among all seen samples, in O(1) time. + // This isn't affected by RemoveSample(). + std::optional GetMin() const { + if (size_ == 0) { + return std::nullopt; + } + return min_; + } + + // Returns maximum among all seen samples, in O(1) time. + // This isn't affected by RemoveSample(). + std::optional GetMax() const { + if (size_ == 0) { + return std::nullopt; + } + return max_; + } + + // Returns sum in O(1) time. + std::optional GetSum() const { + if (size_ == 0) { + return std::nullopt; + } + return sum_; + } + + // Returns mean in O(1) time. + std::optional GetMean() const { + if (size_ == 0) { + return std::nullopt; + } + return mean_; + } + + // Returns unbiased sample variance in O(1) time. + std::optional GetVariance() const { + if (size_ == 0) { + return std::nullopt; + } + return cumul_ / size_; + } + + // Returns unbiased standard deviation in O(1) time. + std::optional GetStandardDeviation() const { + if (size_ == 0) { + return std::nullopt; + } + return std::sqrt(*GetVariance()); + } + + private: + int64_t size_ = 0; // Samples seen. + T min_ = infinity_or_max(); + T max_ = minus_infinity_or_min(); + double mean_ = 0; + double cumul_ = 0; // Variance * size_, sometimes noted m2. + double sum_ = 0; +}; + +} // namespace webrtc_impl +} // namespace webrtc + +#endif // API_NUMERICS_RUNNING_STATISTICS_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/safe_compare.h b/pkg/apm/webrtc/rtc_base/numerics/safe_compare.h new file mode 100644 index 00000000..ef8d384d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/safe_compare.h @@ -0,0 +1,188 @@ +/* + * Copyright 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. + */ + +// This file defines six constexpr functions: +// +// webrtc::SafeEq // == +// webrtc::SafeNe // != +// webrtc::SafeLt // < +// webrtc::SafeLe // <= +// webrtc::SafeGt // > +// webrtc::SafeGe // >= +// +// They each accept two arguments of arbitrary types, and in almost all cases, +// they simply call the appropriate comparison operator. However, if both +// arguments are integers, they don't compare them using C++'s quirky rules, +// but instead adhere to the true mathematical definitions. It is as if the +// arguments were first converted to infinite-range signed integers, and then +// compared, although of course nothing expensive like that actually takes +// place. In practice, for signed/signed and unsigned/unsigned comparisons and +// some mixed-signed comparisons with a compile-time constant, the overhead is +// zero; in the remaining cases, it is just a few machine instructions (no +// branches). + +#ifndef RTC_BASE_NUMERICS_SAFE_COMPARE_H_ +#define RTC_BASE_NUMERICS_SAFE_COMPARE_H_ + +#include +#include + +#include + +#include "rtc_base/type_traits.h" + +namespace webrtc { + +namespace safe_cmp_impl { + +template +struct LargerIntImpl : std::false_type {}; +template <> +struct LargerIntImpl : std::true_type { + using type = int16_t; +}; +template <> +struct LargerIntImpl : std::true_type { + using type = int32_t; +}; +template <> +struct LargerIntImpl : std::true_type { + using type = int64_t; +}; + +// LargerInt::value is true iff there's a signed type that's larger +// than T1 (and no larger than the larger of T2 and int*, for performance +// reasons); and if there is such a type, LargerInt::type is an alias +// for it. +template +struct LargerInt + : LargerIntImpl {}; + +template +constexpr typename std::make_unsigned::type MakeUnsigned(T a) { + return static_cast::type>(a); +} + +// Overload for when both T1 and T2 have the same signedness. +template ::value == + std::is_signed::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return Op::Op(a, b); +} + +// Overload for signed - unsigned comparison that can be promoted to a bigger +// signed type. +template ::value && + std::is_unsigned::value && + LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return Op::Op(a, static_cast::type>(b)); +} + +// Overload for unsigned - signed comparison that can be promoted to a bigger +// signed type. +template ::value && + std::is_signed::value && + LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return Op::Op(static_cast::type>(a), b); +} + +// Overload for signed - unsigned comparison that can't be promoted to a bigger +// signed type. +template ::value && + std::is_unsigned::value && + !LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return a < 0 ? Op::Op(-1, 0) : Op::Op(safe_cmp_impl::MakeUnsigned(a), b); +} + +// Overload for unsigned - signed comparison that can't be promoted to a bigger +// signed type. +template ::value && + std::is_signed::value && + !LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return b < 0 ? Op::Op(0, -1) : Op::Op(a, safe_cmp_impl::MakeUnsigned(b)); +} + +#define RTC_SAFECMP_MAKE_OP(name, op) \ + struct name { \ + template \ + static constexpr bool Op(T1 a, T2 b) { \ + return a op b; \ + } \ + }; +RTC_SAFECMP_MAKE_OP(EqOp, ==) +RTC_SAFECMP_MAKE_OP(NeOp, !=) +RTC_SAFECMP_MAKE_OP(LtOp, <) +RTC_SAFECMP_MAKE_OP(LeOp, <=) +RTC_SAFECMP_MAKE_OP(GtOp, >) +RTC_SAFECMP_MAKE_OP(GeOp, >=) +#undef RTC_SAFECMP_MAKE_OP + +} // namespace safe_cmp_impl + +#define RTC_SAFECMP_MAKE_FUN(name) \ + template \ + constexpr \ + typename std::enable_if::value && IsIntlike::value, \ + bool>::type Safe##name(T1 a, T2 b) { \ + /* Unary plus here turns enums into real integral types. */ \ + return safe_cmp_impl::Cmp(+a, +b); \ + } \ + template \ + constexpr \ + typename std::enable_if::value || !IsIntlike::value, \ + bool>::type Safe##name(const T1& a, \ + const T2& b) { \ + return safe_cmp_impl::name##Op::Op(a, b); \ + } +RTC_SAFECMP_MAKE_FUN(Eq) +RTC_SAFECMP_MAKE_FUN(Ne) +RTC_SAFECMP_MAKE_FUN(Lt) +RTC_SAFECMP_MAKE_FUN(Le) +RTC_SAFECMP_MAKE_FUN(Gt) +RTC_SAFECMP_MAKE_FUN(Ge) +#undef RTC_SAFECMP_MAKE_FUN + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::SafeEq; +using ::webrtc::SafeGe; +using ::webrtc::SafeGt; +using ::webrtc::SafeLe; +using ::webrtc::SafeLt; +using ::webrtc::SafeNe; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NUMERICS_SAFE_COMPARE_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/safe_conversions.h b/pkg/apm/webrtc/rtc_base/numerics/safe_conversions.h new file mode 100644 index 00000000..8b981ac9 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/safe_conversions.h @@ -0,0 +1,85 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/numerics/safe_conversions.h. + +#ifndef RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ +#define RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions_impl.h" + +namespace webrtc { + +// Convenience function that returns true if the supplied value is in range +// for the destination type. +template +inline constexpr bool IsValueInRangeForNumericType(Src value) { + return internal::RangeCheck(value) == internal::TYPE_VALID; +} + +// checked_cast<> and dchecked_cast<> are analogous to static_cast<> for +// numeric types, except that they [D]CHECK that the specified numeric +// conversion will not overflow or underflow. NaN source will always trigger +// the [D]CHECK. +template +inline constexpr Dst checked_cast(Src value) { + RTC_CHECK(IsValueInRangeForNumericType(value)); + return static_cast(value); +} +template +inline constexpr Dst dchecked_cast(Src value) { + RTC_DCHECK(IsValueInRangeForNumericType(value)); + return static_cast(value); +} + +// saturated_cast<> is analogous to static_cast<> for numeric types, except +// that the specified numeric conversion will saturate rather than overflow or +// underflow. NaN assignment to an integral will trigger a RTC_CHECK condition. +template +inline constexpr Dst saturated_cast(Src value) { + // Optimization for floating point values, which already saturate. + if (std::numeric_limits::is_iec559) + return static_cast(value); + + switch (internal::RangeCheck(value)) { + case internal::TYPE_VALID: + return static_cast(value); + + case internal::TYPE_UNDERFLOW: + return std::numeric_limits::min(); + + case internal::TYPE_OVERFLOW: + return std::numeric_limits::max(); + + // Should fail only on attempting to assign NaN to a saturated integer. + case internal::TYPE_INVALID: + RTC_CHECK_NOTREACHED(); + } + + RTC_CHECK_NOTREACHED(); +} + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::checked_cast; +using ::webrtc::dchecked_cast; +using ::webrtc::IsValueInRangeForNumericType; +using ::webrtc::saturated_cast; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/safe_conversions_impl.h b/pkg/apm/webrtc/rtc_base/numerics/safe_conversions_impl.h new file mode 100644 index 00000000..99a3bc83 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/safe_conversions_impl.h @@ -0,0 +1,180 @@ +/* + * Copyright 2014 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/numerics/safe_conversions_impl.h. + +#ifndef RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ +#define RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ + +#include +#include + +namespace webrtc { +namespace internal { + +enum DstSign { DST_UNSIGNED, DST_SIGNED }; + +enum SrcSign { SRC_UNSIGNED, SRC_SIGNED }; + +enum DstRange { OVERLAPS_RANGE, CONTAINS_RANGE }; + +// Helper templates to statically determine if our destination type can contain +// all values represented by the source type. + +template ::is_signed ? DST_SIGNED : DST_UNSIGNED, + SrcSign IsSrcSigned = + std::numeric_limits::is_signed ? SRC_SIGNED : SRC_UNSIGNED> +struct StaticRangeCheck {}; + +template +struct StaticRangeCheck { + typedef std::numeric_limits DstLimits; + typedef std::numeric_limits SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static const size_t kDstMaxExponent = + DstLimits::is_iec559 ? DstLimits::max_exponent : (sizeof(Dst) * 8 - 1); + static const size_t kSrcMaxExponent = + SrcLimits::is_iec559 ? SrcLimits::max_exponent : (sizeof(Src) * 8 - 1); + static const DstRange value = + kDstMaxExponent >= kSrcMaxExponent ? CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template +struct StaticRangeCheck { + static const DstRange value = + sizeof(Dst) >= sizeof(Src) ? CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template +struct StaticRangeCheck { + typedef std::numeric_limits DstLimits; + typedef std::numeric_limits SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static const size_t kDstMaxExponent = + DstLimits::is_iec559 ? DstLimits::max_exponent : (sizeof(Dst) * 8 - 1); + static const size_t kSrcMaxExponent = sizeof(Src) * 8; + static const DstRange value = + kDstMaxExponent >= kSrcMaxExponent ? CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template +struct StaticRangeCheck { + static const DstRange value = OVERLAPS_RANGE; +}; + +enum RangeCheckResult { + TYPE_VALID = 0, // Value can be represented by the destination type. + TYPE_UNDERFLOW = 1, // Value would overflow. + TYPE_OVERFLOW = 2, // Value would underflow. + TYPE_INVALID = 3 // Source value is invalid (i.e. NaN). +}; + +// This macro creates a RangeCheckResult from an upper and lower bound +// check by taking advantage of the fact that only NaN can be out of range in +// both directions at once. +#define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \ + RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \ + ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW)) + +template ::is_signed ? DST_SIGNED : DST_UNSIGNED, + SrcSign IsSrcSigned = + std::numeric_limits::is_signed ? SRC_SIGNED : SRC_UNSIGNED, + DstRange IsSrcRangeContained = StaticRangeCheck::value> +struct RangeCheckImpl {}; + +// The following templates are for ranges that must be verified at runtime. We +// split it into checks based on signedness to avoid confusing casts and +// compiler warnings on signed an unsigned comparisons. + +// Dst range always contains the result: nothing to check. +template +struct RangeCheckImpl { + static constexpr RangeCheckResult Check(Src /* value */) { + return TYPE_VALID; + } +}; + +// Signed to signed narrowing. +template +struct RangeCheckImpl { + static constexpr RangeCheckResult Check(Src value) { + typedef std::numeric_limits DstLimits; + return DstLimits::is_iec559 + ? BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(DstLimits::max() * -1)) + : BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(DstLimits::min())); + } +}; + +// Unsigned to unsigned narrowing. +template +struct RangeCheckImpl { + static constexpr RangeCheckResult Check(Src value) { + typedef std::numeric_limits DstLimits; + return BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), true); + } +}; + +// Unsigned to signed. +template +struct RangeCheckImpl { + static constexpr RangeCheckResult Check(Src value) { + typedef std::numeric_limits DstLimits; + return sizeof(Dst) > sizeof(Src) + ? TYPE_VALID + : BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), true); + } +}; + +// Signed to unsigned. +template +struct RangeCheckImpl { + typedef std::numeric_limits DstLimits; + typedef std::numeric_limits SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static constexpr size_t DstMaxExponent() { return sizeof(Dst) * 8; } + static constexpr size_t SrcMaxExponent() { + return SrcLimits::is_iec559 ? SrcLimits::max_exponent + : (sizeof(Src) * 8 - 1); + } + static constexpr RangeCheckResult Check(Src value) { + return (DstMaxExponent() >= SrcMaxExponent()) + ? BASE_NUMERIC_RANGE_CHECK_RESULT(true, + value >= static_cast(0)) + : BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(0)); + } +}; + +template +inline constexpr RangeCheckResult RangeCheck(Src value) { + static_assert(std::numeric_limits::is_specialized, + "argument must be numeric"); + static_assert(std::numeric_limits::is_specialized, + "result must be numeric"); + return RangeCheckImpl::Check(value); +} + +} // namespace internal +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/safe_minmax.h b/pkg/apm/webrtc/rtc_base/numerics/safe_minmax.h new file mode 100644 index 00000000..95993796 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/safe_minmax.h @@ -0,0 +1,346 @@ +/* + * 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. + */ + +// Minimum and maximum +// =================== +// +// webrtc::SafeMin(x, y) +// webrtc::SafeMax(x, y) +// +// (These are both constexpr.) +// +// Accept two arguments of either any two integral or any two floating-point +// types, and return the smaller and larger value, respectively, with no +// truncation or wrap-around. If only one of the input types is statically +// guaranteed to be able to represent the result, the return type is that type; +// if either one would do, the result type is the smaller type. (One of these +// two cases always applies.) +// +// * The case with one floating-point and one integral type is not allowed, +// because the floating-point type will have greater range, but may not +// have sufficient precision to represent the integer value exactly.) +// +// Clamp (a.k.a. constrain to a given interval) +// ============================================ +// +// webrtc::SafeClamp(x, a, b) +// +// Accepts three arguments of any mix of integral types or any mix of +// floating-point types, and returns the value in the closed interval [a, b] +// that is closest to x (that is, if x < a it returns a; if x > b it returns b; +// and if a <= x <= b it returns x). As for SafeMin() and SafeMax(), there is +// no truncation or wrap-around. The result type +// +// 1. is statically guaranteed to be able to represent the result; +// +// 2. is no larger than the largest of the three argument types; and +// +// 3. has the same signedness as the type of the first argument, if this is +// possible without violating the First or Second Law. +// +// There is always at least one type that meets criteria 1 and 2. If more than +// one type meets these criteria equally well, the result type is one of the +// types that is smallest. Note that unlike SafeMin() and SafeMax(), +// SafeClamp() will sometimes pick a return type that isn't the type of any of +// its arguments. +// +// * In this context, a type A is smaller than a type B if it has a smaller +// range; that is, if A::max() - A::min() < B::max() - B::min(). For +// example, int8_t < int16_t == uint16_t < int32_t, and all integral types +// are smaller than all floating-point types.) +// +// * As for SafeMin and SafeMax, mixing integer and floating-point arguments +// is not allowed, because floating-point types have greater range than +// integer types, but do not have sufficient precision to represent the +// values of most integer types exactly. +// +// Requesting a specific return type +// ================================= +// +// All three functions allow callers to explicitly specify the return type as a +// template parameter, overriding the default return type. E.g. +// +// webrtc::SafeMin(x, y) // returns an int +// +// If the requested type is statically guaranteed to be able to represent the +// result, then everything's fine, and the return type is as requested. But if +// the requested type is too small, a static_assert is triggered. + +#ifndef RTC_BASE_NUMERICS_SAFE_MINMAX_H_ +#define RTC_BASE_NUMERICS_SAFE_MINMAX_H_ + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_compare.h" +#include "rtc_base/type_traits.h" + +namespace webrtc { + +namespace safe_minmax_impl { + +// Make the range of a type available via something other than a constexpr +// function, to work around MSVC limitations. See +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +template +struct Limits { + static constexpr T lowest = std::numeric_limits::lowest(); + static constexpr T max = std::numeric_limits::max(); +}; + +template ::value> +struct UnderlyingType; + +template +struct UnderlyingType { + using type = T; +}; + +template +struct UnderlyingType { + using type = typename std::underlying_type::type; +}; + +// Given two types T1 and T2, find types that can hold the smallest (in +// ::min_t) and the largest (in ::max_t) of the two values. +template ::value, + bool int2 = IsIntlike::value> +struct MType { + static_assert(int1 == int2, + "You may not mix integral and floating-point arguments"); +}; + +// Specialization for when neither type is integral (and therefore presumably +// floating-point). +template +struct MType { + using min_t = typename std::common_type::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); + + using max_t = typename std::common_type::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); +}; + +// Specialization for when both types are integral. +template +struct MType { + // The type with the lowest minimum value. In case of a tie, the type with + // the lowest maximum value. In case that too is a tie, the types have the + // same range, and we arbitrarily pick T1. + using min_t = typename std::conditional< + SafeLt(Limits::lowest, Limits::lowest), + T1, + typename std::conditional< + SafeGt(Limits::lowest, Limits::lowest), + T2, + typename std::conditional::max, Limits::max), + T1, + T2>::type>::type>::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); + + // The type with the highest maximum value. In case of a tie, the types have + // the same range (because in C++, integer types with the same maximum also + // have the same minimum). + static_assert(SafeNe(Limits::max, Limits::max) || + SafeEq(Limits::lowest, Limits::lowest), + "integer types with the same max should have the same min"); + using max_t = typename std:: + conditional::max, Limits::max), T1, T2>::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); +}; + +// A dummy type that we pass around at compile time but never actually use. +// Declared but not defined. +struct DefaultType; + +// ::type is A, except we fall back to B if A is DefaultType. We static_assert +// that the chosen type can hold all values that B can hold. +template +struct TypeOr { + using type = typename std:: + conditional::value, B, A>::type; + static_assert(SafeLe(Limits::lowest, Limits::lowest) && + SafeGe(Limits::max, Limits::max), + "The specified type isn't large enough"); + static_assert(IsIntlike::value == IsIntlike::value && + std::is_floating_point::value == + std::is_floating_point::value, + "float<->int conversions not allowed"); +}; + +} // namespace safe_minmax_impl + +template < + typename R = safe_minmax_impl::DefaultType, + typename T1 = safe_minmax_impl::DefaultType, + typename T2 = safe_minmax_impl::DefaultType, + typename R2 = typename safe_minmax_impl::TypeOr< + R, + typename safe_minmax_impl::MType< + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type>::min_t>::type> +constexpr R2 SafeMin(T1 a, T2 b) { + static_assert(IsIntlike::value || std::is_floating_point::value, + "The first argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The second argument must be integral or floating-point"); + return SafeLt(a, b) ? static_cast(a) : static_cast(b); +} + +template < + typename R = safe_minmax_impl::DefaultType, + typename T1 = safe_minmax_impl::DefaultType, + typename T2 = safe_minmax_impl::DefaultType, + typename R2 = typename safe_minmax_impl::TypeOr< + R, + typename safe_minmax_impl::MType< + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type>::max_t>::type> +constexpr R2 SafeMax(T1 a, T2 b) { + static_assert(IsIntlike::value || std::is_floating_point::value, + "The first argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The second argument must be integral or floating-point"); + return SafeGt(a, b) ? static_cast(a) : static_cast(b); +} + +namespace safe_minmax_impl { + +// Given three types T, L, and H, let ::type be a suitable return value for +// SafeClamp(T, L, H). See the docs at the top of this file for details. +template ::value, + bool int2 = IsIntlike::value, + bool int3 = IsIntlike::value> +struct ClampType { + static_assert(int1 == int2 && int1 == int3, + "You may not mix integral and floating-point arguments"); +}; + +// Specialization for when all three types are floating-point. +template +struct ClampType { + using type = typename std::common_type::type; +}; + +// Specialization for when all three types are integral. +template +struct ClampType { + private: + // Range of the return value. The return type must be able to represent this + // full range. + static constexpr auto r_min = + SafeMax(Limits::lowest, SafeMin(Limits::lowest, Limits::lowest)); + static constexpr auto r_max = + SafeMin(Limits::max, SafeMax(Limits::max, Limits::max)); + + // Is the given type an acceptable return type? (That is, can it represent + // all possible return values, and is it no larger than the largest of the + // input types?) + template + struct AcceptableType { + private: + static constexpr bool not_too_large = sizeof(A) <= sizeof(L) || + sizeof(A) <= sizeof(H) || + sizeof(A) <= sizeof(T); + static constexpr bool range_contained = + SafeLe(Limits::lowest, r_min) && SafeLe(r_max, Limits::max); + + public: + static constexpr bool value = not_too_large && range_contained; + }; + + using best_signed_type = typename std::conditional< + AcceptableType::value, + int8_t, + typename std::conditional< + AcceptableType::value, + int16_t, + typename std::conditional::value, + int32_t, + int64_t>::type>::type>::type; + + using best_unsigned_type = typename std::conditional< + AcceptableType::value, + uint8_t, + typename std::conditional< + AcceptableType::value, + uint16_t, + typename std::conditional::value, + uint32_t, + uint64_t>::type>::type>::type; + + public: + // Pick the best type, preferring the same signedness as T but falling back + // to the other one if necessary. + using type = typename std::conditional< + std::is_signed::value, + typename std::conditional::value, + best_signed_type, + best_unsigned_type>::type, + typename std::conditional::value, + best_unsigned_type, + best_signed_type>::type>::type; + static_assert(AcceptableType::value, ""); +}; + +} // namespace safe_minmax_impl + +template < + typename R = safe_minmax_impl::DefaultType, + typename T = safe_minmax_impl::DefaultType, + typename L = safe_minmax_impl::DefaultType, + typename H = safe_minmax_impl::DefaultType, + typename R2 = typename safe_minmax_impl::TypeOr< + R, + typename safe_minmax_impl::ClampType< + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type>::type>::type> +R2 SafeClamp(T x, L min, H max) { + static_assert(IsIntlike::value || std::is_floating_point::value, + "The first argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The second argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The third argument must be integral or floating-point"); + RTC_DCHECK_LE(min, max); + return SafeLe(x, min) ? static_cast(min) + : SafeGe(x, max) ? static_cast(max) + : static_cast(x); +} + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::SafeClamp; +using ::webrtc::SafeMax; +using ::webrtc::SafeMin; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_NUMERICS_SAFE_MINMAX_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/sample_counter.cc b/pkg/apm/webrtc/rtc_base/numerics/sample_counter.cc new file mode 100644 index 00000000..822378fe --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/sample_counter.cc @@ -0,0 +1,120 @@ +/* + * 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 "rtc_base/numerics/sample_counter.h" + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { + +SampleCounter::SampleCounter() = default; +SampleCounter::~SampleCounter() = default; + +void SampleCounter::Add(int sample) { + if (sum_ > 0) { + RTC_DCHECK_LE(sample, std::numeric_limits::max() - sum_); + } else { + RTC_DCHECK_GE(sample, std::numeric_limits::min() - sum_); + } + sum_ += sample; + ++num_samples_; + if (!max_ || sample > *max_) { + max_ = sample; + } + if (!min_ || sample < *min_) { + min_ = sample; + } +} + +void SampleCounter::Add(const SampleCounter& other) { + if (sum_ > 0) { + RTC_DCHECK_LE(other.sum_, std::numeric_limits::max() - sum_); + } else { + RTC_DCHECK_GE(other.sum_, std::numeric_limits::min() - sum_); + } + sum_ += other.sum_; + RTC_DCHECK_LE(other.num_samples_, + std::numeric_limits::max() - num_samples_); + num_samples_ += other.num_samples_; + if (other.max_ && (!max_ || *max_ < *other.max_)) + max_ = other.max_; + if (other.min_ && (!min_ || *min_ > *other.min_)) + min_ = other.min_; +} + +std::optional SampleCounter::Avg(int64_t min_required_samples) const { + RTC_DCHECK_GT(min_required_samples, 0); + if (num_samples_ < min_required_samples) + return std::nullopt; + return dchecked_cast(sum_ / num_samples_); +} + +std::optional SampleCounter::Max() const { + return max_; +} + +std::optional SampleCounter::Min() const { + return min_; +} + +std::optional SampleCounter::Sum(int64_t min_required_samples) const { + RTC_DCHECK_GT(min_required_samples, 0); + if (num_samples_ < min_required_samples) + return std::nullopt; + return sum_; +} + +int64_t SampleCounter::NumSamples() const { + return num_samples_; +} + +void SampleCounter::Reset() { + *this = {}; +} + +SampleCounterWithVariance::SampleCounterWithVariance() = default; +SampleCounterWithVariance::~SampleCounterWithVariance() = default; + +std::optional SampleCounterWithVariance::Variance( + int64_t min_required_samples) const { + RTC_DCHECK_GT(min_required_samples, 0); + if (num_samples_ < min_required_samples) + return std::nullopt; + // E[(x-mean)^2] = E[x^2] - mean^2 + int64_t mean = sum_ / num_samples_; + return sum_squared_ / num_samples_ - mean * mean; +} + +void SampleCounterWithVariance::Add(int sample) { + SampleCounter::Add(sample); + // Prevent overflow in squaring. + RTC_DCHECK_GT(sample, std::numeric_limits::min()); + RTC_DCHECK_LE(int64_t{sample} * sample, + std::numeric_limits::max() - sum_squared_); + sum_squared_ += int64_t{sample} * sample; +} + +void SampleCounterWithVariance::Add(const SampleCounterWithVariance& other) { + SampleCounter::Add(other); + RTC_DCHECK_LE(other.sum_squared_, + std::numeric_limits::max() - sum_squared_); + sum_squared_ += other.sum_squared_; +} + +void SampleCounterWithVariance::Reset() { + *this = {}; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/numerics/sample_counter.h b/pkg/apm/webrtc/rtc_base/numerics/sample_counter.h new file mode 100644 index 00000000..86a1691c --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/sample_counter.h @@ -0,0 +1,69 @@ +/* + * 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 RTC_BASE_NUMERICS_SAMPLE_COUNTER_H_ +#define RTC_BASE_NUMERICS_SAMPLE_COUNTER_H_ + +#include + +#include + +namespace webrtc { + +// Simple utility class for counting basic statistics (max./avg./variance) on +// stream of samples. +class SampleCounter { + public: + SampleCounter(); + ~SampleCounter(); + void Add(int sample); + std::optional Avg(int64_t min_required_samples) const; + std::optional Max() const; + std::optional Min() const; + std::optional Sum(int64_t min_required_samples) const; + int64_t NumSamples() const; + void Reset(); + // Adds all the samples from the `other` SampleCounter as if they were all + // individually added using `Add(int)` method. + void Add(const SampleCounter& other); + + protected: + int64_t sum_ = 0; + int64_t num_samples_ = 0; + std::optional max_; + std::optional min_; +}; + +class SampleCounterWithVariance : public SampleCounter { + public: + SampleCounterWithVariance(); + ~SampleCounterWithVariance(); + void Add(int sample); + std::optional Variance(int64_t min_required_samples) const; + void Reset(); + // Adds all the samples from the `other` SampleCounter as if they were all + // individually added using `Add(int)` method. + void Add(const SampleCounterWithVariance& other); + + private: + int64_t sum_squared_ = 0; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::SampleCounter; +using ::webrtc::SampleCounterWithVariance; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES +#endif // RTC_BASE_NUMERICS_SAMPLE_COUNTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/sample_stats.cc b/pkg/apm/webrtc/rtc_base/numerics/sample_stats.cc new file mode 100644 index 00000000..606b1b62 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/sample_stats.cc @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "rtc_base/numerics/sample_stats.h" + +#include + +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +double SampleStats::Max() { + if (IsEmpty()) + return INFINITY; + return GetMax(); +} + +double SampleStats::Mean() { + if (IsEmpty()) + return 0; + return GetAverage(); +} + +double SampleStats::Median() { + return Quantile(0.5); +} + +double SampleStats::Quantile(double quantile) { + if (IsEmpty()) + return 0; + return GetPercentile(quantile); +} + +double SampleStats::Min() { + if (IsEmpty()) + return -INFINITY; + return GetMin(); +} + +double SampleStats::Variance() { + if (IsEmpty()) + return 0; + return GetVariance(); +} + +double SampleStats::StandardDeviation() { + return sqrt(Variance()); +} + +int SampleStats::Count() { + return static_cast(GetSamples().size()); +} + +void SampleStats::AddSample(TimeDelta delta) { + RTC_DCHECK(delta.IsFinite()); + stats_.AddSample(delta.seconds()); +} + +void SampleStats::AddSampleMs(double delta_ms) { + AddSample(TimeDelta::Millis(delta_ms)); +} +void SampleStats::AddSamples(const SampleStats& other) { + stats_.AddSamples(other.stats_); +} + +bool SampleStats::IsEmpty() { + return stats_.IsEmpty(); +} + +TimeDelta SampleStats::Max() { + return TimeDelta::Seconds(stats_.Max()); +} + +TimeDelta SampleStats::Mean() { + return TimeDelta::Seconds(stats_.Mean()); +} + +TimeDelta SampleStats::Median() { + return Quantile(0.5); +} + +TimeDelta SampleStats::Quantile(double quantile) { + return TimeDelta::Seconds(stats_.Quantile(quantile)); +} + +TimeDelta SampleStats::Min() { + return TimeDelta::Seconds(stats_.Min()); +} + +TimeDelta SampleStats::Variance() { + return TimeDelta::Seconds(stats_.Variance()); +} + +TimeDelta SampleStats::StandardDeviation() { + return TimeDelta::Seconds(stats_.StandardDeviation()); +} + +int SampleStats::Count() { + return stats_.Count(); +} + +void SampleStats::AddSample(DataRate sample) { + stats_.AddSample(sample.bps()); +} + +void SampleStats::AddSampleBps(double rate_bps) { + stats_.AddSample(rate_bps); +} + +void SampleStats::AddSamples(const SampleStats& other) { + stats_.AddSamples(other.stats_); +} + +bool SampleStats::IsEmpty() { + return stats_.IsEmpty(); +} + +DataRate SampleStats::Max() { + return DataRate::BitsPerSec(stats_.Max()); +} + +DataRate SampleStats::Mean() { + return DataRate::BitsPerSec(stats_.Mean()); +} + +DataRate SampleStats::Median() { + return Quantile(0.5); +} + +DataRate SampleStats::Quantile(double quantile) { + return DataRate::BitsPerSec(stats_.Quantile(quantile)); +} + +DataRate SampleStats::Min() { + return DataRate::BitsPerSec(stats_.Min()); +} + +DataRate SampleStats::Variance() { + return DataRate::BitsPerSec(stats_.Variance()); +} + +DataRate SampleStats::StandardDeviation() { + return DataRate::BitsPerSec(stats_.StandardDeviation()); +} + +int SampleStats::Count() { + return stats_.Count(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/numerics/sample_stats.h b/pkg/apm/webrtc/rtc_base/numerics/sample_stats.h new file mode 100644 index 00000000..11a884ba --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/sample_stats.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_NUMERICS_SAMPLE_STATS_H_ +#define RTC_BASE_NUMERICS_SAMPLE_STATS_H_ + +#include "api/numerics/samples_stats_counter.h" +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" + +namespace webrtc { +template +class SampleStats; + +// TODO(srte): Merge this implementation with SamplesStatsCounter. +template <> +class SampleStats : public SamplesStatsCounter { + public: + double Max(); + double Mean(); + double Median(); + double Quantile(double quantile); + double Min(); + double Variance(); + double StandardDeviation(); + int Count(); +}; + +template <> +class SampleStats { + public: + void AddSample(TimeDelta delta); + void AddSampleMs(double delta_ms); + void AddSamples(const SampleStats& other); + bool IsEmpty(); + TimeDelta Max(); + TimeDelta Mean(); + TimeDelta Median(); + TimeDelta Quantile(double quantile); + TimeDelta Min(); + TimeDelta Variance(); + TimeDelta StandardDeviation(); + int Count(); + + private: + SampleStats stats_; +}; + +template <> +class SampleStats { + public: + void AddSample(DataRate rate); + void AddSampleBps(double rate_bps); + void AddSamples(const SampleStats& other); + bool IsEmpty(); + DataRate Max(); + DataRate Mean(); + DataRate Median(); + DataRate Quantile(double quantile); + DataRate Min(); + DataRate Variance(); + DataRate StandardDeviation(); + int Count(); + + private: + SampleStats stats_; +}; +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_SAMPLE_STATS_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/sequence_number_unwrapper.h b/pkg/apm/webrtc/rtc_base/numerics/sequence_number_unwrapper.h new file mode 100644 index 00000000..1def581c --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/sequence_number_unwrapper.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UNWRAPPER_H_ +#define RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UNWRAPPER_H_ + +#include + +#include +#include +#include + +#include "rtc_base/numerics/sequence_number_util.h" + +namespace webrtc { + +// A sequence number unwrapper where the first unwrapped value equals the +// first value being unwrapped. +template +class SeqNumUnwrapper { + static_assert( + std::is_unsigned::value && + std::numeric_limits::max() < std::numeric_limits::max(), + "Type unwrapped must be an unsigned integer smaller than int64_t."); + + public: + // Unwraps `value` and updates the internal state of the unwrapper. + int64_t Unwrap(T value) { + if (!last_value_) { + last_unwrapped_ = {value}; + } else { + last_unwrapped_ += Delta(*last_value_, value); + } + + last_value_ = value; + return last_unwrapped_; + } + + // Returns the `value` without updating the internal state of the unwrapper. + int64_t PeekUnwrap(T value) const { + if (!last_value_) { + return value; + } + return last_unwrapped_ + Delta(*last_value_, value); + } + + // Resets the unwrapper to its initial state. Unwrapped sequence numbers will + // being at 0 after resetting. + void Reset() { + last_unwrapped_ = 0; + last_value_.reset(); + } + + private: + static int64_t Delta(T last_value, T new_value) { + constexpr int64_t kBackwardAdjustment = + M == 0 ? int64_t{std::numeric_limits::max()} + 1 : M; + int64_t result = ForwardDiff(last_value, new_value); + if (!AheadOrAt(new_value, last_value)) { + result -= kBackwardAdjustment; + } + return result; + } + + int64_t last_unwrapped_ = 0; + std::optional last_value_; +}; + +using RtpTimestampUnwrapper = SeqNumUnwrapper; +using RtpSequenceNumberUnwrapper = SeqNumUnwrapper; + +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UNWRAPPER_H_ diff --git a/pkg/apm/webrtc/rtc_base/numerics/sequence_number_util.h b/pkg/apm/webrtc/rtc_base/numerics/sequence_number_util.h new file mode 100644 index 00000000..5aeaf552 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/numerics/sequence_number_util.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_ +#define RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_ + +#include +#include + +#include "rtc_base/numerics/mod_ops.h" + +namespace webrtc { + +// Test if the sequence number `a` is ahead or at sequence number `b`. +// +// If `M` is an even number and the two sequence numbers are at max distance +// from each other, then the sequence number with the highest value is +// considered to be ahead. +template +inline typename std::enable_if<(M > 0), bool>::type AheadOrAt(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + const T maxDist = M / 2; + if (!(M & 1) && MinDiff(a, b) == maxDist) + return b < a; + return ForwardDiff(b, a) <= maxDist; +} + +template +inline typename std::enable_if<(M == 0), bool>::type AheadOrAt(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + const T maxDist = std::numeric_limits::max() / 2 + T(1); + if (a - b == maxDist) + return b < a; + return ForwardDiff(b, a) < maxDist; +} + +template +inline bool AheadOrAt(T a, T b) { + return AheadOrAt(a, b); +} + +// Test if the sequence number `a` is ahead of sequence number `b`. +// +// If `M` is an even number and the two sequence numbers are at max distance +// from each other, then the sequence number with the highest value is +// considered to be ahead. +template +inline bool AheadOf(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return a != b && AheadOrAt(a, b); +} + +// Comparator used to compare sequence numbers in a continuous fashion. +// +// WARNING! If used to sort sequence numbers of length M then the interval +// covered by the sequence numbers may not be larger than floor(M/2). +template +struct AscendingSeqNumComp { + bool operator()(T a, T b) const { return AheadOf(a, b); } +}; + +// Comparator used to compare sequence numbers in a continuous fashion. +// +// WARNING! If used to sort sequence numbers of length M then the interval +// covered by the sequence numbers may not be larger than floor(M/2). +template +struct DescendingSeqNumComp { + bool operator()(T a, T b) const { return AheadOf(b, a); } +}; + +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_ diff --git a/pkg/apm/webrtc/rtc_base/one_time_event.h b/pkg/apm/webrtc/rtc_base/one_time_event.h new file mode 100644 index 00000000..d33ddbd5 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/one_time_event.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_ONE_TIME_EVENT_H_ +#define RTC_BASE_ONE_TIME_EVENT_H_ + +#include "rtc_base/synchronization/mutex.h" + +namespace webrtc { +// Provides a simple way to perform an operation (such as logging) one +// time in a certain scope. +// Example: +// OneTimeEvent firstFrame; +// ... +// if (firstFrame()) { +// RTC_LOG(LS_INFO) << "This is the first frame". +// } +class OneTimeEvent { + public: + OneTimeEvent() {} + bool operator()() { + MutexLock lock(&mutex_); + if (happened_) { + return false; + } + happened_ = true; + return true; + } + + private: + bool happened_ = false; + Mutex mutex_; +}; + +// A non-thread-safe, ligher-weight version of the OneTimeEvent class. +class ThreadUnsafeOneTimeEvent { + public: + ThreadUnsafeOneTimeEvent() {} + bool operator()() { + if (happened_) { + return false; + } + happened_ = true; + return true; + } + + private: + bool happened_ = false; +}; + +} // namespace webrtc + +#endif // RTC_BASE_ONE_TIME_EVENT_H_ diff --git a/pkg/apm/webrtc/rtc_base/openssl.h b/pkg/apm/webrtc/rtc_base/openssl.h new file mode 100644 index 00000000..17af84d0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/openssl.h @@ -0,0 +1,25 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_OPENSSL_H_ +#define RTC_BASE_OPENSSL_H_ + +#if defined(WEBRTC_WIN) +// Must be included first before openssl headers. +#include "rtc_base/win32.h" // NOLINT +#endif // WEBRTC_WIN + +#include // IWYU pragma: export + +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) +#error OpenSSL is older than 1.1.0, which is the minimum supported version. +#endif + +#endif // RTC_BASE_OPENSSL_H_ diff --git a/pkg/apm/webrtc/rtc_base/openssl_adapter.h b/pkg/apm/webrtc/rtc_base/openssl_adapter.h new file mode 100644 index 00000000..479b1a67 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/openssl_adapter.h @@ -0,0 +1,248 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_OPENSSL_ADAPTER_H_ +#define RTC_BASE_OPENSSL_ADAPTER_H_ + +#include +#include +#include + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/task_queue/pending_task_safety_flag.h" +#include "rtc_base/buffer.h" +#include "rtc_base/openssl_stream_adapter.h" +#ifdef OPENSSL_IS_BORINGSSL +#include "rtc_base/boringssl_identity.h" +#else +#include "rtc_base/openssl_identity.h" +#endif +#include "rtc_base/openssl_session_cache.h" +#include "rtc_base/socket.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/ssl_adapter.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/ssl_stream_adapter.h" + +namespace webrtc { + +class OpenSSLAdapter final : public SSLAdapter { + public: + static bool InitializeSSL(); + static bool CleanupSSL(); + + // Creating an OpenSSLAdapter requires a socket to bind to, an optional + // session cache if you wish to improve performance by caching sessions for + // hostnames you have previously connected to and an optional + // SSLCertificateVerifier which can override any existing trusted roots to + // validate a peer certificate. The cache and verifier are effectively + // immutable after the the SSL connection starts. + explicit OpenSSLAdapter(Socket* socket, + OpenSSLSessionCache* ssl_session_cache = nullptr, + SSLCertificateVerifier* ssl_cert_verifier = nullptr); + ~OpenSSLAdapter() override; + + void SetIgnoreBadCert(bool ignore) override; + void SetAlpnProtocols(const std::vector& protos) override; + void SetEllipticCurves(const std::vector& curves) override; + [[deprecated]] void SetMode(SSLMode mode) override; + void SetCertVerifier(SSLCertificateVerifier* ssl_cert_verifier) override; + void SetIdentity(std::unique_ptr identity) override; + void SetRole(SSLRole role) override; + int StartSSL(absl::string_view hostname) override; + int Send(const void* pv, size_t cb) override; + int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override; + int Recv(void* pv, size_t cb, int64_t* timestamp) override; + int RecvFrom(void* pv, + size_t cb, + SocketAddress* paddr, + int64_t* timestamp) override; + int Close() override; + // Note that the socket returns ST_CONNECTING while SSL is being negotiated. + ConnState GetState() const override; + bool IsResumedSession() override; + // Creates a new SSL_CTX object, configured for client-to-server usage + // with SSLMode `mode`, and if `enable_cache` is true, with support for + // storing successful sessions so that they can be later resumed. + // OpenSSLAdapterFactory will call this method to create its own internal + // SSL_CTX, and OpenSSLAdapter will also call this when used without a + // factory. + static SSL_CTX* CreateContext(SSLMode mode, bool enable_cache); + + protected: + void OnConnectEvent(Socket* socket) override; + void OnReadEvent(Socket* socket) override; + void OnWriteEvent(Socket* socket) override; + void OnCloseEvent(Socket* socket, int err) override; + + private: + class EarlyExitCatcher { + public: + EarlyExitCatcher(OpenSSLAdapter& adapter_ptr); + void disable(); + ~EarlyExitCatcher(); + + private: + bool disabled_ = false; + OpenSSLAdapter& adapter_ptr_; + }; + enum SSLState { + SSL_NONE, + SSL_WAIT, + SSL_CONNECTING, + SSL_CONNECTED, + SSL_ERROR + }; + + int BeginSSL(); + int ContinueSSL(); + void Error(absl::string_view context, int err, bool signal = true); + void Cleanup(); + void OnTimeout(); + + // Return value and arguments have the same meanings as for Send; `error` is + // an output parameter filled with the result of SSL_get_error. + int DoSslWrite(const void* pv, size_t cb, int* error); + bool SSLPostConnectionCheck(SSL* ssl, absl::string_view host); + + // Logs info about the state of the SSL connection. + static void SSLInfoCallback(const SSL* ssl, int where, int ret); + +#if defined(OPENSSL_IS_BORINGSSL) && \ + defined(WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS) + static enum ssl_verify_result_t SSLVerifyCallback(SSL* ssl, + uint8_t* out_alert); + enum ssl_verify_result_t SSLVerifyInternal(SSL* ssl, uint8_t* out_alert); +#else + static int SSLVerifyCallback(int ok, X509_STORE_CTX* store); + // Call a custom verifier, if installed. + // Returns 1 on success, `status_on_error` on error or verification failure. + int SSLVerifyInternal(int status_on_error, SSL* ssl, X509_STORE_CTX* store); +#endif + friend class OpenSSLStreamAdapter; // for custom_verify_callback_; + + // If the SSL_CTX was created with `enable_cache` set to true, this callback + // will be called when a SSL session has been successfully established, + // to allow its SSL_SESSION* to be cached for later resumption. + static int NewSSLSessionCallback(SSL* ssl, SSL_SESSION* session); + + // Optional SSL Shared session cache to improve performance. + OpenSSLSessionCache* ssl_session_cache_ = nullptr; + // Optional SSL Certificate verifier which can be set by a third party. + SSLCertificateVerifier* ssl_cert_verifier_ = nullptr; + // The current connection state of the (d)TLS connection. + SSLState state_; + +#ifdef OPENSSL_IS_BORINGSSL + std::unique_ptr identity_; +#else + std::unique_ptr identity_; +#endif + // Indicates whethere this is a client or a server. + SSLRole role_; + bool ssl_read_needs_write_; + bool ssl_write_needs_read_; + // This buffer is used if SSL_write fails with SSL_ERROR_WANT_WRITE, which + // means we need to keep retrying with *the same exact data* until it + // succeeds. Afterwards it will be cleared. + Buffer pending_data_; + SSL* ssl_; + // Holds the SSL context, which may be shared if an session cache is provided. + SSL_CTX* ssl_ctx_; + // Hostname of server that is being connected, used for SNI. + std::string ssl_host_name_; + // Set the adapter to DTLS or TLS mode before creating the context. + SSLMode ssl_mode_; + // If true, the server certificate need not match the configured hostname. + bool ignore_bad_cert_; + // List of protocols to be used in the TLS ALPN extension. + std::vector alpn_protocols_; + // List of elliptic curves to be used in the TLS elliptic curves extension. + std::vector elliptic_curves_; + // Holds the result of the call to run of the ssl_cert_verify_->Verify() + bool custom_cert_verifier_status_; + // Flag to cancel pending timeout task. + ScopedTaskSafety timer_; +}; + +// The OpenSSLAdapterFactory is responsbile for creating multiple new +// OpenSSLAdapters with a shared SSL_CTX and a shared SSL_SESSION cache. The +// SSL_SESSION cache allows existing SSL_SESSIONS to be reused instead of +// recreating them leading to a significant performance improvement. +class OpenSSLAdapterFactory : public SSLAdapterFactory { + public: + OpenSSLAdapterFactory(); + ~OpenSSLAdapterFactory() override; + // Set the SSL Mode to use with this factory. This should only be set before + // the first adapter is created with the factory. If it is called after it + // will DCHECK. + void SetMode(SSLMode mode) override; + + // Set a custom certificate verifier to be passed down to each instance + // created with this factory. This should only ever be set before the first + // call to the factory and cannot be changed after the fact. + void SetCertVerifier(SSLCertificateVerifier* ssl_cert_verifier) override; + + void SetIdentity(std::unique_ptr identity) override; + + // Choose whether the socket acts as a server socket or client socket. + void SetRole(SSLRole role) override; + + // Methods that control server certificate verification, used in unit tests. + // Do not call these methods in production code. + void SetIgnoreBadCert(bool ignore) override; + + // Constructs a new socket using the shared OpenSSLSessionCache. This means + // existing SSLSessions already in the cache will be reused instead of + // re-created for improved performance. + OpenSSLAdapter* CreateAdapter(Socket* socket) override; + + private: + // Holds the SSLMode (DTLS,TLS) that will be used to set the session cache. + SSLMode ssl_mode_ = webrtc::SSL_MODE_TLS; + SSLRole ssl_role_ = webrtc::SSL_CLIENT; + bool ignore_bad_cert_ = false; + + std::unique_ptr identity_; + + // Holds a cache of existing SSL Sessions. + std::unique_ptr ssl_session_cache_; + // Provides an optional custom callback for verifying SSL certificates, this + // in currently only used for TURN/TLS connections. + SSLCertificateVerifier* ssl_cert_verifier_ = nullptr; + // TODO(benwright): Remove this when context is moved to OpenSSLCommon. + // Hold a friend class to the OpenSSLAdapter to retrieve the context. + friend class OpenSSLAdapter; +}; + +// The EarlyExitCatcher is responsible for calling OpenSSLAdapter::Cleanup on +// destruction. By doing this we have scoped cleanup which can be disabled if +// there were no errors, aka early exits. + +std::string TransformAlpnProtocols(const std::vector& protos); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::OpenSSLAdapter; +using ::webrtc::OpenSSLAdapterFactory; +using ::webrtc::TransformAlpnProtocols; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_OPENSSL_ADAPTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/openssl_certificate.h b/pkg/apm/webrtc/rtc_base/openssl_certificate.h new file mode 100644 index 00000000..044edca3 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/openssl_certificate.h @@ -0,0 +1,81 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_OPENSSL_CERTIFICATE_H_ +#define RTC_BASE_OPENSSL_CERTIFICATE_H_ + +#include +#include +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/buffer.h" +#include "rtc_base/openssl_key_pair.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_identity.h" + +namespace webrtc { + +// OpenSSLCertificate encapsulates an OpenSSL X509* certificate object, +// which is also reference counted inside the OpenSSL library. +class OpenSSLCertificate final : public SSLCertificate { + public: + // X509 object has its reference count incremented. So the caller and + // OpenSSLCertificate share ownership. + explicit OpenSSLCertificate(X509* x509); + + static std::unique_ptr Generate( + OpenSSLKeyPair* key_pair, + const SSLIdentityParams& params); + static std::unique_ptr FromPEMString( + absl::string_view pem_string); + + ~OpenSSLCertificate() override; + + OpenSSLCertificate(const OpenSSLCertificate&) = delete; + OpenSSLCertificate& operator=(const OpenSSLCertificate&) = delete; + + std::unique_ptr Clone() const override; + + X509* x509() const { return x509_; } + + std::string ToPEMString() const override; + void ToDER(Buffer* der_buffer) const override; + bool operator==(const OpenSSLCertificate& other) const; + bool operator!=(const OpenSSLCertificate& other) const; + + // Compute the digest of the certificate given algorithm + bool ComputeDigest(absl::string_view algorithm, + Buffer& digest) const override; + + bool GetSignatureDigestAlgorithm(std::string* algorithm) const override; + + int64_t CertificateExpirationTime() const override; + + private: + X509* x509_; // NOT OWNED +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { + +using ::webrtc::OpenSSLCertificate; + +} +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_OPENSSL_CERTIFICATE_H_ diff --git a/pkg/apm/webrtc/rtc_base/openssl_digest.h b/pkg/apm/webrtc/rtc_base/openssl_digest.h new file mode 100644 index 00000000..345fe4a9 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/openssl_digest.h @@ -0,0 +1,59 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_OPENSSL_DIGEST_H_ +#define RTC_BASE_OPENSSL_DIGEST_H_ + +#include + +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/message_digest.h" +#include "rtc_base/openssl.h" + +namespace webrtc { + +// An implementation of the digest class that uses OpenSSL. +class OpenSSLDigest final : public MessageDigest { + public: + // Creates an OpenSSLDigest with `algorithm` as the hash algorithm. + explicit OpenSSLDigest(absl::string_view algorithm); + ~OpenSSLDigest() override; + // Returns the digest output size (e.g. 16 bytes for MD5). + size_t Size() const override; + // Updates the digest with `len` bytes from `buf`. + void Update(const void* buf, size_t len) override; + // Outputs the digest value to `buf` with length `len`. + size_t Finish(void* buf, size_t len) override; + + // Helper function to look up a digest's EVP by name. + static bool GetDigestEVP(absl::string_view algorithm, const EVP_MD** md); + // Helper function to look up a digest's name by EVP. + static bool GetDigestName(const EVP_MD* md, std::string* algorithm); + // Helper function to get the length of a digest. + static bool GetDigestSize(absl::string_view algorithm, size_t* len); + + private: + EVP_MD_CTX* ctx_ = nullptr; + const EVP_MD* md_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::OpenSSLDigest; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_OPENSSL_DIGEST_H_ diff --git a/pkg/apm/webrtc/rtc_base/openssl_identity.h b/pkg/apm/webrtc/rtc_base/openssl_identity.h new file mode 100644 index 00000000..2b17ae08 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/openssl_identity.h @@ -0,0 +1,83 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_OPENSSL_IDENTITY_H_ +#define RTC_BASE_OPENSSL_IDENTITY_H_ + +#include + +#include +#include +#include + +#include "rtc_base/openssl_certificate.h" +#include "rtc_base/openssl_key_pair.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_identity.h" + +namespace webrtc { + +// Holds a keypair and certificate together, and a method to generate +// them consistently. +class OpenSSLIdentity final : public SSLIdentity { + public: + static std::unique_ptr CreateWithExpiration( + absl::string_view common_name, + const KeyParams& key_params, + time_t certificate_lifetime); + static std::unique_ptr CreateForTest( + const SSLIdentityParams& params); + static std::unique_ptr CreateFromPEMStrings( + absl::string_view private_key, + absl::string_view certificate); + static std::unique_ptr CreateFromPEMChainStrings( + absl::string_view private_key, + absl::string_view certificate_chain); + ~OpenSSLIdentity() override; + + OpenSSLIdentity(const OpenSSLIdentity&) = delete; + OpenSSLIdentity& operator=(const OpenSSLIdentity&) = delete; + + const OpenSSLCertificate& certificate() const override; + const SSLCertChain& cert_chain() const override; + + // Configure an SSL context object to use our key and certificate. + bool ConfigureIdentity(SSL_CTX* ctx); + + std::string PrivateKeyToPEMString() const override; + std::string PublicKeyToPEMString() const override; + bool operator==(const OpenSSLIdentity& other) const; + bool operator!=(const OpenSSLIdentity& other) const; + + private: + OpenSSLIdentity(std::unique_ptr key_pair, + std::unique_ptr certificate); + OpenSSLIdentity(std::unique_ptr key_pair, + std::unique_ptr cert_chain); + std::unique_ptr CloneInternal() const override; + + static std::unique_ptr CreateInternal( + const SSLIdentityParams& params); + + std::unique_ptr key_pair_; + std::unique_ptr cert_chain_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::OpenSSLIdentity; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_OPENSSL_IDENTITY_H_ diff --git a/pkg/apm/webrtc/rtc_base/openssl_key_pair.h b/pkg/apm/webrtc/rtc_base/openssl_key_pair.h new file mode 100644 index 00000000..a8a3aa0d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/openssl_key_pair.h @@ -0,0 +1,69 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_OPENSSL_KEY_PAIR_H_ +#define RTC_BASE_OPENSSL_KEY_PAIR_H_ + +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/ssl_identity.h" + +namespace webrtc { + +// OpenSSLKeyPair encapsulates an OpenSSL EVP_PKEY* keypair object, +// which is reference counted inside the OpenSSL library. +class OpenSSLKeyPair final { + public: + // Takes ownership of the key. + explicit OpenSSLKeyPair(EVP_PKEY* pkey) : pkey_(pkey) { + RTC_DCHECK(pkey_ != nullptr); + } + + static std::unique_ptr Generate(const KeyParams& key_params); + // Constructs a key pair from the private key PEM string. This must not result + // in missing public key parameters. Returns null on error. + static std::unique_ptr FromPrivateKeyPEMString( + absl::string_view pem_string); + + ~OpenSSLKeyPair(); + + OpenSSLKeyPair(const OpenSSLKeyPair&) = delete; + OpenSSLKeyPair& operator=(const OpenSSLKeyPair&) = delete; + + std::unique_ptr Clone(); + + EVP_PKEY* pkey() const { return pkey_; } + std::string PrivateKeyToPEMString() const; + std::string PublicKeyToPEMString() const; + bool operator==(const OpenSSLKeyPair& other) const; + bool operator!=(const OpenSSLKeyPair& other) const; + + private: + void AddReference(); + + EVP_PKEY* pkey_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::OpenSSLKeyPair; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_OPENSSL_KEY_PAIR_H_ diff --git a/pkg/apm/webrtc/rtc_base/openssl_session_cache.h b/pkg/apm/webrtc/rtc_base/openssl_session_cache.h new file mode 100644 index 00000000..0bcbad0e --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/openssl_session_cache.h @@ -0,0 +1,80 @@ +/* + * 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 RTC_BASE_OPENSSL_SESSION_CACHE_H_ +#define RTC_BASE_OPENSSL_SESSION_CACHE_H_ + +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/string_utils.h" + +#ifndef OPENSSL_IS_BORINGSSL +typedef struct ssl_session_st SSL_SESSION; +#endif + +namespace webrtc { + +// The OpenSSLSessionCache maps hostnames to SSL_SESSIONS. This cache is +// owned by the OpenSSLAdapterFactory and is passed down to each OpenSSLAdapter +// created with the factory. +class OpenSSLSessionCache final { + public: + // Creates a new OpenSSLSessionCache using the provided the SSL_CTX and + // the ssl_mode. The SSL_CTX will be up_refed. ssl_ctx cannot be nullptr, + // the constructor immediately dchecks this. + OpenSSLSessionCache(SSLMode ssl_mode, SSL_CTX* ssl_ctx); + // Frees the cached SSL_SESSIONS and then frees the SSL_CTX. + ~OpenSSLSessionCache(); + + OpenSSLSessionCache(const OpenSSLSessionCache&) = delete; + OpenSSLSessionCache& operator=(const OpenSSLSessionCache&) = delete; + + // Looks up a session by hostname. The returned SSL_SESSION is not up_refed. + SSL_SESSION* LookupSession(absl::string_view hostname) const; + // Adds a session to the cache, and up_refs it. Any existing session with the + // same hostname is replaced. + void AddSession(absl::string_view hostname, SSL_SESSION* session); + // Returns the true underlying SSL Context that holds these cached sessions. + SSL_CTX* GetSSLContext() const; + // The SSL Mode tht the OpenSSLSessionCache was constructed with. This cannot + // be changed after launch. + SSLMode GetSSLMode() const; + + private: + // Holds the SSL Mode that the OpenSSLCache was initialized with. This is + // immutable after creation and cannot change. + const SSLMode ssl_mode_; + /// SSL Context for all shared cached sessions. This SSL_CTX is initialized + // with SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT); Meaning + // all client sessions will be added to the cache internal to the context. + SSL_CTX* ssl_ctx_ = nullptr; + // Map of hostnames to SSL_SESSIONs; holds references to the SSL_SESSIONs, + // which are cleaned up when the factory is destroyed. + // TODO(juberti): Add LRU eviction to keep the cache from growing forever. + std::map sessions_; + // The cache should never be copied or assigned directly. +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::OpenSSLSessionCache; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_OPENSSL_SESSION_CACHE_H_ diff --git a/pkg/apm/webrtc/rtc_base/openssl_stream_adapter.h b/pkg/apm/webrtc/rtc_base/openssl_stream_adapter.h new file mode 100644 index 00000000..4259f8c1 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/openssl_stream_adapter.h @@ -0,0 +1,280 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_OPENSSL_STREAM_ADAPTER_H_ +#define RTC_BASE_OPENSSL_STREAM_ADAPTER_H_ + +#include +#include +#include + +#include +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "rtc_base/buffer.h" +#include "rtc_base/ssl_certificate.h" +#ifdef OPENSSL_IS_BORINGSSL +#include "rtc_base/boringssl_identity.h" +#include "rtc_base/openssl.h" +#else +#include "rtc_base/openssl_identity.h" +#endif +#include "api/field_trials_view.h" +#include "api/task_queue/pending_task_safety_flag.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/stream.h" +#include "rtc_base/task_utils/repeating_task.h" +#include "rtc_base/thread.h" + +namespace webrtc { + +// This class was written with OpenSSLAdapter (a socket adapter) as a +// starting point. It has similar structure and functionality, but uses a +// "peer-to-peer" mode, verifying the peer's certificate using a digest +// sent over a secure signaling channel. +// +// Static methods to initialize and deinit the SSL library are in +// OpenSSLAdapter. These should probably be moved out to a neutral class. +// +// In a few cases I have factored out some OpenSSLAdapter code into static +// methods so it can be reused from this class. Eventually that code should +// probably be moved to a common support class. Unfortunately there remain a +// few duplicated sections of code. I have not done more restructuring because +// I did not want to affect existing code that uses OpenSSLAdapter. +// +// This class does not support the SSL connection restart feature present in +// OpenSSLAdapter. I am not entirely sure how the feature is useful and I am +// not convinced that it works properly. +// +// This implementation is careful to disallow data exchange after an SSL error, +// and it has an explicit SSL_CLOSED state. It should not be possible to send +// any data in clear after one of the StartSSL methods has been called. + +// Look in ssl_stream_adapter.h for documentation of the methods. + +/////////////////////////////////////////////////////////////////////////////// + +class OpenSSLStreamAdapter final : public SSLStreamAdapter { + public: + OpenSSLStreamAdapter( + std::unique_ptr stream, + absl::AnyInvocable handshake_error, + const FieldTrialsView* field_trials = nullptr); + ~OpenSSLStreamAdapter() override; + + void SetIdentity(std::unique_ptr identity) override; + SSLIdentity* GetIdentityForTesting() const override; + + // Default argument is for compatibility + void SetServerRole(SSLRole role = webrtc::SSL_SERVER) override; + SSLPeerCertificateDigestError SetPeerCertificateDigest( + absl::string_view digest_alg, + ArrayView digest_val) override; + + std::unique_ptr GetPeerSSLCertChain() const override; + + // Goes from state SSL_NONE to either SSL_CONNECTING or SSL_WAIT, depending + // on whether the underlying stream is already open or not. + int StartSSL() override; + [[deprecated]] void SetMode(SSLMode mode) override; + void SetMaxProtocolVersion(SSLProtocolVersion version) override; + void SetInitialRetransmissionTimeout(int timeout_ms) override; + void SetMTU(int mtu) override; + + StreamResult Read(ArrayView data, size_t& read, int& error) override; + StreamResult Write(ArrayView data, + size_t& written, + int& error) override; + void Close() override; + StreamState GetState() const override; + + std::optional GetTlsCipherSuiteName() const override; + + bool GetSslCipherSuite(int* cipher) const override; + [[deprecated("Use GetSslVersionBytes")]] SSLProtocolVersion GetSslVersion() + const override; + bool GetSslVersionBytes(int* version) const override; + // Key Extractor interface + bool ExportSrtpKeyingMaterial( + ZeroOnFreeBuffer& keying_material) override; + + uint16_t GetPeerSignatureAlgorithm() const override; + + // DTLS-SRTP interface + bool SetDtlsSrtpCryptoSuites(const std::vector& crypto_suites) override; + bool GetDtlsSrtpCryptoSuite(int* crypto_suite) const override; + + bool IsTlsConnected() override; + + // Capabilities interfaces. + static bool IsBoringSsl(); + + static bool IsAcceptableCipher(int cipher, KeyType key_type); + static bool IsAcceptableCipher(absl::string_view cipher, KeyType key_type); + + // Use our timeutils.h source of timing in BoringSSL, allowing us to test + // using a fake clock. + static void EnableTimeCallbackForTesting(); + + // Return max DTLS SSLProtocolVersion supported by implementation. + static SSLProtocolVersion GetMaxSupportedDTLSProtocolVersion(); + + // Return number of times DTLS retransmission has been triggered. + // Used for testing (and maybe put into stats?). + int GetRetransmissionCount() const override { return retransmission_count_; } + + // Return the the ID of the group used by the adapters most recently + // completed handshake, or 0 if not applicable (e.g. before the handshake). + uint16_t GetSslGroupIdForTesting() const override; + + private: + enum SSLState { + // Before calling one of the StartSSL methods, data flows + // in clear text. + SSL_NONE, + SSL_WAIT, // waiting for the stream to open to start SSL negotiation + SSL_CONNECTING, // SSL negotiation in progress + SSL_CONNECTED, // SSL stream successfully established + SSL_ERROR, // some SSL error occurred, stream is closed + SSL_CLOSED // Clean close + }; + + void OnEvent(int events, int err); + + void PostEvent(int events, int err); + void SetTimeout(int delay_ms); + + // The following three methods return 0 on success and a negative + // error code on failure. The error code may be from OpenSSL or -1 + // on some other error cases, so it can't really be interpreted + // unfortunately. + + // Prepare SSL library, state is SSL_CONNECTING. + int BeginSSL(); + // Perform SSL negotiation steps. + int ContinueSSL(); + + // Error handler helper. signal is given as true for errors in + // asynchronous contexts (when an error method was not returned + // through some other method), and in that case an SE_CLOSE event is + // raised on the stream with the specified error. + // A 0 error means a graceful close, otherwise there is not really enough + // context to interpret the error code. + // `alert` indicates an alert description (one of the SSL_AD constants) to + // send to the remote endpoint when closing the association. If 0, a normal + // shutdown will be performed. + void Error(absl::string_view context, int err, uint8_t alert, bool signal); + void Cleanup(uint8_t alert); + + // Flush the input buffers by reading left bytes (for DTLS) + void FlushInput(unsigned int left); + + // SSL library configuration + SSL_CTX* SetupSSLContext(); + // Verify the peer certificate matches the signaled digest. + bool VerifyPeerCertificate(); + +#ifdef OPENSSL_IS_BORINGSSL + // SSL certificate verification callback. See SSL_CTX_set_custom_verify. + static enum ssl_verify_result_t SSLVerifyCallback(SSL* ssl, + uint8_t* out_alert); +#else + // SSL certificate verification callback. See + // SSL_CTX_set_cert_verify_callback. + static int SSLVerifyCallback(X509_STORE_CTX* store, void* arg); +#endif + + bool WaitingToVerifyPeerCertificate() const { + return GetClientAuthEnabled() && !peer_certificate_verified_; + } + + bool HasPeerCertificateDigest() const { + return !peer_certificate_digest_algorithm_.empty() && + !peer_certificate_digest_value_.empty(); + } + + const std::unique_ptr stream_; + absl::AnyInvocable handshake_error_; + + Thread* const owner_; + ScopedTaskSafety task_safety_; + RepeatingTaskHandle timeout_task_; + + SSLState state_; + SSLRole role_; + int ssl_error_code_; // valid when state_ == SSL_ERROR or SSL_CLOSED + // Whether the SSL negotiation is blocked on needing to read or + // write to the wrapped stream. + bool ssl_read_needs_write_; + bool ssl_write_needs_read_; + + SSL* ssl_; + SSL_CTX* ssl_ctx_; + + // Our key and certificate. +#ifdef OPENSSL_IS_BORINGSSL + std::unique_ptr identity_; +#else + std::unique_ptr identity_; +#endif + // The certificate chain that the peer presented. Initially null, until the + // connection is established. + std::unique_ptr peer_cert_chain_; + bool peer_certificate_verified_ = false; + // The digest of the certificate that the peer must present. + Buffer peer_certificate_digest_value_; + std::string peer_certificate_digest_algorithm_; + + // The DtlsSrtp ciphers + std::string srtp_ciphers_; + + // Do DTLS or not + SSLMode ssl_mode_; + + // Max. allowed protocol version + SSLProtocolVersion ssl_max_version_; + + // A 50-ms initial timeout ensures rapid setup on fast connections, but may + // be too aggressive for low bandwidth links. + int dtls_handshake_timeout_ms_ = 50; + + // MTU configured for dtls. + int dtls_mtu_ = 1200; + + // 0 == Disabled + // 1 == Max + // 2 == Enabled (both min and max) + const int force_dtls_13_ = 0; + + int retransmission_count_ = 0; + + // Experimental flag to enable Post-Quantum Cryptography TLS. + const bool enable_dtls_pqc_ = false; +}; + +///////////////////////////////////////////////////////////////////////////// + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::OpenSSLStreamAdapter; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_OPENSSL_STREAM_ADAPTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/openssl_utility.h b/pkg/apm/webrtc/rtc_base/openssl_utility.h new file mode 100644 index 00000000..d3f5b0e0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/openssl_utility.h @@ -0,0 +1,82 @@ +/* + * 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 RTC_BASE_OPENSSL_UTILITY_H_ +#define RTC_BASE_OPENSSL_UTILITY_H_ + +#include + +#include + +#include "absl/strings/string_view.h" + +namespace webrtc { +// The openssl namespace holds static helper methods. All methods related +// to OpenSSL that are commonly used and don't require global state should be +// placed here. +namespace openssl { + +#ifdef OPENSSL_IS_BORINGSSL +// Does minimal parsing of a certificate (only verifying the presence of major +// fields), primarily for the purpose of extracting the relevant out +// parameters. Any that the caller is uninterested in can be null. +bool ParseCertificate(CRYPTO_BUFFER* cert_buffer, + CBS* signature_algorithm_oid, + int64_t* expiration_time); +#endif + +// Verifies that the hostname provided matches that in the peer certificate +// attached to this SSL state. +// TODO(crbug.com/webrtc/11710): When OS certificate verification is available, +// skip compiling this as it adds a dependency on OpenSSL X509 objects, which we +// are trying to avoid in favor of CRYPTO_BUFFERs (see crbug.com/webrtc/11410). +bool VerifyPeerCertMatchesHost(SSL* ssl, absl::string_view host); + +// Logs all the errors in the OpenSSL errror queue from the current thread. A +// prefix can be provided for context. +void LogSSLErrors(absl::string_view prefix); + +#ifndef WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS +// Attempt to add the certificates from the loader into the SSL_CTX. False is +// returned only if there are no certificates returned from the loader or none +// of them can be added to the TrustStore for the provided context. +bool LoadBuiltinSSLRootCertificates(SSL_CTX* ssl_ctx); +#endif // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS + +#ifdef OPENSSL_IS_BORINGSSL +CRYPTO_BUFFER_POOL* GetBufferPool(); +#endif + +} // namespace openssl +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +namespace openssl { + +#ifndef WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS +using ::webrtc::openssl::LoadBuiltinSSLRootCertificates; +#endif + +using ::webrtc::openssl::LogSSLErrors; +using ::webrtc::openssl::VerifyPeerCertMatchesHost; + +#ifdef OPENSSL_IS_BORINGSSL +using ::webrtc::openssl::GetBufferPool; +using ::webrtc::openssl::ParseCertificate; +#endif + +} // namespace openssl +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_OPENSSL_UTILITY_H_ diff --git a/pkg/apm/webrtc/rtc_base/operations_chain.h b/pkg/apm/webrtc/rtc_base/operations_chain.h new file mode 100644 index 00000000..7c83b0a0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/operations_chain.h @@ -0,0 +1,209 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_OPERATIONS_CHAIN_H_ +#define RTC_BASE_OPERATIONS_CHAIN_H_ + +#include +#include +#include +#include +#include +#include + +#include "api/ref_counted_base.h" +#include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +namespace rtc_operations_chain_internal { + +// Abstract base class for operations on the OperationsChain. Run() must be +// invoked exactly once during the Operation's lifespan. +class Operation { + public: + virtual ~Operation() {} + + virtual void Run() = 0; +}; + +// FunctorT is the same as in OperationsChain::ChainOperation(). `callback_` is +// passed on to the `functor_` and is used to inform the OperationsChain that +// the operation completed. The functor is responsible for invoking the +// callback when the operation has completed. +template +class OperationWithFunctor final : public Operation { + public: + OperationWithFunctor(FunctorT&& functor, std::function callback) + : functor_(std::forward(functor)), + callback_(std::move(callback)) {} + + ~OperationWithFunctor() override { +#if RTC_DCHECK_IS_ON + RTC_DCHECK(has_run_); +#endif // RTC_DCHECK_IS_ON + } + + void Run() override { +#if RTC_DCHECK_IS_ON + RTC_DCHECK(!has_run_); + has_run_ = true; +#endif // RTC_DCHECK_IS_ON + // The functor being executed may invoke the callback synchronously, + // marking the operation as complete. As such, `this` OperationWithFunctor + // object may get deleted here, including destroying `functor_`. To + // protect the functor from self-destruction while running, it is moved to + // a local variable. + auto functor = std::move(functor_); + functor(std::move(callback_)); + // `this` may now be deleted; don't touch any member variables. + } + + private: + typename std::remove_reference::type functor_; + std::function callback_; +#if RTC_DCHECK_IS_ON + bool has_run_ = false; +#endif // RTC_DCHECK_IS_ON +}; + +} // namespace rtc_operations_chain_internal + +// An implementation of an operations chain. An operations chain is used to +// ensure that asynchronous tasks are executed in-order with at most one task +// running at a time. The notion of an operation chain is defined in +// https://w3c.github.io/webrtc-pc/#dfn-operations-chain, though unlike this +// implementation, the referenced definition is coupled with a peer connection. +// +// An operation is an asynchronous task. The operation starts when its functor +// is invoked, and completes when the callback that is passed to functor is +// invoked by the operation. The operation must start and complete on the same +// sequence that the operation was "chained" on. As such, the OperationsChain +// operates in a "single-threaded" fashion, but the asynchronous operations may +// use any number of threads to achieve "in parallel" behavior. +// +// When an operation is chained onto the OperationsChain, it is enqueued to be +// executed. Operations are executed in FIFO order, where the next operation +// does not start until the previous operation has completed. OperationsChain +// guarantees that: +// - If the operations chain is empty when an operation is chained, the +// operation starts immediately, inside ChainOperation(). +// - If the operations chain is not empty when an operation is chained, the +// operation starts upon the previous operation completing, inside the +// callback. +// +// An operation is contractually obligated to invoke the completion callback +// exactly once. Cancelling a chained operation is not supported by the +// OperationsChain; an operation that wants to be cancellable is responsible for +// aborting its own steps. The callback must still be invoked. +// +// The OperationsChain is kept-alive through reference counting if there are +// operations pending. This, together with the contract, guarantees that all +// operations that are chained get executed. +class OperationsChain final : public RefCountedNonVirtual { + public: + static scoped_refptr Create(); + ~OperationsChain(); + + OperationsChain(const OperationsChain&) = delete; + OperationsChain& operator=(const OperationsChain&) = delete; + + void SetOnChainEmptyCallback(std::function on_chain_empty_callback); + bool IsEmpty() const; + + // Chains an operation. Chained operations are executed in FIFO order. The + // operation starts when `functor` is executed by the OperationsChain and is + // contractually obligated to invoke the callback passed to it when the + // operation is complete. Operations must start and complete on the same + // sequence that this method was invoked on. + // + // If the OperationsChain is empty, the operation starts immediately. + // Otherwise it starts upon the previous operation completing. + // + // Requirements of FunctorT: + // - FunctorT is movable. + // - FunctorT implements "T operator()(std::function callback)" or + // "T operator()(std::function callback) const" for some T (if T is + // not void, the return value is discarded in the invoking sequence). The + // operator starts the operation; when the operation is complete, "callback" + // MUST be invoked, and it MUST be so on the sequence that ChainOperation() + // was invoked on. + // + // Lambda expressions are valid functors. + template + void ChainOperation(FunctorT&& functor) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + chained_operations_.push( + std::make_unique< + rtc_operations_chain_internal::OperationWithFunctor>( + std::forward(functor), CreateOperationsChainCallback())); + // If this is the only operation in the chain we execute it immediately. + // Otherwise the callback will get invoked when the pending operation + // completes which will trigger the next operation to execute. + if (chained_operations_.size() == 1) { + chained_operations_.front()->Run(); + } + } + + private: + friend class CallbackHandle; + + // The callback that is passed to an operation's functor (that is used to + // inform the OperationsChain that the operation has completed) is of type + // std::function, which is a copyable type. To allow the callback to + // be copyable, it is backed up by this reference counted handle. See + // CreateOperationsChainCallback(). + class CallbackHandle final : public RefCountedNonVirtual { + public: + explicit CallbackHandle(scoped_refptr operations_chain); + ~CallbackHandle(); + + CallbackHandle(const CallbackHandle&) = delete; + CallbackHandle& operator=(const CallbackHandle&) = delete; + + void OnOperationComplete(); + + private: + scoped_refptr operations_chain_; +#if RTC_DCHECK_IS_ON + bool has_run_ = false; +#endif // RTC_DCHECK_IS_ON + }; + + OperationsChain(); + + std::function CreateOperationsChainCallback(); + void OnOperationComplete(); + + RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_; + // FIFO-list of operations that are chained. An operation that is executing + // remains on this list until it has completed by invoking the callback passed + // to it. + std::queue> + chained_operations_ RTC_GUARDED_BY(sequence_checker_); + std::optional> on_chain_empty_callback_ + RTC_GUARDED_BY(sequence_checker_); +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::OperationsChain; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_OPERATIONS_CHAIN_H_ diff --git a/pkg/apm/webrtc/rtc_base/physical_socket_server.h b/pkg/apm/webrtc/rtc_base/physical_socket_server.h new file mode 100644 index 00000000..3c9eca55 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/physical_socket_server.h @@ -0,0 +1,329 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_PHYSICAL_SOCKET_SERVER_H_ +#define RTC_BASE_PHYSICAL_SOCKET_SERVER_H_ + +#include + +#include "api/async_dns_resolver.h" +#include "api/transport/ecn_marking.h" +#include "api/units/time_delta.h" +#include "rtc_base/socket.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/third_party/sigslot/sigslot.h" + +#if defined(WEBRTC_POSIX) +#if defined(WEBRTC_LINUX) +// On Linux, use epoll. +#include + +#define WEBRTC_USE_EPOLL 1 +#elif defined(WEBRTC_FUCHSIA) || defined(WEBRTC_MAC) +// Fuchsia implements select and poll but not epoll, and testing shows that poll +// is faster than select. +#include + +#define WEBRTC_USE_POLL 1 +#else +// On other POSIX systems, use select by default. +#endif // WEBRTC_LINUX, WEBRTC_FUCHSIA, WEBRTC_MAC +#endif // WEBRTC_POSIX + +#include +#include +#include +#include +#include +#include + +#include "rtc_base/deprecated/recursive_critical_section.h" +#include "rtc_base/socket_server.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread_annotations.h" + +#if defined(WEBRTC_POSIX) +typedef int SOCKET; +#endif // WEBRTC_POSIX + +namespace webrtc { + +class Signaler; + +// Event constants for the Dispatcher class. +enum DispatcherEvent { + DE_READ = 0x0001, + DE_WRITE = 0x0002, + DE_CONNECT = 0x0004, + DE_CLOSE = 0x0008, + DE_ACCEPT = 0x0010, +}; + +class Dispatcher { + public: + virtual ~Dispatcher() {} + virtual uint32_t GetRequestedEvents() = 0; + virtual void OnEvent(uint32_t ff, int err) = 0; +#if defined(WEBRTC_WIN) + virtual WSAEVENT GetWSAEvent() = 0; + virtual SOCKET GetSocket() = 0; + virtual bool CheckSignalClose() = 0; +#elif defined(WEBRTC_POSIX) + virtual int GetDescriptor() = 0; + virtual bool IsDescriptorClosed() = 0; +#endif +}; + +// A socket server that provides the real sockets of the underlying OS. +class RTC_EXPORT PhysicalSocketServer : public SocketServer { + public: + PhysicalSocketServer(); + ~PhysicalSocketServer() override; + + // SocketFactory: + Socket* CreateSocket(int family, int type) override; + + // Internal Factory for Accept (virtual so it can be overwritten in tests). + virtual Socket* WrapSocket(SOCKET s); + + // SocketServer: + bool Wait(TimeDelta max_wait_duration, bool process_io) override; + void WakeUp() override; + + void Add(Dispatcher* dispatcher); + void Remove(Dispatcher* dispatcher); + void Update(Dispatcher* dispatcher); + + private: + // The number of events to process with one call to "epoll_wait". + static constexpr size_t kNumEpollEvents = 128; + // A local historical definition of "foreverness", in milliseconds. + static constexpr int kForeverMs = -1; + + static int ToCmsWait(TimeDelta max_wait_duration); + +#if defined(WEBRTC_POSIX) + bool WaitSelect(int cmsWait, bool process_io); + +#if defined(WEBRTC_USE_EPOLL) + void AddEpoll(Dispatcher* dispatcher, uint64_t key); + void RemoveEpoll(Dispatcher* dispatcher); + void UpdateEpoll(Dispatcher* dispatcher, uint64_t key); + bool WaitEpoll(int cmsWait); + bool WaitPollOneDispatcher(int cmsWait, Dispatcher* dispatcher); + + // This array is accessed in isolation by a thread calling into Wait(). + // It's useless to use a SequenceChecker to guard it because a socket + // server can outlive the thread it's bound to, forcing the Wait call + // to have to reset the sequence checker on Wait calls. + std::array epoll_events_; + const int epoll_fd_ = INVALID_SOCKET; + +#elif defined(WEBRTC_USE_POLL) + bool WaitPoll(int cmsWait, bool process_io); + +#endif // WEBRTC_USE_EPOLL, WEBRTC_USE_POLL +#endif // WEBRTC_POSIX + + // uint64_t keys are used to uniquely identify a dispatcher in order to avoid + // the ABA problem during the epoll loop (a dispatcher being destroyed and + // replaced by one with the same address). + uint64_t next_dispatcher_key_ RTC_GUARDED_BY(crit_) = 0; + std::unordered_map dispatcher_by_key_ + RTC_GUARDED_BY(crit_); + // Reverse lookup necessary for removals/updates. + std::unordered_map key_by_dispatcher_ + RTC_GUARDED_BY(crit_); + // A list of dispatcher keys that we're interested in for the current + // select(), poll(), or WSAWaitForMultipleEvents() loop. Again, used to avoid + // the ABA problem (a socket being destroyed and a new one created with the + // same handle, erroneously receiving the events from the destroyed socket). + // + // Kept as a member variable just for efficiency. + std::vector current_dispatcher_keys_; + Signaler* signal_wakeup_; // Assigned in constructor only + RecursiveCriticalSection crit_; +#if defined(WEBRTC_WIN) + const WSAEVENT socket_ev_; +#endif + bool fWait_; + // Are we currently in a select()/epoll()/WSAWaitForMultipleEvents loop? + // Used for a DCHECK, because we don't support reentrant waiting. + bool waiting_ = false; +}; + +class PhysicalSocket : public Socket, public sigslot::has_slots<> { + public: + PhysicalSocket(PhysicalSocketServer* ss, SOCKET s = INVALID_SOCKET); + ~PhysicalSocket() override; + + // Creates the underlying OS socket (same as the "socket" function). + virtual bool Create(int family, int type); + + SocketAddress GetLocalAddress() const override; + SocketAddress GetRemoteAddress() const override; + + int Bind(const SocketAddress& bind_addr) override; + int Connect(const SocketAddress& addr) override; + + int GetError() const override; + void SetError(int error) override; + + ConnState GetState() const override; + + int GetOption(Option opt, int* value) override; + int SetOption(Option opt, int value) override; + + int Send(const void* pv, size_t cb) override; + int SendTo(const void* buffer, + size_t length, + const SocketAddress& addr) override; + + int Recv(void* buffer, size_t length, int64_t* timestamp) override; + // TODO(webrtc:15368): Deprecate and remove. + int RecvFrom(void* buffer, + size_t length, + SocketAddress* out_addr, + int64_t* timestamp) override; + int RecvFrom(ReceiveBuffer& buffer) override; + + int Listen(int backlog) override; + Socket* Accept(SocketAddress* out_addr) override; + + int Close() override; + + SocketServer* socketserver() { return ss_; } + + SOCKET GetSocketFD() const { return s_; } + + protected: + int DoConnect(const SocketAddress& connect_addr); + + // Make virtual so ::accept can be overwritten in tests. + virtual SOCKET DoAccept(SOCKET socket, sockaddr* addr, socklen_t* addrlen); + + // Make virtual so ::send can be overwritten in tests. + virtual int DoSend(SOCKET socket, const char* buf, int len, int flags); + + // Make virtual so ::sendto can be overwritten in tests. + virtual int DoSendTo(SOCKET socket, + const char* buf, + int len, + int flags, + const struct sockaddr* dest_addr, + socklen_t addrlen); + + int DoReadFromSocket(void* buffer, + size_t length, + SocketAddress* out_addr, + int64_t* timestamp, + EcnMarking* ecn); + + void OnResolveResult(const AsyncDnsResolverResult& resolver); + + void UpdateLastError(); + void MaybeRemapSendError(); + + uint8_t enabled_events() const { return enabled_events_; } + virtual void SetEnabledEvents(uint8_t events); + virtual void EnableEvents(uint8_t events); + virtual void DisableEvents(uint8_t events); + + int TranslateOption(Option opt, int* slevel, int* sopt); + + PhysicalSocketServer* ss_; + SOCKET s_; + bool udp_; + int family_ = 0; + mutable Mutex mutex_; + int error_ RTC_GUARDED_BY(mutex_); + ConnState state_; + std::unique_ptr resolver_; + uint8_t dscp_ = 0; // 6bit. + uint8_t ecn_ = 0; // 2bits. + +#if !defined(NDEBUG) + std::string dbg_addr_; +#endif + + private: + uint8_t enabled_events_ = 0; +}; + +class SocketDispatcher : public Dispatcher, public PhysicalSocket { + public: + explicit SocketDispatcher(PhysicalSocketServer* ss); + SocketDispatcher(SOCKET s, PhysicalSocketServer* ss); + ~SocketDispatcher() override; + + bool Initialize(); + + virtual bool Create(int type); + bool Create(int family, int type) override; + +#if defined(WEBRTC_WIN) + WSAEVENT GetWSAEvent() override; + SOCKET GetSocket() override; + bool CheckSignalClose() override; +#elif defined(WEBRTC_POSIX) + int GetDescriptor() override; + bool IsDescriptorClosed() override; +#endif + + uint32_t GetRequestedEvents() override; + void OnEvent(uint32_t ff, int err) override; + + int Close() override; + +#if defined(WEBRTC_USE_EPOLL) + protected: + void StartBatchedEventUpdates(); + void FinishBatchedEventUpdates(); + + void SetEnabledEvents(uint8_t events) override; + void EnableEvents(uint8_t events) override; + void DisableEvents(uint8_t events) override; +#endif + + private: +#if defined(WEBRTC_WIN) + static int next_id_; + int id_; + bool signal_close_; + int signal_err_; +#endif // WEBRTC_WIN +#if defined(WEBRTC_USE_EPOLL) + void MaybeUpdateDispatcher(uint8_t old_events); + + int saved_enabled_events_ = -1; +#endif +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::DE_ACCEPT; +using ::webrtc::DE_CLOSE; +using ::webrtc::DE_CONNECT; +using ::webrtc::DE_READ; +using ::webrtc::DE_WRITE; +using ::webrtc::Dispatcher; +using ::webrtc::DispatcherEvent; +using ::webrtc::PhysicalSocket; +using ::webrtc::PhysicalSocketServer; +using ::webrtc::SocketDispatcher; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_PHYSICAL_SOCKET_SERVER_H_ diff --git a/pkg/apm/webrtc/rtc_base/platform_thread.cc b/pkg/apm/webrtc/rtc_base/platform_thread.cc new file mode 100644 index 00000000..53381a4d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/platform_thread.cc @@ -0,0 +1,219 @@ +/* + * 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 "rtc_base/platform_thread.h" + +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/platform_thread_types.h" + +#if !defined(WEBRTC_WIN) +#include +#endif + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +#if defined(WEBRTC_WIN) +int Win32PriorityFromThreadPriority(ThreadPriority priority) { + switch (priority) { + case ThreadPriority::kLow: + return THREAD_PRIORITY_BELOW_NORMAL; + case ThreadPriority::kNormal: + return THREAD_PRIORITY_NORMAL; + case ThreadPriority::kHigh: + return THREAD_PRIORITY_ABOVE_NORMAL; + case ThreadPriority::kRealtime: + return THREAD_PRIORITY_TIME_CRITICAL; + } +} +#endif + +bool SetPriority(ThreadPriority priority) { +#if defined(WEBRTC_WIN) + return SetThreadPriority(GetCurrentThread(), + Win32PriorityFromThreadPriority(priority)) != FALSE; +#elif defined(__native_client__) || defined(WEBRTC_FUCHSIA) || \ + (defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_PTHREADS__)) + // Setting thread priorities is not supported in NaCl, Fuchsia or Emscripten + // without pthreads. + return true; +#elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX) + // TODO(tommi): Switch to the same mechanism as Chromium uses for changing + // thread priorities. + return true; +#else + const int policy = SCHED_FIFO; + const int min_prio = sched_get_priority_min(policy); + const int max_prio = sched_get_priority_max(policy); + if (min_prio == -1 || max_prio == -1) { + return false; + } + + if (max_prio - min_prio <= 2) + return false; + + // Convert webrtc priority to system priorities: + sched_param param; + const int top_prio = max_prio - 1; + const int low_prio = min_prio + 1; + switch (priority) { + case ThreadPriority::kLow: + param.sched_priority = low_prio; + break; + case ThreadPriority::kNormal: + // The -1 ensures that the kHighPriority is always greater or equal to + // kNormalPriority. + param.sched_priority = (low_prio + top_prio - 1) / 2; + break; + case ThreadPriority::kHigh: + param.sched_priority = std::max(top_prio - 2, low_prio); + break; + case ThreadPriority::kRealtime: + param.sched_priority = top_prio; + break; + } + return pthread_setschedparam(pthread_self(), policy, ¶m) == 0; +#endif // defined(WEBRTC_WIN) +} + +#if defined(WEBRTC_WIN) +DWORD WINAPI RunPlatformThread(void* param) { + // The GetLastError() function only returns valid results when it is called + // after a Win32 API function that returns a "failed" result. A crash dump + // contains the result from GetLastError() and to make sure it does not + // falsely report a Windows error we call SetLastError here. + ::SetLastError(ERROR_SUCCESS); + auto function = static_cast*>(param); + (*function)(); + delete function; + return 0; +} +#else +void* RunPlatformThread(void* param) { + auto function = static_cast*>(param); + (*function)(); + delete function; + return 0; +} +#endif // defined(WEBRTC_WIN) + +} // namespace + +PlatformThread::PlatformThread(Handle handle, bool joinable) + : handle_(handle), joinable_(joinable) {} + +PlatformThread::PlatformThread(PlatformThread&& rhs) + : handle_(rhs.handle_), joinable_(rhs.joinable_) { + rhs.handle_ = std::nullopt; +} + +PlatformThread& PlatformThread::operator=(PlatformThread&& rhs) { + Finalize(); + handle_ = rhs.handle_; + joinable_ = rhs.joinable_; + rhs.handle_ = std::nullopt; + return *this; +} + +PlatformThread::~PlatformThread() { + Finalize(); +} + +PlatformThread PlatformThread::SpawnJoinable( + std::function thread_function, + absl::string_view name, + ThreadAttributes attributes) { + return SpawnThread(std::move(thread_function), name, attributes, + /*joinable=*/true); +} + +PlatformThread PlatformThread::SpawnDetached( + std::function thread_function, + absl::string_view name, + ThreadAttributes attributes) { + return SpawnThread(std::move(thread_function), name, attributes, + /*joinable=*/false); +} + +std::optional PlatformThread::GetHandle() const { + return handle_; +} + +#if defined(WEBRTC_WIN) +bool PlatformThread::QueueAPC(PAPCFUNC function, ULONG_PTR data) { + RTC_DCHECK(handle_.has_value()); + return handle_.has_value() ? QueueUserAPC(function, *handle_, data) != FALSE + : false; +} +#endif + +void PlatformThread::Finalize() { + if (!handle_.has_value()) + return; +#if defined(WEBRTC_WIN) + if (joinable_) + WaitForSingleObject(*handle_, INFINITE); + CloseHandle(*handle_); +#else + if (joinable_) + RTC_CHECK_EQ(0, pthread_join(*handle_, nullptr)); +#endif + handle_ = std::nullopt; +} + +PlatformThread PlatformThread::SpawnThread( + std::function thread_function, + absl::string_view name, + ThreadAttributes attributes, + bool joinable) { + RTC_DCHECK(thread_function); + RTC_DCHECK(!name.empty()); + // TODO(tommi): Consider lowering the limit to 15 (limit on Linux). + RTC_DCHECK(name.length() < 64); + auto start_thread_function_ptr = + new std::function([thread_function = std::move(thread_function), + name = std::string(name), attributes] { + SetCurrentThreadName(name.c_str()); + SetPriority(attributes.priority); + thread_function(); + }); +#if defined(WEBRTC_WIN) + // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION. + // Set the reserved stack stack size to 1M, which is the default on Windows + // and Linux. + DWORD thread_id = 0; + PlatformThread::Handle handle = ::CreateThread( + nullptr, 1024 * 1024, &RunPlatformThread, start_thread_function_ptr, + STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id); + RTC_CHECK(handle) << "CreateThread failed"; +#else + pthread_attr_t attr; + pthread_attr_init(&attr); + // Set the stack stack size to 1M. + pthread_attr_setstacksize(&attr, 1024 * 1024); + pthread_attr_setdetachstate( + &attr, joinable ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED); + PlatformThread::Handle handle; + RTC_CHECK_EQ(0, pthread_create(&handle, &attr, &RunPlatformThread, + start_thread_function_ptr)); + pthread_attr_destroy(&attr); +#endif // defined(WEBRTC_WIN) + return PlatformThread(handle, joinable); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/platform_thread.h b/pkg/apm/webrtc/rtc_base/platform_thread.h new file mode 100644 index 00000000..df7ca46f --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/platform_thread.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_PLATFORM_THREAD_H_ +#define RTC_BASE_PLATFORM_THREAD_H_ + +#include +#include +#if !defined(WEBRTC_WIN) +#include +#endif + +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/platform_thread_types.h" + +namespace webrtc { + +enum class ThreadPriority { + kLow = 1, + kNormal, + kHigh, + kRealtime, +}; + +struct ThreadAttributes { + ThreadPriority priority = ThreadPriority::kNormal; + ThreadAttributes& SetPriority(ThreadPriority priority_param) { + priority = priority_param; + return *this; + } +}; + +// Represents a simple worker thread. +class PlatformThread final { + public: + // Handle is the base platform thread handle. +#if defined(WEBRTC_WIN) + using Handle = HANDLE; +#else + using Handle = pthread_t; +#endif // defined(WEBRTC_WIN) + // This ctor creates the PlatformThread with an unset handle (returning true + // in empty()) and is provided for convenience. + // TODO(bugs.webrtc.org/12727) Look into if default and move support can be + // removed. + PlatformThread() = default; + + // Moves `rhs` into this, storing an empty state in `rhs`. + // TODO(bugs.webrtc.org/12727) Look into if default and move support can be + // removed. + PlatformThread(PlatformThread&& rhs); + + // Copies won't work since we'd have problems with joinable threads. + PlatformThread(const PlatformThread&) = delete; + PlatformThread& operator=(const PlatformThread&) = delete; + + // Moves `rhs` into this, storing an empty state in `rhs`. + // TODO(bugs.webrtc.org/12727) Look into if default and move support can be + // removed. + PlatformThread& operator=(PlatformThread&& rhs); + + // For a PlatformThread that's been spawned joinable, the destructor suspends + // the calling thread until the created thread exits unless the thread has + // already exited. + ~PlatformThread(); + + // Finalizes any allocated resources. + // For a PlatformThread that's been spawned joinable, Finalize() suspends + // the calling thread until the created thread exits unless the thread has + // already exited. + // empty() returns true after completion. + void Finalize(); + + // Returns true if default constructed, moved from, or Finalize()ed. + bool empty() const { return !handle_.has_value(); } + + // Creates a started joinable thread which will be joined when the returned + // PlatformThread destructs or Finalize() is called. + static PlatformThread SpawnJoinable( + std::function thread_function, + absl::string_view name, + ThreadAttributes attributes = ThreadAttributes()); + + // Creates a started detached thread. The caller has to use external + // synchronization as nothing is provided by the PlatformThread construct. + static PlatformThread SpawnDetached( + std::function thread_function, + absl::string_view name, + ThreadAttributes attributes = ThreadAttributes()); + + // Returns the base platform thread handle of this thread. + std::optional GetHandle() const; + +#if defined(WEBRTC_WIN) + // Queue a Windows APC function that runs when the thread is alertable. + bool QueueAPC(PAPCFUNC apc_function, ULONG_PTR data); +#endif + + private: + PlatformThread(Handle handle, bool joinable); + static PlatformThread SpawnThread(std::function thread_function, + absl::string_view name, + ThreadAttributes attributes, + bool joinable); + + std::optional handle_; + bool joinable_ = false; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::PlatformThread; +using ::webrtc::ThreadAttributes; +using ::webrtc::ThreadPriority; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_PLATFORM_THREAD_H_ diff --git a/pkg/apm/webrtc/rtc_base/platform_thread_types.cc b/pkg/apm/webrtc/rtc_base/platform_thread_types.cc new file mode 100644 index 00000000..9b6e7a13 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/platform_thread_types.cc @@ -0,0 +1,128 @@ +/* + * 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 "rtc_base/platform_thread_types.h" + +// IWYU pragma: begin_keep +#if defined(WEBRTC_LINUX) +#include +#include +#endif + +#if defined(WEBRTC_WIN) +#include "rtc_base/arraysize.h" + +// The SetThreadDescription API was brought in version 1607 of Windows 10. +// For compatibility with various versions of winuser and avoid clashing with +// a potentially defined type, we use the RTC_ prefix. +typedef HRESULT(WINAPI* RTC_SetThreadDescription)(HANDLE hThread, + PCWSTR lpThreadDescription); +#endif + +#if defined(WEBRTC_FUCHSIA) +#include +#include + +#include "rtc_base/checks.h" +#endif +// IWYU pragma: end_keep + +namespace webrtc { + +PlatformThreadId CurrentThreadId() { +#if defined(WEBRTC_WIN) + return GetCurrentThreadId(); +#elif defined(WEBRTC_POSIX) +#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) + return pthread_mach_thread_np(pthread_self()); +#elif defined(WEBRTC_ANDROID) + return gettid(); +#elif defined(WEBRTC_FUCHSIA) + return zx_thread_self(); +#elif defined(WEBRTC_LINUX) + return syscall(__NR_gettid); +#elif defined(__EMSCRIPTEN__) + return static_cast(pthread_self()); +#else + // Default implementation for nacl and solaris. + return reinterpret_cast(pthread_self()); +#endif +#endif // defined(WEBRTC_POSIX) +} + +PlatformThreadRef CurrentThreadRef() { +#if defined(WEBRTC_WIN) + return GetCurrentThreadId(); +#elif defined(WEBRTC_FUCHSIA) + return zx_thread_self(); +#elif defined(WEBRTC_POSIX) + return pthread_self(); +#endif +} + +bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b) { +#if defined(WEBRTC_WIN) || defined(WEBRTC_FUCHSIA) + return a == b; +#elif defined(WEBRTC_POSIX) + return pthread_equal(a, b); +#endif +} + +void SetCurrentThreadName(const char* name) { +#if defined(WEBRTC_WIN) + // The SetThreadDescription API works even if no debugger is attached. + // The names set with this API also show up in ETW traces. Very handy. + static auto set_thread_description_func = + reinterpret_cast(::GetProcAddress( + ::GetModuleHandleA("Kernel32.dll"), "SetThreadDescription")); + if (set_thread_description_func) { + // Convert from ASCII to UTF-16. + wchar_t wide_thread_name[64]; + for (size_t i = 0; i < arraysize(wide_thread_name) - 1; ++i) { + wide_thread_name[i] = name[i]; + if (wide_thread_name[i] == L'\0') + break; + } + // Guarantee null-termination. + wide_thread_name[arraysize(wide_thread_name) - 1] = L'\0'; + set_thread_description_func(::GetCurrentThread(), wide_thread_name); + } + + // For details see: + // https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code +#pragma pack(push, 8) + struct { + DWORD dwType; + LPCSTR szName; + DWORD dwThreadID; + DWORD dwFlags; + } threadname_info = {0x1000, name, static_cast(-1), 0}; +#pragma pack(pop) + +#pragma warning(push) +#pragma warning(disable : 6320 6322) + __try { + ::RaiseException(0x406D1388, 0, sizeof(threadname_info) / sizeof(ULONG_PTR), + reinterpret_cast(&threadname_info)); + } __except (EXCEPTION_EXECUTE_HANDLER) { // NOLINT + } +#pragma warning(pop) +#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID) + prctl(PR_SET_NAME, reinterpret_cast(name)); // NOLINT +#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS) + pthread_setname_np(name); +#elif defined(WEBRTC_FUCHSIA) + zx_status_t status = zx_object_set_property(zx_thread_self(), ZX_PROP_NAME, + name, strlen(name)); + RTC_DCHECK_EQ(status, ZX_OK); +#endif +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/platform_thread_types.h b/pkg/apm/webrtc/rtc_base/platform_thread_types.h new file mode 100644 index 00000000..603e46ff --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/platform_thread_types.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_PLATFORM_THREAD_TYPES_H_ +#define RTC_BASE_PLATFORM_THREAD_TYPES_H_ + +// IWYU pragma: begin_exports +// clang-format off +// clang formating would change include order. +#if defined(WEBRTC_WIN) +// Include winsock2.h before including to maintain consistency with +// win32.h. To include win32.h directly, it must be broken out into its own +// build target. +#include +#include +#elif defined(WEBRTC_FUCHSIA) +#include +#include +#elif defined(WEBRTC_POSIX) +#include +#include +#if defined(WEBRTC_MAC) +#include +#endif +#endif +// clang-format on +// IWYU pragma: end_exports + +namespace webrtc { +#if defined(WEBRTC_WIN) +typedef DWORD PlatformThreadId; +typedef DWORD PlatformThreadRef; +#elif defined(WEBRTC_FUCHSIA) +typedef zx_handle_t PlatformThreadId; +typedef zx_handle_t PlatformThreadRef; +#elif defined(WEBRTC_POSIX) +typedef pid_t PlatformThreadId; +typedef pthread_t PlatformThreadRef; +#endif + +// Retrieve the ID of the current thread. +PlatformThreadId CurrentThreadId(); + +// Retrieves a reference to the current thread. On Windows, this is the same +// as CurrentThreadId. On other platforms it's the pthread_t returned by +// pthread_self(). +PlatformThreadRef CurrentThreadRef(); + +// Compares two thread identifiers for equality. +bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b); + +// Sets the current thread name. +void SetCurrentThreadName(const char* name); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::CurrentThreadId; +using ::webrtc::CurrentThreadRef; +using ::webrtc::IsThreadRefEqual; +using ::webrtc::PlatformThreadId; +using ::webrtc::PlatformThreadRef; +using ::webrtc::SetCurrentThreadName; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_PLATFORM_THREAD_TYPES_H_ diff --git a/pkg/apm/webrtc/rtc_base/protobuf_utils.h b/pkg/apm/webrtc/rtc_base/protobuf_utils.h new file mode 100644 index 00000000..497b7f48 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/protobuf_utils.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#ifndef RTC_BASE_PROTOBUF_UTILS_H_ +#define RTC_BASE_PROTOBUF_UTILS_H_ + +#if WEBRTC_ENABLE_PROTOBUF + +#include "third_party/protobuf/src/google/protobuf/message_lite.h" // nogncheck +#include "third_party/protobuf/src/google/protobuf/repeated_field.h" // nogncheck + +namespace webrtc { + +using google::protobuf::MessageLite; +using google::protobuf::RepeatedPtrField; + +} // namespace webrtc + +#endif // WEBRTC_ENABLE_PROTOBUF + +#endif // RTC_BASE_PROTOBUF_UTILS_H_ diff --git a/pkg/apm/webrtc/rtc_base/proxy_server.h b/pkg/apm/webrtc/rtc_base/proxy_server.h new file mode 100644 index 00000000..ded2478d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/proxy_server.h @@ -0,0 +1,102 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_PROXY_SERVER_H_ +#define RTC_BASE_PROXY_SERVER_H_ + +#include +#include + +#include "rtc_base/memory/fifo_buffer.h" +#include "rtc_base/server_socket_adapters.h" +#include "rtc_base/socket.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/socket_factory.h" +#include "rtc_base/third_party/sigslot/sigslot.h" + +namespace webrtc { + +// ProxyServer is a base class that allows for easy construction of proxy +// servers. With its helper class ProxyBinding, it contains all the necessary +// logic for receiving and bridging connections. The specific client-server +// proxy protocol is implemented by an instance of the AsyncProxyServerSocket +// class; children of ProxyServer implement WrapSocket appropriately to return +// the correct protocol handler. + +class ProxyBinding : public sigslot::has_slots<> { + public: + ProxyBinding(AsyncProxyServerSocket* in_socket, Socket* out_socket); + ~ProxyBinding() override; + + ProxyBinding(const ProxyBinding&) = delete; + ProxyBinding& operator=(const ProxyBinding&) = delete; + + sigslot::signal1 SignalDestroyed; + + private: + void OnConnectRequest(AsyncProxyServerSocket* socket, + const SocketAddress& addr); + void OnInternalRead(Socket* socket); + void OnInternalWrite(Socket* socket); + void OnInternalClose(Socket* socket, int err); + void OnExternalConnect(Socket* socket); + void OnExternalRead(Socket* socket); + void OnExternalWrite(Socket* socket); + void OnExternalClose(Socket* socket, int err); + + static void Read(Socket* socket, FifoBuffer* buffer); + static void Write(Socket* socket, FifoBuffer* buffer); + void Destroy(); + + static const int kBufferSize = 4096; + std::unique_ptr int_socket_; + std::unique_ptr ext_socket_; + bool connected_; + FifoBuffer out_buffer_; + FifoBuffer in_buffer_; +}; + +class ProxyServer : public sigslot::has_slots<> { + public: + ProxyServer(SocketFactory* int_factory, + const SocketAddress& int_addr, + SocketFactory* ext_factory, + const SocketAddress& ext_ip); + ~ProxyServer() override; + + ProxyServer(const ProxyServer&) = delete; + ProxyServer& operator=(const ProxyServer&) = delete; + + // Returns the address to which the proxy server is bound + SocketAddress GetServerAddress(); + + protected: + void OnAcceptEvent(Socket* socket); + virtual AsyncProxyServerSocket* WrapSocket(Socket* socket) = 0; + + private: + SocketFactory* ext_factory_; + SocketAddress ext_ip_; + std::unique_ptr server_socket_; + std::vector> bindings_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::ProxyBinding; +using ::webrtc::ProxyServer; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_PROXY_SERVER_H_ diff --git a/pkg/apm/webrtc/rtc_base/race_checker.cc b/pkg/apm/webrtc/rtc_base/race_checker.cc new file mode 100644 index 00000000..e7359cc3 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/race_checker.cc @@ -0,0 +1,58 @@ +/* + * 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 "rtc_base/race_checker.h" + +#include "rtc_base/platform_thread_types.h" + +namespace webrtc { + +RaceChecker::RaceChecker() {} + +// Note that the implementation here is in itself racy, but we pretend it does +// not matter because we want this useful in release builds without having to +// pay the cost of using atomics. A race hitting the race checker is likely to +// cause access_count_ to diverge from zero and therefore cause the ThreadRef +// comparison to fail, signaling a race, although it may not be in the exact +// spot where a race *first* appeared in the code we're trying to protect. There +// is also a chance that an actual race is missed, however the probability of +// that has been considered small enough to be an acceptable trade off. +bool RaceChecker::Acquire() const { + const PlatformThreadRef current_thread = CurrentThreadRef(); + // Set new accessing thread if this is a new use. + const int current_access_count = access_count_; + access_count_ = access_count_ + 1; + if (current_access_count == 0) + accessing_thread_ = current_thread; + // If this is being used concurrently this check will fail for the second + // thread entering since it won't set the thread. Recursive use of checked + // methods are OK since the accessing thread remains the same. + const PlatformThreadRef accessing_thread = accessing_thread_; + return IsThreadRefEqual(accessing_thread, current_thread); +} + +void RaceChecker::Release() const { + access_count_ = access_count_ - 1; +} + +namespace internal { +RaceCheckerScope::RaceCheckerScope(const RaceChecker* race_checker) + : race_checker_(race_checker), race_check_ok_(race_checker->Acquire()) {} + +bool RaceCheckerScope::RaceDetected() const { + return !race_check_ok_; +} + +RaceCheckerScope::~RaceCheckerScope() { + race_checker_->Release(); +} + +} // namespace internal +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/race_checker.h b/pkg/apm/webrtc/rtc_base/race_checker.h new file mode 100644 index 00000000..4a0c594f --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/race_checker.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_RACE_CHECKER_H_ +#define RTC_BASE_RACE_CHECKER_H_ + +#include "rtc_base/checks.h" +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +namespace internal { +class RaceCheckerScope; +} // namespace internal + +// Best-effort race-checking implementation. This primitive uses no +// synchronization at all to be as-fast-as-possible in the non-racy case. +class RTC_LOCKABLE RaceChecker { + public: + friend class internal::RaceCheckerScope; + RaceChecker(); + + private: + bool Acquire() const RTC_EXCLUSIVE_LOCK_FUNCTION(); + void Release() const RTC_UNLOCK_FUNCTION(); + + // Volatile to prevent code being optimized away in Acquire()/Release(). + mutable volatile int access_count_ = 0; + mutable volatile PlatformThreadRef accessing_thread_; +}; + +namespace internal { +class RTC_SCOPED_LOCKABLE RaceCheckerScope { + public: + explicit RaceCheckerScope(const RaceChecker* race_checker) + RTC_EXCLUSIVE_LOCK_FUNCTION(race_checker); + + bool RaceDetected() const; + ~RaceCheckerScope() RTC_UNLOCK_FUNCTION(); + + private: + const RaceChecker* const race_checker_; + const bool race_check_ok_; +}; + +class RTC_SCOPED_LOCKABLE RaceCheckerScopeDoNothing { + public: + explicit RaceCheckerScopeDoNothing(const RaceChecker* race_checker) + RTC_EXCLUSIVE_LOCK_FUNCTION(race_checker) {} + + ~RaceCheckerScopeDoNothing() RTC_UNLOCK_FUNCTION() {} +}; + +} // namespace internal +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::RaceChecker; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#define RTC_CHECK_RUNS_SERIALIZED(x) RTC_CHECK_RUNS_SERIALIZED_NEXT(x, __LINE__) + +#define RTC_CHECK_RUNS_SERIALIZED_NEXT(x, suffix) \ + RTC_CHECK_RUNS_SERIALIZED_IMPL(x, suffix) + +#define RTC_CHECK_RUNS_SERIALIZED_IMPL(x, suffix) \ + webrtc::internal::RaceCheckerScope race_checker##suffix(x); \ + RTC_CHECK(!race_checker##suffix.RaceDetected()) + +#if RTC_DCHECK_IS_ON +#define RTC_DCHECK_RUNS_SERIALIZED(x) \ + webrtc::internal::RaceCheckerScope race_checker(x); \ + RTC_DCHECK(!race_checker.RaceDetected()) +#else +#define RTC_DCHECK_RUNS_SERIALIZED(x) \ + webrtc::internal::RaceCheckerScopeDoNothing race_checker(x) +#endif + +#endif // RTC_BASE_RACE_CHECKER_H_ diff --git a/pkg/apm/webrtc/rtc_base/random.cc b/pkg/apm/webrtc/rtc_base/random.cc new file mode 100644 index 00000000..55b8749c --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/random.cc @@ -0,0 +1,87 @@ +/* + * 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 "rtc_base/random.h" + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { + +Random::Random(uint64_t seed) { + RTC_DCHECK(seed != 0x0ull); + state_ = seed; +} + +uint32_t Random::Rand(uint32_t t) { + // Casting the output to 32 bits will give an almost uniform number. + // Pr[x=0] = (2^32-1) / (2^64-1) + // Pr[x=k] = 2^32 / (2^64-1) for k!=0 + // Uniform would be Pr[x=k] = 2^32 / 2^64 for all 32-bit integers k. + uint32_t x = NextOutput(); + // If x / 2^32 is uniform on [0,1), then x / 2^32 * (t+1) is uniform on + // the interval [0,t+1), so the integer part is uniform on [0,t]. + uint64_t result = x * (static_cast(t) + 1); + result >>= 32; + return result; +} + +uint32_t Random::Rand(uint32_t low, uint32_t high) { + RTC_DCHECK(low <= high); + return Rand(high - low) + low; +} + +int32_t Random::Rand(int32_t low, int32_t high) { + RTC_DCHECK(low <= high); + const int64_t low_i64{low}; + return dchecked_cast(Rand(dchecked_cast(high - low_i64)) + + low_i64); +} + +template <> +float Random::Rand() { + double result = NextOutput() - 1; + result = result / static_cast(0xFFFFFFFFFFFFFFFFull); + return static_cast(result); +} + +template <> +double Random::Rand() { + double result = NextOutput() - 1; + result = result / static_cast(0xFFFFFFFFFFFFFFFFull); + return result; +} + +template <> +bool Random::Rand() { + return Rand(0, 1) == 1; +} + +double Random::Gaussian(double mean, double standard_deviation) { + // Creating a Normal distribution variable from two independent uniform + // variables based on the Box-Muller transform, which is defined on the + // interval (0, 1]. Note that we rely on NextOutput to generate integers + // in the range [1, 2^64-1]. Normally this behavior is a bit frustrating, + // but here it is exactly what we need. + const double kPi = 3.14159265358979323846; + double u1 = static_cast(NextOutput()) / + static_cast(0xFFFFFFFFFFFFFFFFull); + double u2 = static_cast(NextOutput()) / + static_cast(0xFFFFFFFFFFFFFFFFull); + return mean + standard_deviation * sqrt(-2 * log(u1)) * cos(2 * kPi * u2); +} + +double Random::Exponential(double lambda) { + double uniform = Rand(); + return -log(uniform) / lambda; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/random.h b/pkg/apm/webrtc/rtc_base/random.h new file mode 100644 index 00000000..b3b9fd16 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/random.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_RANDOM_H_ +#define RTC_BASE_RANDOM_H_ + +#include + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +class Random { + public: + // TODO(tommi): Change this so that the seed can be initialized internally, + // e.g. by offering two ways of constructing or offer a static method that + // returns a seed that's suitable for initialization. + // The problem now is that callers are calling clock_->TimeInMicroseconds() + // which calls TickTime::Now().Ticks(), which can return a very low value on + // Mac and can result in a seed of 0 after conversion to microseconds. + // Besides the quality of the random seed being poor, this also requires + // the client to take on extra dependencies to generate a seed. + // If we go for a static seed generator in Random, we can use something from + // webrtc/rtc_base and make sure that it works the same way across platforms. + // See also discussion here: https://codereview.webrtc.org/1623543002/ + explicit Random(uint64_t seed); + + Random() = delete; + Random(const Random&) = delete; + Random& operator=(const Random&) = delete; + + // Return pseudo-random integer of the specified type. + // We need to limit the size to 32 bits to keep the output close to uniform. + template + T Rand() { + static_assert(std::numeric_limits::is_integer && + std::numeric_limits::radix == 2 && + std::numeric_limits::digits <= 32, + "Rand is only supported for built-in integer types that are " + "32 bits or smaller."); + return static_cast(NextOutput()); + } + + // Uniformly distributed pseudo-random number in the interval [0, t]. + uint32_t Rand(uint32_t t); + + // Uniformly distributed pseudo-random number in the interval [low, high]. + uint32_t Rand(uint32_t low, uint32_t high); + + // Uniformly distributed pseudo-random number in the interval [low, high]. + int32_t Rand(int32_t low, int32_t high); + + // Normal Distribution. + double Gaussian(double mean, double standard_deviation); + + // Exponential Distribution. + double Exponential(double lambda); + + private: + // Outputs a nonzero 64-bit random number using Xorshift algorithm. + // https://en.wikipedia.org/wiki/Xorshift + uint64_t NextOutput() { + state_ ^= state_ >> 12; + state_ ^= state_ << 25; + state_ ^= state_ >> 27; + RTC_DCHECK(state_ != 0x0ULL); + return state_ * 2685821657736338717ull; + } + + uint64_t state_; +}; + +// Return pseudo-random number in the interval [0.0, 1.0). +template <> +float Random::Rand(); + +// Return pseudo-random number in the interval [0.0, 1.0). +template <> +double Random::Rand(); + +// Return pseudo-random boolean value. +template <> +bool Random::Rand(); + +} // namespace webrtc + +#endif // RTC_BASE_RANDOM_H_ diff --git a/pkg/apm/webrtc/rtc_base/rate_limiter.h b/pkg/apm/webrtc/rtc_base/rate_limiter.h new file mode 100644 index 00000000..9bbe21f9 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/rate_limiter.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_RATE_LIMITER_H_ +#define RTC_BASE_RATE_LIMITER_H_ + +#include +#include + +#include "rtc_base/rate_statistics.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class Clock; + +// Class used to limit a bitrate, making sure the average does not exceed a +// maximum as measured over a sliding window. This class is thread safe; all +// methods will acquire (the same) lock befeore executing. +class RateLimiter { + public: + RateLimiter(Clock* clock, int64_t max_window_ms); + + RateLimiter() = delete; + RateLimiter(const RateLimiter&) = delete; + RateLimiter& operator=(const RateLimiter&) = delete; + + ~RateLimiter(); + + // Try to use rate to send bytes. Returns true on success and if so updates + // current rate. + bool TryUseRate(size_t packet_size_bytes); + + // Set the maximum bitrate, in bps, that this limiter allows to send. + void SetMaxRate(uint32_t max_rate_bps); + + // Set the window size over which to measure the current bitrate. + // For example, irt retransmissions, this is typically the RTT. + // Returns true on success and false if window_size_ms is out of range. + bool SetWindowSize(int64_t window_size_ms); + + private: + Clock* const clock_; + Mutex lock_; + RateStatistics current_rate_ RTC_GUARDED_BY(lock_); + int64_t window_size_ms_ RTC_GUARDED_BY(lock_); + uint32_t max_rate_bps_ RTC_GUARDED_BY(lock_); +}; + +} // namespace webrtc + +#endif // RTC_BASE_RATE_LIMITER_H_ diff --git a/pkg/apm/webrtc/rtc_base/rate_statistics.h b/pkg/apm/webrtc/rtc_base/rate_statistics.h new file mode 100644 index 00000000..7bcb0012 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/rate_statistics.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_RATE_STATISTICS_H_ +#define RTC_BASE_RATE_STATISTICS_H_ + +#include +#include + +#include +#include +#include + +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Class to estimate rates based on counts in a sequence of 1-millisecond +// intervals. + +// This class uses int64 for all its numbers because some rates can be very +// high; for instance, a 20 Mbit/sec video stream can wrap a 32-bit byte +// counter in 14 minutes. + +// Note that timestamps used in Update(), Rate() and SetWindowSize() must never +// decrease for two consecutive calls. +// TODO(bugs.webrtc.org/11600): Migrate from int64_t to Timestamp. + +class RTC_EXPORT RateStatistics { + public: + static constexpr float kBpsScale = 8000.0f; + + // max_window_size_ms = Maximum window size in ms for the rate estimation. + // Initial window size is set to this, but may be changed + // to something lower by calling SetWindowSize(). + // scale = coefficient to convert counts/ms to desired unit + // ex: kBpsScale (8000) for bits/s if count represents bytes. + RateStatistics(int64_t max_window_size_ms, float scale); + + RateStatistics(const RateStatistics& other); + + RateStatistics(RateStatistics&& other); + + ~RateStatistics(); + + // Reset instance to original state. + void Reset(); + + // Update rate with a new data point, moving averaging window as needed. + void Update(int64_t count, int64_t now_ms); + + // Note that despite this being a const method, it still updates the internal + // state (moves averaging window), but it doesn't make any alterations that + // are observable from the other methods, as long as supplied timestamps are + // from a monotonic clock. Ie, it doesn't matter if this call moves the + // window, since any subsequent call to Update or Rate would still have moved + // the window as much or more. + std::optional Rate(int64_t now_ms) const; + + // Update the size of the averaging window. The maximum allowed value for + // window_size_ms is max_window_size_ms as supplied in the constructor. + bool SetWindowSize(int64_t window_size_ms, int64_t now_ms); + + private: + void EraseOld(int64_t now_ms); + + struct Bucket { + explicit Bucket(int64_t timestamp); + int64_t sum; // Sum of all samples in this bucket. + int num_samples; // Number of samples in this bucket. + const int64_t timestamp; // Timestamp this bucket corresponds to. + }; + // All buckets within the time window, ordered by time. + std::deque buckets_; + + // Total count recorded in all buckets. + int64_t accumulated_count_; + + // Timestamp of the first data point seen, or -1 of none seen. + int64_t first_timestamp_; + + // True if accumulated_count_ has ever grown too large to be + // contained in its integer type. + bool overflow_ = false; + + // The total number of samples in the buckets. + int num_samples_; + + // To convert counts/ms to desired units + const float scale_; + + // The window sizes, in ms, over which the rate is calculated. + const int64_t max_window_size_ms_; + int64_t current_window_size_ms_; +}; +} // namespace webrtc + +#endif // RTC_BASE_RATE_STATISTICS_H_ diff --git a/pkg/apm/webrtc/rtc_base/rate_tracker.h b/pkg/apm/webrtc/rtc_base/rate_tracker.h new file mode 100644 index 00000000..37155878 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/rate_tracker.h @@ -0,0 +1,80 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_RATE_TRACKER_H_ +#define RTC_BASE_RATE_TRACKER_H_ + +#include +#include + +namespace webrtc { + +// Computes units per second over a given interval by tracking the units over +// each bucket of a given size and calculating the instantaneous rate assuming +// that over each bucket the rate was constant. +class RateTracker { + public: + RateTracker(int64_t bucket_milliseconds, size_t bucket_count); + virtual ~RateTracker(); + + // Computes the average rate over the most recent interval_milliseconds, + // or if the first sample was added within this period, computes the rate + // since the first sample was added. + double ComputeRateForInterval(int64_t interval_milliseconds) const; + + // Computes the average rate over the rate tracker's recording interval + // of bucket_milliseconds * bucket_count. + double ComputeRate() const { + return ComputeRateForInterval(bucket_milliseconds_ * + static_cast(bucket_count_)); + } + + // Computes the average rate since the first sample was added to the + // rate tracker. + double ComputeTotalRate() const; + + // The total number of samples added. + int64_t TotalSampleCount() const; + + // Reads the current time in order to determine the appropriate bucket for + // these samples, and increments the count for that bucket by sample_count. + void AddSamples(int64_t sample_count); + + // Increment count for bucket at `current_time_ms`. + void AddSamplesAtTime(int64_t current_time_ms, int64_t sample_count); + + protected: + // overrideable for tests + virtual int64_t Time() const; + + private: + void EnsureInitialized(); + size_t NextBucketIndex(size_t bucket_index) const; + + const int64_t bucket_milliseconds_; + const size_t bucket_count_; + int64_t* sample_buckets_; + size_t total_sample_count_; + size_t current_bucket_; + int64_t bucket_start_time_milliseconds_; + int64_t initialization_time_milliseconds_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::RateTracker; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_RATE_TRACKER_H_ diff --git a/pkg/apm/webrtc/rtc_base/ref_count.h b/pkg/apm/webrtc/rtc_base/ref_count.h new file mode 100644 index 00000000..d1ef1433 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ref_count.h @@ -0,0 +1,31 @@ +/* + * 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 RTC_BASE_REF_COUNT_H_ +#define RTC_BASE_REF_COUNT_H_ + +// Transition file for backwards compatibility with source code +// that includes the non-API file. + +#include "api/ref_count.h" + +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { + +// TODO(bugs.webrtc.org/15622): Deprecate and remove these aliases. +using RefCountInterface [[deprecated("Use webrtc::RefCountInterface")]] = + webrtc::RefCountInterface; +using RefCountReleaseStatus + [[deprecated("Use webrtc::RefCountReleaseStatus")]] = + webrtc::RefCountReleaseStatus; + +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_REF_COUNT_H_ diff --git a/pkg/apm/webrtc/rtc_base/ref_counted_object.h b/pkg/apm/webrtc/rtc_base/ref_counted_object.h new file mode 100644 index 00000000..032e9ad1 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ref_counted_object.h @@ -0,0 +1,98 @@ +/* + * Copyright 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. + */ +#ifndef RTC_BASE_REF_COUNTED_OBJECT_H_ +#define RTC_BASE_REF_COUNTED_OBJECT_H_ + +#include "api/scoped_refptr.h" +#include "rtc_base/ref_count.h" +#include "rtc_base/ref_counter.h" + +namespace webrtc { + +template +class RefCountedObject : public T { + public: + RefCountedObject() {} + + RefCountedObject(const RefCountedObject&) = delete; + RefCountedObject& operator=(const RefCountedObject&) = delete; + + template + explicit RefCountedObject(P0&& p0) : T(std::forward(p0)) {} + + template + RefCountedObject(P0&& p0, P1&& p1, Args&&... args) + : T(std::forward(p0), + std::forward(p1), + std::forward(args)...) {} + + void AddRef() const override { ref_count_.IncRef(); } + + RefCountReleaseStatus Release() const override { + const auto status = ref_count_.DecRef(); + if (status == RefCountReleaseStatus::kDroppedLastRef) { + delete this; + } + return status; + } + + // Return whether the reference count is one. If the reference count is used + // in the conventional way, a reference count of 1 implies that the current + // thread owns the reference and no other thread shares it. This call + // performs the test for a reference count of one, and performs the memory + // barrier needed for the owning thread to act on the object, knowing that it + // has exclusive access to the object. + virtual bool HasOneRef() const { return ref_count_.HasOneRef(); } + + protected: + ~RefCountedObject() override {} + + mutable webrtc::webrtc_impl::RefCounter ref_count_{0}; +}; + +template +class FinalRefCountedObject final : public T { + public: + using T::T; + // Above using declaration propagates a default move constructor + // FinalRefCountedObject(FinalRefCountedObject&& other), but we also need + // move construction from T. + explicit FinalRefCountedObject(T&& other) : T(std::move(other)) {} + FinalRefCountedObject(const FinalRefCountedObject&) = delete; + FinalRefCountedObject& operator=(const FinalRefCountedObject&) = delete; + + void AddRef() const { ref_count_.IncRef(); } + RefCountReleaseStatus Release() const { + const auto status = ref_count_.DecRef(); + if (status == RefCountReleaseStatus::kDroppedLastRef) { + delete this; + } + return status; + } + bool HasOneRef() const { return ref_count_.HasOneRef(); } + + private: + ~FinalRefCountedObject() = default; + + mutable webrtc::webrtc_impl::RefCounter ref_count_{0}; +}; + +} // namespace webrtc + +// Backwards compatibe aliases. +// TODO: https://issues.webrtc.org/42225969 - deprecate and remove. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::FinalRefCountedObject; +using ::webrtc::RefCountedObject; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_REF_COUNTED_OBJECT_H_ diff --git a/pkg/apm/webrtc/rtc_base/ref_counter.h b/pkg/apm/webrtc/rtc_base/ref_counter.h new file mode 100644 index 00000000..93ae3d21 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ref_counter.h @@ -0,0 +1,75 @@ +/* + * 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 RTC_BASE_REF_COUNTER_H_ +#define RTC_BASE_REF_COUNTER_H_ + +#include + +#include "rtc_base/ref_count.h" + +namespace webrtc { +namespace webrtc_impl { + +class RefCounter { + public: + explicit RefCounter(int ref_count) : ref_count_(ref_count) {} + RefCounter() = delete; + + void IncRef() { + // Relaxed memory order: The current thread is allowed to act on the + // resource protected by the reference counter both before and after the + // atomic op, so this function doesn't prevent memory access reordering. + ref_count_.fetch_add(1, std::memory_order_relaxed); + } + + // Returns kDroppedLastRef if this call dropped the last reference; the caller + // should therefore free the resource protected by the reference counter. + // Otherwise, returns kOtherRefsRemained (note that in case of multithreading, + // some other caller may have dropped the last reference by the time this call + // returns; all we know is that we didn't do it). + RefCountReleaseStatus DecRef() { + // Use release-acquire barrier to ensure all actions on the protected + // resource are finished before the resource can be freed. + // When ref_count_after_subtract > 0, this function require + // std::memory_order_release part of the barrier. + // When ref_count_after_subtract == 0, this function require + // std::memory_order_acquire part of the barrier. + // In addition std::memory_order_release is used for synchronization with + // the HasOneRef function to make sure all actions on the protected resource + // are finished before the resource is assumed to have exclusive access. + int ref_count_after_subtract = + ref_count_.fetch_sub(1, std::memory_order_acq_rel) - 1; + return ref_count_after_subtract == 0 + ? RefCountReleaseStatus::kDroppedLastRef + : RefCountReleaseStatus::kOtherRefsRemained; + } + + // Return whether the reference count is one. If the reference count is used + // in the conventional way, a reference count of 1 implies that the current + // thread owns the reference and no other thread shares it. This call performs + // the test for a reference count of one, and performs the memory barrier + // needed for the owning thread to act on the resource protected by the + // reference counter, knowing that it has exclusive access. + bool HasOneRef() const { + // To ensure resource protected by the reference counter has exclusive + // access, all changes to the resource before it was released by other + // threads must be visible by current thread. That is provided by release + // (in DecRef) and acquire (in this function) ordering. + return ref_count_.load(std::memory_order_acquire) == 1; + } + + private: + std::atomic ref_count_; +}; + +} // namespace webrtc_impl +} // namespace webrtc + +#endif // RTC_BASE_REF_COUNTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/rolling_accumulator.h b/pkg/apm/webrtc/rtc_base/rolling_accumulator.h new file mode 100644 index 00000000..8a46d5fe --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/rolling_accumulator.h @@ -0,0 +1,153 @@ +/* + * 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 RTC_BASE_ROLLING_ACCUMULATOR_H_ +#define RTC_BASE_ROLLING_ACCUMULATOR_H_ + +#include + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/running_statistics.h" + +namespace webrtc { + +// RollingAccumulator stores and reports statistics +// over N most recent samples. +// +// T is assumed to be an int, long, double or float. +template +class RollingAccumulator { + public: + explicit RollingAccumulator(size_t max_count) : samples_(max_count) { + RTC_DCHECK(max_count > 0); + Reset(); + } + ~RollingAccumulator() {} + + RollingAccumulator(const RollingAccumulator&) = delete; + RollingAccumulator& operator=(const RollingAccumulator&) = delete; + + size_t max_count() const { return samples_.size(); } + + size_t count() const { return static_cast(stats_.Size()); } + + void Reset() { + stats_ = webrtc_impl::RunningStatistics(); + next_index_ = 0U; + max_ = T(); + max_stale_ = false; + min_ = T(); + min_stale_ = false; + } + + void AddSample(T sample) { + if (count() == max_count()) { + // Remove oldest sample. + T sample_to_remove = samples_[next_index_]; + stats_.RemoveSample(sample_to_remove); + if (sample_to_remove >= max_) { + max_stale_ = true; + } + if (sample_to_remove <= min_) { + min_stale_ = true; + } + } + // Add new sample. + samples_[next_index_] = sample; + if (count() == 0 || sample >= max_) { + max_ = sample; + max_stale_ = false; + } + if (count() == 0 || sample <= min_) { + min_ = sample; + min_stale_ = false; + } + stats_.AddSample(sample); + // Update next_index_. + next_index_ = (next_index_ + 1) % max_count(); + } + + double ComputeMean() const { return stats_.GetMean().value_or(0); } + + T ComputeMax() const { + if (max_stale_) { + RTC_DCHECK(count() > 0) + << "It shouldn't be possible for max_stale_ && count() == 0"; + max_ = samples_[next_index_]; + for (size_t i = 1u; i < count(); i++) { + max_ = std::max(max_, samples_[(next_index_ + i) % max_count()]); + } + max_stale_ = false; + } + return max_; + } + + T ComputeMin() const { + if (min_stale_) { + RTC_DCHECK(count() > 0) + << "It shouldn't be possible for min_stale_ && count() == 0"; + min_ = samples_[next_index_]; + for (size_t i = 1u; i < count(); i++) { + min_ = std::min(min_, samples_[(next_index_ + i) % max_count()]); + } + min_stale_ = false; + } + return min_; + } + + // O(n) time complexity. + // Weights nth sample with weight (learning_rate)^n. Learning_rate should be + // between (0.0, 1.0], otherwise the non-weighted mean is returned. + double ComputeWeightedMean(double learning_rate) const { + if (count() < 1 || learning_rate <= 0.0 || learning_rate >= 1.0) { + return ComputeMean(); + } + double weighted_mean = 0.0; + double current_weight = 1.0; + double weight_sum = 0.0; + const size_t max_size = max_count(); + for (size_t i = 0; i < count(); ++i) { + current_weight *= learning_rate; + weight_sum += current_weight; + // Add max_size to prevent underflow. + size_t index = (next_index_ + max_size - i - 1) % max_size; + weighted_mean += current_weight * samples_[index]; + } + return weighted_mean / weight_sum; + } + + // Compute estimated variance. Estimation is more accurate + // as the number of samples grows. + double ComputeVariance() const { return stats_.GetVariance().value_or(0); } + + private: + webrtc_impl::RunningStatistics stats_; + size_t next_index_; + mutable T max_; + mutable bool max_stale_; + mutable T min_; + mutable bool min_stale_; + std::vector samples_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::RollingAccumulator; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_ROLLING_ACCUMULATOR_H_ diff --git a/pkg/apm/webrtc/rtc_base/rtc_base.go b/pkg/apm/webrtc/rtc_base/rtc_base.go new file mode 100644 index 00000000..3b37bcb3 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/rtc_base.go @@ -0,0 +1,19 @@ +//go:build console + +package rtc_base + +// #cgo CXXFLAGS: -I${SRCDIR}/.. -I${SRCDIR}/../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" + +import ( + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/rtc_base/containers" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/rtc_base/memory" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/rtc_base/numerics" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/rtc_base/strings" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/rtc_base/synchronization" + _ "github.com/livekit/livekit-cli/v2/pkg/apm/webrtc/rtc_base/system" +) diff --git a/pkg/apm/webrtc/rtc_base/rtc_certificate.h b/pkg/apm/webrtc/rtc_base/rtc_certificate.h new file mode 100644 index 00000000..7d476565 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/rtc_certificate.h @@ -0,0 +1,103 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_RTC_CERTIFICATE_H_ +#define RTC_BASE_RTC_CERTIFICATE_H_ + +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/ref_counted_base.h" +#include "api/scoped_refptr.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// This class contains PEM strings of an RTCCertificate's private key and +// certificate and acts as a text representation of RTCCertificate. Certificates +// can be serialized and deserialized to and from this format, which allows for +// cloning and storing of certificates to disk. The PEM format is that of +// `SSLIdentity::PrivateKeyToPEMString` and `SSLCertificate::ToPEMString`, e.g. +// the string representations used by OpenSSL. +class RTCCertificatePEM { + public: + RTCCertificatePEM(absl::string_view private_key, + absl::string_view certificate) + : private_key_(private_key), certificate_(certificate) {} + + const std::string& private_key() const { return private_key_; } + const std::string& certificate() const { return certificate_; } + + private: + std::string private_key_; + std::string certificate_; +}; + +// A thin abstraction layer between "lower level crypto stuff" like +// SSLCertificate and WebRTC usage. Takes ownership of some lower level objects, +// reference counting protects these from premature destruction. +class RTC_EXPORT RTCCertificate final + : public RefCountedNonVirtual { + public: + // Takes ownership of `identity`. + static scoped_refptr Create( + std::unique_ptr identity); + + // Returns the expiration time in ms relative to epoch, 1970-01-01T00:00:00Z. + uint64_t Expires() const; + // Checks if the certificate has expired, where `now` is expressed in ms + // relative to epoch, 1970-01-01T00:00:00Z. + bool HasExpired(uint64_t now) const; + + const SSLCertificate& GetSSLCertificate() const; + const SSLCertChain& GetSSLCertificateChain() const; + + // TODO(hbos): If possible, remove once RTCCertificate and its + // GetSSLCertificate() is used in all relevant places. Should not pass around + // raw SSLIdentity* for the sake of accessing SSLIdentity::certificate(). + // However, some places might need SSLIdentity* for its public/private key... + SSLIdentity* identity() const { return identity_.get(); } + + // To/from PEM, a text representation of the RTCCertificate. + RTCCertificatePEM ToPEM() const; + // Can return nullptr if the certificate is invalid. + static scoped_refptr FromPEM(const RTCCertificatePEM& pem); + bool operator==(const RTCCertificate& certificate) const; + bool operator!=(const RTCCertificate& certificate) const; + + protected: + explicit RTCCertificate(SSLIdentity* identity); + + friend class RefCountedNonVirtual; + ~RTCCertificate(); + + private: + // The SSLIdentity is the owner of the SSLCertificate. To protect our + // GetSSLCertificate() we take ownership of `identity_`. + const std::unique_ptr identity_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::RTCCertificate; +using ::webrtc::RTCCertificatePEM; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_RTC_CERTIFICATE_H_ diff --git a/pkg/apm/webrtc/rtc_base/rtc_certificate_generator.h b/pkg/apm/webrtc/rtc_base/rtc_certificate_generator.h new file mode 100644 index 00000000..ca5d419c --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/rtc_certificate_generator.h @@ -0,0 +1,93 @@ +/* + * Copyright 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. + */ + +#ifndef RTC_BASE_RTC_CERTIFICATE_GENERATOR_H_ +#define RTC_BASE_RTC_CERTIFICATE_GENERATOR_H_ + +#include + +#include + +#include "absl/functional/any_invocable.h" +#include "api/scoped_refptr.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread.h" + +namespace webrtc { + +// Generates `RTCCertificate`s. +// See `RTCCertificateGenerator` for the WebRTC repo's implementation. +class RTCCertificateGeneratorInterface { + public: + // Functor that will be called when certificate is generated asynchroniosly. + // Called with nullptr as the parameter on failure. + using Callback = + absl::AnyInvocable) &&>; + + virtual ~RTCCertificateGeneratorInterface() = default; + + // Generates a certificate asynchronously on the worker thread. + // Must be called on the signaling thread. The `callback` is invoked with the + // result on the signaling thread. `exipres_ms` optionally specifies for how + // long we want the certificate to be valid, but the implementation may choose + // its own restrictions on the expiration time. + virtual void GenerateCertificateAsync( + const KeyParams& key_params, + const std::optional& expires_ms, + Callback callback) = 0; +}; + +// Standard implementation of `RTCCertificateGeneratorInterface`. +// The static function `GenerateCertificate` generates a certificate on the +// current thread. The `RTCCertificateGenerator` instance generates certificates +// asynchronously on the worker thread with `GenerateCertificateAsync`. +class RTC_EXPORT RTCCertificateGenerator + : public RTCCertificateGeneratorInterface { + public: + // Generates a certificate on the current thread. Returns null on failure. + // If `expires_ms` is specified, the certificate will expire in approximately + // that many milliseconds from now. `expires_ms` is limited to a year, a + // larger value than that is clamped down to a year. If `expires_ms` is not + // specified, a default expiration time is used. + static scoped_refptr GenerateCertificate( + const KeyParams& key_params, + const std::optional& expires_ms); + + RTCCertificateGenerator(Thread* signaling_thread, Thread* worker_thread); + ~RTCCertificateGenerator() override {} + + // `RTCCertificateGeneratorInterface` overrides. + // If `expires_ms` is specified, the certificate will expire in approximately + // that many milliseconds from now. `expires_ms` is limited to a year, a + // larger value than that is clamped down to a year. If `expires_ms` is not + // specified, a default expiration time is used. + void GenerateCertificateAsync(const KeyParams& key_params, + const std::optional& expires_ms, + Callback callback) override; + + private: + Thread* const signaling_thread_; + Thread* const worker_thread_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::RTCCertificateGenerator; +using ::webrtc::RTCCertificateGeneratorInterface; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_RTC_CERTIFICATE_GENERATOR_H_ diff --git a/pkg/apm/webrtc/rtc_base/sanitizer.h b/pkg/apm/webrtc/rtc_base/sanitizer.h new file mode 100644 index 00000000..2f5045d1 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/sanitizer.h @@ -0,0 +1,176 @@ +/* + * Copyright 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. + */ + +#ifndef RTC_BASE_SANITIZER_H_ +#define RTC_BASE_SANITIZER_H_ + +#include // For size_t. + +#ifdef __cplusplus +#include +#endif + +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define RTC_HAS_ASAN 1 +#endif +#if __has_feature(memory_sanitizer) +#define RTC_HAS_MSAN 1 +#endif +#endif +#ifndef RTC_HAS_ASAN +#define RTC_HAS_ASAN 0 +#endif +#ifndef RTC_HAS_MSAN +#define RTC_HAS_MSAN 0 +#endif + +#if RTC_HAS_ASAN +#include +#endif +#if RTC_HAS_MSAN +#include +#endif + +#ifdef __has_attribute +#if __has_attribute(no_sanitize) +#define RTC_NO_SANITIZE(what) __attribute__((no_sanitize(what))) +#endif +#endif +#ifndef RTC_NO_SANITIZE +#define RTC_NO_SANITIZE(what) +#endif + +// Ask ASan to mark the memory range [ptr, ptr + element_size * num_elements) +// as being unaddressable, so that reads and writes are not allowed. ASan may +// narrow the range to the nearest alignment boundaries. +static inline void rtc_AsanPoison(const volatile void* ptr, + size_t element_size, + size_t num_elements) { +#if RTC_HAS_ASAN + ASAN_POISON_MEMORY_REGION(ptr, element_size * num_elements); +#else + // This is to prevent from the compiler raising a warning/error over unused + // variables. We cannot use clang's annotation (`[[maybe_unused]]`) because + // this file is also included from c files which doesn't support the + // annotation till we switch to C23 + (void)ptr; + (void)element_size; + (void)num_elements; +#endif +} + +// Ask ASan to mark the memory range [ptr, ptr + element_size * num_elements) +// as being addressable, so that reads and writes are allowed. ASan may widen +// the range to the nearest alignment boundaries. +static inline void rtc_AsanUnpoison(const volatile void* ptr, + size_t element_size, + size_t num_elements) { +#if RTC_HAS_ASAN + ASAN_UNPOISON_MEMORY_REGION(ptr, element_size * num_elements); +#else + (void)ptr; + (void)element_size; + (void)num_elements; +#endif +} + +// Ask MSan to mark the memory range [ptr, ptr + element_size * num_elements) +// as being uninitialized. +static inline void rtc_MsanMarkUninitialized(const volatile void* ptr, + size_t element_size, + size_t num_elements) { +#if RTC_HAS_MSAN + __msan_poison(ptr, element_size * num_elements); +#else + (void)ptr; + (void)element_size; + (void)num_elements; +#endif +} + +// Force an MSan check (if any bits in the memory range [ptr, ptr + +// element_size * num_elements) are uninitialized the call will crash with an +// MSan report). +static inline void rtc_MsanCheckInitialized(const volatile void* ptr, + size_t element_size, + size_t num_elements) { +#if RTC_HAS_MSAN + __msan_check_mem_is_initialized(ptr, element_size * num_elements); +#else + (void)ptr; + (void)element_size; + (void)num_elements; +#endif +} + +#ifdef __cplusplus + +namespace webrtc { +namespace sanitizer_impl { + +template +constexpr bool IsTriviallyCopyable() { + return static_cast(std::is_trivially_copy_constructible::value && + (std::is_trivially_copy_assignable::value || + !std::is_copy_assignable::value) && + std::is_trivially_destructible::value); +} + +} // namespace sanitizer_impl + +template +inline void AsanPoison(const T& mem) { + rtc_AsanPoison(mem.data(), sizeof(mem.data()[0]), mem.size()); +} + +template +inline void AsanUnpoison(const T& mem) { + rtc_AsanUnpoison(mem.data(), sizeof(mem.data()[0]), mem.size()); +} + +template +inline void MsanMarkUninitialized(const T& mem) { + rtc_MsanMarkUninitialized(mem.data(), sizeof(mem.data()[0]), mem.size()); +} + +template +inline T MsanUninitialized(T t) { +#if RTC_HAS_MSAN + // TODO(bugs.webrtc.org/8762): Switch to std::is_trivially_copyable when it + // becomes available in downstream projects. + static_assert(sanitizer_impl::IsTriviallyCopyable(), ""); +#endif + rtc_MsanMarkUninitialized(&t, sizeof(T), 1); + return t; +} + +template +inline void MsanCheckInitialized(const T& mem) { + rtc_MsanCheckInitialized(mem.data(), sizeof(mem.data()[0]), mem.size()); +} + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::AsanPoison; +using ::webrtc::AsanUnpoison; +using ::webrtc::MsanCheckInitialized; +using ::webrtc::MsanMarkUninitialized; +using ::webrtc::MsanUninitialized; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // __cplusplus + +#endif // RTC_BASE_SANITIZER_H_ diff --git a/pkg/apm/webrtc/rtc_base/server_socket_adapters.h b/pkg/apm/webrtc/rtc_base/server_socket_adapters.h new file mode 100644 index 00000000..f9eeb1f9 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/server_socket_adapters.h @@ -0,0 +1,52 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SERVER_SOCKET_ADAPTERS_H_ +#define RTC_BASE_SERVER_SOCKET_ADAPTERS_H_ + +#include "rtc_base/socket_adapters.h" + +namespace webrtc { + +// Interface for implementing proxy server sockets. +class AsyncProxyServerSocket : public BufferedReadAdapter { + public: + AsyncProxyServerSocket(Socket* socket, size_t buffer_size); + ~AsyncProxyServerSocket() override; + sigslot::signal2 + SignalConnectRequest; + virtual void SendConnectResult(int err, const SocketAddress& addr) = 0; +}; + +// Implements a socket adapter that performs the server side of a +// fake SSL handshake. Used when implementing a relay server that does "ssltcp". +class AsyncSSLServerSocket : public BufferedReadAdapter { + public: + explicit AsyncSSLServerSocket(Socket* socket); + + AsyncSSLServerSocket(const AsyncSSLServerSocket&) = delete; + AsyncSSLServerSocket& operator=(const AsyncSSLServerSocket&) = delete; + + protected: + void ProcessInput(char* data, size_t* len) override; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::AsyncProxyServerSocket; +using ::webrtc::AsyncSSLServerSocket; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SERVER_SOCKET_ADAPTERS_H_ diff --git a/pkg/apm/webrtc/rtc_base/socket.h b/pkg/apm/webrtc/rtc_base/socket.h new file mode 100644 index 00000000..ac220cb0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/socket.h @@ -0,0 +1,186 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SOCKET_H_ +#define RTC_BASE_SOCKET_H_ + +#include + +#include +#include +#include + +// IWYU pragma: begin_exports +#if defined(WEBRTC_POSIX) +#include +#include +#define SOCKET_EACCES EACCES +#endif +// IWYU pragma: end_exports + +#include "api/units/timestamp.h" +#include "rtc_base/buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/ip_address.h" +#include "rtc_base/net_helpers.h" +#include "rtc_base/network/ecn_marking.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/third_party/sigslot/sigslot.h" + +// Rather than converting errors into a private namespace, +// Reuse the POSIX socket api errors. Note this depends on +// Win32 compatibility. + +#if defined(WEBRTC_WIN) +#undef EWOULDBLOCK // Remove errno.h's definition for each macro below. +#define EWOULDBLOCK WSAEWOULDBLOCK +#undef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#undef EALREADY +#define EALREADY WSAEALREADY +#undef EMSGSIZE +#define EMSGSIZE WSAEMSGSIZE +#undef EADDRINUSE +#define EADDRINUSE WSAEADDRINUSE +#undef EADDRNOTAVAIL +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#undef ENETDOWN +#define ENETDOWN WSAENETDOWN +#undef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#undef ENOBUFS +#define ENOBUFS WSAENOBUFS +#undef EISCONN +#define EISCONN WSAEISCONN +#undef ENOTCONN +#define ENOTCONN WSAENOTCONN +#undef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#undef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#undef ENETUNREACH +#define ENETUNREACH WSAENETUNREACH +#define SOCKET_EACCES WSAEACCES +#endif // WEBRTC_WIN + +#if defined(WEBRTC_POSIX) +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define closesocket(s) close(s) +#endif // WEBRTC_POSIX + +namespace webrtc { + +inline bool IsBlockingError(int e) { + return (e == EWOULDBLOCK) || (e == EAGAIN) || (e == EINPROGRESS); +} + +// General interface for the socket implementations of various networks. The +// methods match those of normal UNIX sockets very closely. +class RTC_EXPORT Socket { + public: + struct ReceiveBuffer { + ReceiveBuffer(Buffer& payload) : payload(payload) {} + + std::optional arrival_time; + SocketAddress source_address; + EcnMarking ecn = EcnMarking::kNotEct; + Buffer& payload; + }; + virtual ~Socket() {} + + Socket(const Socket&) = delete; + Socket& operator=(const Socket&) = delete; + + // Returns the address to which the socket is bound. If the socket is not + // bound, then the any-address is returned. + virtual SocketAddress GetLocalAddress() const = 0; + + // Returns the address to which the socket is connected. If the socket is + // not connected, then the any-address is returned. + virtual SocketAddress GetRemoteAddress() const = 0; + + virtual int Bind(const SocketAddress& addr) = 0; + virtual int Connect(const SocketAddress& addr) = 0; + virtual int Send(const void* pv, size_t cb) = 0; + virtual int SendTo(const void* pv, size_t cb, const SocketAddress& addr) = 0; + // `timestamp` is in units of microseconds. + virtual int Recv(void* pv, size_t cb, int64_t* timestamp) = 0; + // TODO(webrtc:15368): Deprecate and remove. + virtual int RecvFrom(void* /* pv */, + size_t /* cb */, + SocketAddress* /* paddr */, + int64_t* /* timestamp */) { + // Not implemented. Use RecvFrom(ReceiveBuffer& buffer). + RTC_CHECK_NOTREACHED(); + } + // Intended to replace RecvFrom(void* ...). + // Default implementation calls RecvFrom(void* ...) with 64Kbyte buffer. + // Returns number of bytes received or a negative value on error. + virtual int RecvFrom(ReceiveBuffer& buffer); + virtual int Listen(int backlog) = 0; + virtual Socket* Accept(SocketAddress* paddr) = 0; + virtual int Close() = 0; + virtual int GetError() const = 0; + virtual void SetError(int error) = 0; + inline bool IsBlocking() const { return IsBlockingError(GetError()); } + + enum ConnState { CS_CLOSED, CS_CONNECTING, CS_CONNECTED }; + virtual ConnState GetState() const = 0; + + enum Option { + OPT_DONTFRAGMENT, + OPT_RCVBUF, // receive buffer size + OPT_SNDBUF, // send buffer size + OPT_NODELAY, // whether Nagle algorithm is enabled + OPT_IPV6_V6ONLY, // Whether the socket is IPv6 only. + OPT_DSCP, // DSCP code + OPT_RTP_SENDTIME_EXTN_ID, // This is a non-traditional socket option param. + // This is specific to libjingle and will be used + // if SendTime option is needed at socket level. + OPT_SEND_ECN, // 2-bit ECN + OPT_RECV_ECN, + OPT_KEEPALIVE, // Enable socket keep alive + OPT_TCP_KEEPCNT, // Set TCP keep alive count + OPT_TCP_KEEPIDLE, // Set TCP keep alive idle time in seconds + OPT_TCP_KEEPINTVL, // Set TCP keep alive interval in seconds + OPT_TCP_USER_TIMEOUT, // Set TCP user timeout + }; + virtual int GetOption(Option opt, int* value) = 0; + virtual int SetOption(Option opt, int value) = 0; + + // SignalReadEvent and SignalWriteEvent use multi_threaded_local to allow + // access concurrently from different thread. + // For example SignalReadEvent::connect will be called in AsyncUDPSocket ctor + // but at the same time the SocketDispatcher may be signaling the read event. + // ready to read + sigslot::signal1 SignalReadEvent; + // ready to write + sigslot::signal1 SignalWriteEvent; + sigslot::signal1 SignalConnectEvent; // connected + sigslot::signal2 SignalCloseEvent; // closed + + protected: + Socket() {} +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::IsBlockingError; +using ::webrtc::Socket; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SOCKET_H_ diff --git a/pkg/apm/webrtc/rtc_base/socket_adapters.h b/pkg/apm/webrtc/rtc_base/socket_adapters.h new file mode 100644 index 00000000..2e0731e6 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/socket_adapters.h @@ -0,0 +1,88 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SOCKET_ADAPTERS_H_ +#define RTC_BASE_SOCKET_ADAPTERS_H_ + +#include +#include + +#include "api/array_view.h" +#include "rtc_base/async_socket.h" +#include "rtc_base/socket.h" +#include "rtc_base/socket_address.h" + +namespace webrtc { + +/////////////////////////////////////////////////////////////////////////////// + +// Implements a socket adapter that can buffer and process data internally, +// as in the case of connecting to a proxy, where you must speak the proxy +// protocol before commencing normal socket behavior. +class BufferedReadAdapter : public AsyncSocketAdapter { + public: + BufferedReadAdapter(Socket* socket, size_t buffer_size); + ~BufferedReadAdapter() override; + + BufferedReadAdapter(const BufferedReadAdapter&) = delete; + BufferedReadAdapter& operator=(const BufferedReadAdapter&) = delete; + + int Send(const void* pv, size_t cb) override; + int Recv(void* pv, size_t cb, int64_t* timestamp) override; + + protected: + int DirectSend(const void* pv, size_t cb) { + return AsyncSocketAdapter::Send(pv, cb); + } + + void BufferInput(bool on = true); + virtual void ProcessInput(char* data, size_t* len) = 0; + + void OnReadEvent(Socket* socket) override; + + private: + char* buffer_; + size_t buffer_size_, data_len_; + bool buffering_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +// Implements a socket adapter that performs the client side of a +// fake SSL handshake. Used for "ssltcp" P2P functionality. +class AsyncSSLSocket : public BufferedReadAdapter { + public: + static ArrayView SslClientHello(); + static ArrayView SslServerHello(); + + explicit AsyncSSLSocket(Socket* socket); + + AsyncSSLSocket(const AsyncSSLSocket&) = delete; + AsyncSSLSocket& operator=(const AsyncSSLSocket&) = delete; + + int Connect(const SocketAddress& addr) override; + + protected: + void OnConnectEvent(Socket* socket) override; + void ProcessInput(char* data, size_t* len) override; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::AsyncSSLSocket; +using ::webrtc::BufferedReadAdapter; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SOCKET_ADAPTERS_H_ diff --git a/pkg/apm/webrtc/rtc_base/socket_address.h b/pkg/apm/webrtc/rtc_base/socket_address.h new file mode 100644 index 00000000..99fd6a6e --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/socket_address.h @@ -0,0 +1,209 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SOCKET_ADDRESS_H_ +#define RTC_BASE_SOCKET_ADDRESS_H_ + +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/ip_address.h" +#include "rtc_base/system/rtc_export.h" + +#undef SetPort + +struct sockaddr_in; +struct sockaddr_storage; + +namespace webrtc { + +// Records an IP address and port. +class RTC_EXPORT SocketAddress { + public: + // Creates a nil address. + SocketAddress(); + + // Creates the address with the given host and port. Host may be a + // literal IP string or a hostname to be resolved later. + // DCHECKs that port is in valid range (0 to 2^16-1). + SocketAddress(absl::string_view hostname, int port); + + // Creates the address with the given IP and port. + // IP is given as an integer in host byte order. V4 only, to be deprecated. + // DCHECKs that port is in valid range (0 to 2^16-1). + SocketAddress(uint32_t ip_as_host_order_integer, int port); + + // Creates the address with the given IP and port. + // DCHECKs that port is in valid range (0 to 2^16-1). + SocketAddress(const IPAddress& ip, int port); + + // Creates a copy of the given address. + SocketAddress(const SocketAddress& addr); + + // Resets to the nil address. + void Clear(); + + // Determines if this is a nil address (empty hostname, any IP, null port) + bool IsNil() const; + + // Returns true if ip and port are set. + bool IsComplete() const; + + // Replaces our address with the given one. + SocketAddress& operator=(const SocketAddress& addr); + + // Changes the IP of this address to the given one, and clears the hostname + // IP is given as an integer in host byte order. V4 only, to be deprecated.. + void SetIP(uint32_t ip_as_host_order_integer); + + // Changes the IP of this address to the given one, and clears the hostname. + void SetIP(const IPAddress& ip); + + // Changes the hostname of this address to the given one. + // Does not resolve the address; use Resolve to do so. + void SetIP(absl::string_view hostname); + + // Sets the IP address while retaining the hostname. Useful for bypassing + // DNS for a pre-resolved IP. + // IP is given as an integer in host byte order. V4 only, to be deprecated. + void SetResolvedIP(uint32_t ip_as_host_order_integer); + + // Sets the IP address while retaining the hostname. Useful for bypassing + // DNS for a pre-resolved IP. + void SetResolvedIP(const IPAddress& ip); + + // Changes the port of this address to the given one. + // DCHECKs that port is in valid range (0 to 2^16-1). + void SetPort(int port); + + // Returns the hostname. + const std::string& hostname() const { return hostname_; } + + // Returns the IP address as a host byte order integer. + // Returns 0 for non-v4 addresses. + uint32_t ip() const; + + const IPAddress& ipaddr() const; + + int family() const { return ip_.family(); } + + // Returns the port part of this address. + uint16_t port() const; + + // Returns the scope ID associated with this address. Scope IDs are a + // necessary addition to IPv6 link-local addresses, with different network + // interfaces having different scope-ids for their link-local addresses. + // IPv4 address do not have scope_ids and sockaddr_in structures do not have + // a field for them. + int scope_id() const { return scope_id_; } + void SetScopeID(int id) { scope_id_ = id; } + + // Returns the 'host' portion of the address (hostname or IP) in a form + // suitable for use in a URI. If both IP and hostname are present, hostname + // is preferred. IPv6 addresses are enclosed in square brackets ('[' and ']'). + std::string HostAsURIString() const; + + // Same as HostAsURIString but anonymizes IP addresses by hiding the last + // part. + std::string HostAsSensitiveURIString() const; + + // Returns the port as a string. + std::string PortAsString() const; + + // Returns hostname:port or [hostname]:port. + std::string ToString() const; + + // Same as ToString but anonymizes it by hiding the last part. + std::string ToSensitiveString() const; + + // Returns sensitive description of address in a form which both includes + // resolved and unresolved addresses based on their availability. + std::string ToSensitiveNameAndAddressString() const; + + // Parses hostname:port and [hostname]:port. + bool FromString(absl::string_view str); + + // Determines whether this represents a missing / any IP address. + // That is, 0.0.0.0 or ::. + // Hostname and/or port may be set. + bool IsAnyIP() const; + + // Determines whether the IP address refers to a loopback address. + // For v4 addresses this means the address is in the range 127.0.0.0/8. + // For v6 addresses this means the address is ::1. + bool IsLoopbackIP() const; + + // Determines whether the IP address is in one of the private ranges: + // For v4: 127.0.0.0/8 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12. + // For v6: FE80::/16 and ::1. + bool IsPrivateIP() const; + + // Determines whether the hostname has been resolved to an IP. + bool IsUnresolvedIP() const; + + // Determines whether this address is identical to the given one. + bool operator==(const SocketAddress& addr) const; + inline bool operator!=(const SocketAddress& addr) const { + return !this->operator==(addr); + } + + // Compares based on IP and then port. + bool operator<(const SocketAddress& addr) const; + + // Determines whether this address has the same IP as the one given. + bool EqualIPs(const SocketAddress& addr) const; + + // Determines whether this address has the same port as the one given. + bool EqualPorts(const SocketAddress& addr) const; + + // Hashes this address into a small number. + size_t Hash() const; + + // Write this address to a sockaddr_in. + // If IPv6, will zero out the sockaddr_in and sets family to AF_UNSPEC. + void ToSockAddr(sockaddr_in* saddr) const; + + // Read this address from a sockaddr_in. + bool FromSockAddr(const sockaddr_in& saddr); + + // Read and write the address to/from a sockaddr_storage. + // Dual stack version always sets family to AF_INET6, and maps v4 addresses. + // The other version doesn't map, and outputs an AF_INET address for + // v4 or mapped addresses, and AF_INET6 addresses for others. + // Returns the size of the sockaddr_in or sockaddr_in6 structure that is + // written to the sockaddr_storage, or zero on failure. + size_t ToDualStackSockAddrStorage(sockaddr_storage* saddr) const; + size_t ToSockAddrStorage(sockaddr_storage* saddr) const; + + private: + std::string hostname_; + IPAddress ip_; + uint16_t port_; + int scope_id_; + bool literal_; // Indicates that 'hostname_' contains a literal IP string. +}; + +RTC_EXPORT bool SocketAddressFromSockAddrStorage(const sockaddr_storage& saddr, + SocketAddress* out); +SocketAddress EmptySocketAddressWithFamily(int family); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::EmptySocketAddressWithFamily; +using ::webrtc::SocketAddress; +using ::webrtc::SocketAddressFromSockAddrStorage; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SOCKET_ADDRESS_H_ diff --git a/pkg/apm/webrtc/rtc_base/socket_address_pair.h b/pkg/apm/webrtc/rtc_base/socket_address_pair.h new file mode 100644 index 00000000..1da190c8 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/socket_address_pair.h @@ -0,0 +1,51 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SOCKET_ADDRESS_PAIR_H_ +#define RTC_BASE_SOCKET_ADDRESS_PAIR_H_ + +#include + +#include "rtc_base/socket_address.h" + +namespace webrtc { + +// Records a pair (source,destination) of socket addresses. The two addresses +// identify a connection between two machines. (For UDP, this "connection" is +// not maintained explicitly in a socket.) +class SocketAddressPair { + public: + SocketAddressPair() {} + SocketAddressPair(const SocketAddress& srs, const SocketAddress& dest); + + const SocketAddress& source() const { return src_; } + const SocketAddress& destination() const { return dest_; } + + bool operator==(const SocketAddressPair& r) const; + bool operator<(const SocketAddressPair& r) const; + + size_t Hash() const; + + private: + SocketAddress src_; + SocketAddress dest_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::SocketAddressPair; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SOCKET_ADDRESS_PAIR_H_ diff --git a/pkg/apm/webrtc/rtc_base/socket_factory.h b/pkg/apm/webrtc/rtc_base/socket_factory.h new file mode 100644 index 00000000..2aa8acc7 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/socket_factory.h @@ -0,0 +1,36 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SOCKET_FACTORY_H_ +#define RTC_BASE_SOCKET_FACTORY_H_ + +#include "rtc_base/socket.h" + +namespace webrtc { + +class SocketFactory { + public: + virtual ~SocketFactory() {} + + // Returns a new socket. The type can be SOCK_DGRAM and SOCK_STREAM. + virtual Socket* CreateSocket(int family, int type) = 0; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::SocketFactory; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SOCKET_FACTORY_H_ diff --git a/pkg/apm/webrtc/rtc_base/socket_server.h b/pkg/apm/webrtc/rtc_base/socket_server.h new file mode 100644 index 00000000..a655271c --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/socket_server.h @@ -0,0 +1,77 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SOCKET_SERVER_H_ +#define RTC_BASE_SOCKET_SERVER_H_ + +#include + +#include "api/units/time_delta.h" +#include "rtc_base/event.h" +#include "rtc_base/socket_factory.h" + +namespace webrtc { + +class NetworkBinderInterface; +class Thread; +class ThreadManager; + +// Needs to be forward declared because there's a circular dependency between +// NetworkMonitor and Thread. +// TODO(deadbeef): Fix this. + +// Provides the ability to wait for activity on a set of sockets. The Thread +// class provides a nice wrapper on a socket server. +// +// The server is also a socket factory. The sockets it creates will be +// notified of asynchronous I/O from this server's Wait method. +class SocketServer : public SocketFactory { + public: + static constexpr TimeDelta kForever = Event::kForever; + + static std::unique_ptr CreateDefault(); + // When the socket server is installed into a Thread, this function is called + // to allow the socket server to use the thread's message queue for any + // messaging that it might need to perform. It is also called with a null + // argument before the thread is destroyed. + virtual void SetMessageQueue(Thread* /* queue */) {} + + // Sleeps until: + // 1) `max_wait_duration` has elapsed (unless `max_wait_duration` == + // `kForever`) + // 2) WakeUp() is called + // While sleeping, I/O is performed if process_io is true. + virtual bool Wait(TimeDelta max_wait_duration, bool process_io) = 0; + + // Causes the current wait (if one is in progress) to wake up. + virtual void WakeUp() = 0; + + // A network binder will bind the created sockets to a network. + // It is only used in PhysicalSocketServer. + void set_network_binder(NetworkBinderInterface* binder) { + network_binder_ = binder; + } + NetworkBinderInterface* network_binder() const { return network_binder_; } + + private: + NetworkBinderInterface* network_binder_ = nullptr; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::SocketServer; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SOCKET_SERVER_H_ diff --git a/pkg/apm/webrtc/rtc_base/ssl_adapter.h b/pkg/apm/webrtc/rtc_base/ssl_adapter.h new file mode 100644 index 00000000..084e2be7 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ssl_adapter.h @@ -0,0 +1,139 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SSL_ADAPTER_H_ +#define RTC_BASE_SSL_ADAPTER_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/async_socket.h" +#include "rtc_base/checks.h" +#include "rtc_base/socket.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +class SSLAdapter; + +// Class for creating SSL adapters with shared state, e.g., a session cache, +// which allows clients to resume SSL sessions to previously-contacted hosts. +// Clients should create the factory using Create(), set up the factory as +// needed using SetMode, and then call CreateAdapter to create adapters when +// needed. +class SSLAdapterFactory { + public: + virtual ~SSLAdapterFactory() {} + + // Specifies whether TLS or DTLS is to be used for the SSL adapters. + virtual void SetMode(SSLMode mode) = 0; + + // Specify a custom certificate verifier for SSL. + virtual void SetCertVerifier(SSLCertificateVerifier* ssl_cert_verifier) = 0; + + // Set the certificate this socket will present to incoming clients. + // Takes ownership of `identity`. + virtual void SetIdentity(std::unique_ptr identity) = 0; + + // Choose whether the socket acts as a server socket or client socket. + virtual void SetRole(SSLRole role) = 0; + + // Methods that control server certificate verification, used in unit tests. + // Do not call these methods in production code. + virtual void SetIgnoreBadCert(bool ignore) = 0; + + // Creates a new SSL adapter, but from a shared context. + virtual SSLAdapter* CreateAdapter(Socket* socket) = 0; + + static std::unique_ptr Create(); +}; + +// Class that abstracts a client-to-server SSL session. It can be created +// standalone, via SSLAdapter::Create, or through a factory as described above, +// in which case it will share state with other SSLAdapters created from the +// same factory. +// After creation, call StartSSL to initiate the SSL handshake to the server. +class SSLAdapter : public AsyncSocketAdapter { + public: + explicit SSLAdapter(Socket* socket) : AsyncSocketAdapter(socket) {} + + // Methods that control server certificate verification, used in unit tests. + // Do not call these methods in production code. + // TODO(juberti): Remove the opportunistic encryption mechanism in + // BasicPacketSocketFactory that uses this function. + virtual void SetIgnoreBadCert(bool ignore) = 0; + + virtual void SetAlpnProtocols(const std::vector& protos) = 0; + virtual void SetEllipticCurves(const std::vector& curves) = 0; + + [[deprecated("Only TLS is supported by the adapter")]] virtual void SetMode( + SSLMode mode) = 0; + // Specify a custom certificate verifier for SSL. + virtual void SetCertVerifier(SSLCertificateVerifier* ssl_cert_verifier) = 0; + + // Set the certificate this socket will present to incoming clients. + // Takes ownership of `identity`. + virtual void SetIdentity(std::unique_ptr identity) = 0; + + // Choose whether the socket acts as a server socket or client socket. + virtual void SetRole(SSLRole role) = 0; + + // StartSSL returns 0 if successful. + // If StartSSL is called while the socket is closed or connecting, the SSL + // negotiation will begin as soon as the socket connects. + virtual int StartSSL(absl::string_view hostname) = 0; + + // When an SSLAdapterFactory is used, an SSLAdapter may be used to resume + // a previous SSL session, which results in an abbreviated handshake. + // This method, if called after SSL has been established for this adapter, + // indicates whether the current session is a resumption of a previous + // session. + virtual bool IsResumedSession() = 0; + + // Create the default SSL adapter for this platform. On failure, returns null + // and deletes `socket`. Otherwise, the returned SSLAdapter takes ownership + // of `socket`. + static SSLAdapter* Create(Socket* socket); + + private: + // Not supported. + int Listen(int backlog) override { RTC_CHECK(false); } + Socket* Accept(SocketAddress* paddr) override { RTC_CHECK(false); } +}; + +/////////////////////////////////////////////////////////////////////////////// + +// Call this on the main thread, before using SSL. +// Call CleanupSSL when finished with SSL. +RTC_EXPORT bool InitializeSSL(); + +// Call to cleanup additional threads, and also the main thread. +RTC_EXPORT bool CleanupSSL(); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::CleanupSSL; +using ::webrtc::InitializeSSL; +using ::webrtc::SSLAdapter; +using ::webrtc::SSLAdapterFactory; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SSL_ADAPTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/ssl_certificate.h b/pkg/apm/webrtc/rtc_base/ssl_certificate.h new file mode 100644 index 00000000..825953b4 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ssl_certificate.h @@ -0,0 +1,151 @@ +/* + * 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. + */ + +// Generic interface for SSL Certificates, used in both the SSLAdapter +// for TLS TURN connections and the SSLStreamAdapter for DTLS Peer to Peer +// Connections for SRTP Key negotiation and SCTP encryption. + +#ifndef RTC_BASE_SSL_CERTIFICATE_H_ +#define RTC_BASE_SSL_CERTIFICATE_H_ + +#include +#include + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/buffer.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +struct RTC_EXPORT SSLCertificateStats { + SSLCertificateStats(std::string&& fingerprint, + std::string&& fingerprint_algorithm, + std::string&& base64_certificate, + std::unique_ptr issuer); + ~SSLCertificateStats(); + std::string fingerprint; + std::string fingerprint_algorithm; + std::string base64_certificate; + std::unique_ptr issuer; + + std::unique_ptr Copy() const; +}; + +// Abstract interface overridden by SSL library specific +// implementations. + +// A somewhat opaque type used to encapsulate a certificate. +// Wraps the SSL library's notion of a certificate, with reference counting. +// The SSLCertificate object is pretty much immutable once created. +// (The OpenSSL implementation only does reference counting and +// possibly caching of intermediate results.) +class RTC_EXPORT SSLCertificate { + public: + // Parses and builds a certificate from a PEM encoded string. + // Returns null on failure. + // The length of the string representation of the certificate is + // stored in *pem_length if it is non-null, and only if + // parsing was successful. + static std::unique_ptr FromPEMString( + absl::string_view pem_string); + virtual ~SSLCertificate() = default; + + // Returns a new SSLCertificate object instance wrapping the same + // underlying certificate, including its chain if present. + virtual std::unique_ptr Clone() const = 0; + + // Returns a PEM encoded string representation of the certificate. + virtual std::string ToPEMString() const = 0; + + // Provides a DER encoded binary representation of the certificate. + virtual void ToDER(Buffer* der_buffer) const = 0; + + // Gets the name of the digest algorithm that was used to compute this + // certificate's signature. + virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const = 0; + + // Compute the digest of the certificate given algorithm + virtual bool ComputeDigest(absl::string_view algorithm, + Buffer& digest) const = 0; + + // Returns the time in seconds relative to epoch, 1970-01-01T00:00:00Z (UTC), + // or -1 if an expiration time could not be retrieved. + virtual int64_t CertificateExpirationTime() const = 0; + + // Gets information (fingerprint, etc.) about this certificate. This is used + // for certificate stats, see + // https://w3c.github.io/webrtc-stats/#certificatestats-dict*. + std::unique_ptr GetStats() const; +}; + +// SSLCertChain is a simple wrapper for a vector of SSLCertificates. It serves +// primarily to ensure proper memory management (especially deletion) of the +// SSLCertificate pointers. +class RTC_EXPORT SSLCertChain final { + public: + explicit SSLCertChain(std::unique_ptr single_cert); + explicit SSLCertChain(std::vector> certs); + // Allow move semantics for the object. + SSLCertChain(SSLCertChain&&); + SSLCertChain& operator=(SSLCertChain&&); + + ~SSLCertChain(); + + SSLCertChain(const SSLCertChain&) = delete; + SSLCertChain& operator=(const SSLCertChain&) = delete; + + // Vector access methods. + size_t GetSize() const { return certs_.size(); } + + // Returns a temporary reference, only valid until the chain is destroyed. + const SSLCertificate& Get(size_t pos) const { return *(certs_[pos]); } + + // Returns a new SSLCertChain object instance wrapping the same underlying + // certificate chain. + std::unique_ptr Clone() const; + + // Gets information (fingerprint, etc.) about this certificate chain. This is + // used for certificate stats, see + // https://w3c.github.io/webrtc-stats/#certificatestats-dict*. + std::unique_ptr GetStats() const; + + private: + std::vector> certs_; +}; + +// SSLCertificateVerifier provides a simple interface to allow third parties to +// define their own certificate verification code. It is completely independent +// from the underlying SSL implementation. +class SSLCertificateVerifier { + public: + virtual ~SSLCertificateVerifier() = default; + // Returns true if the certificate is valid, else false. It is up to the + // implementer to define what a valid certificate looks like. + virtual bool Verify(const SSLCertificate& certificate) = 0; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::SSLCertChain; +using ::webrtc::SSLCertificate; +using ::webrtc::SSLCertificateStats; +using ::webrtc::SSLCertificateVerifier; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SSL_CERTIFICATE_H_ diff --git a/pkg/apm/webrtc/rtc_base/ssl_fingerprint.h b/pkg/apm/webrtc/rtc_base/ssl_fingerprint.h new file mode 100644 index 00000000..9c9524f1 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ssl_fingerprint.h @@ -0,0 +1,85 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SSL_FINGERPRINT_H_ +#define RTC_BASE_SSL_FINGERPRINT_H_ + +#include +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +struct RTC_EXPORT SSLFingerprint { + // TODO(steveanton): Remove once downstream projects have moved off of this. + static SSLFingerprint* Create(absl::string_view algorithm, + const SSLIdentity* identity); + // TODO(steveanton): Rename to Create once projects have migrated. + static std::unique_ptr CreateUnique( + absl::string_view algorithm, + const SSLIdentity& identity); + + static std::unique_ptr Create(absl::string_view algorithm, + const SSLCertificate& cert); + + // TODO(steveanton): Remove once downstream projects have moved off of this. + static SSLFingerprint* CreateFromRfc4572(absl::string_view algorithm, + absl::string_view fingerprint); + // TODO(steveanton): Rename to CreateFromRfc4572 once projects have migrated. + static std::unique_ptr CreateUniqueFromRfc4572( + absl::string_view algorithm, + absl::string_view fingerprint); + + // Creates a fingerprint from a certificate, using the same digest algorithm + // as the certificate's signature. + static std::unique_ptr CreateFromCertificate( + const RTCCertificate& cert); + + SSLFingerprint(absl::string_view algorithm, + ArrayView digest_view); + // TODO(steveanton): Remove once downstream projects have moved off of this. + SSLFingerprint(absl::string_view algorithm, + const uint8_t* digest_in, + size_t digest_len); + + SSLFingerprint(const SSLFingerprint& from) = default; + SSLFingerprint& operator=(const SSLFingerprint& from) = default; + + bool operator==(const SSLFingerprint& other) const; + + std::string GetRfc4572Fingerprint() const; + + std::string ToString() const; + + std::string algorithm; + CopyOnWriteBuffer digest; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::SSLFingerprint; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SSL_FINGERPRINT_H_ diff --git a/pkg/apm/webrtc/rtc_base/ssl_identity.h b/pkg/apm/webrtc/rtc_base/ssl_identity.h new file mode 100644 index 00000000..2d426e69 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ssl_identity.h @@ -0,0 +1,197 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Handling of certificates and keypairs for SSLStreamAdapter's peer mode. + +#ifndef RTC_BASE_SSL_IDENTITY_H_ +#define RTC_BASE_SSL_IDENTITY_H_ + +#include + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// KT_LAST is intended for vector declarations and loops over all key types; +// it does not represent any key type in itself. +// KT_DEFAULT is used as the default KeyType for KeyParams. +enum KeyType { KT_RSA, KT_ECDSA, KT_LAST, KT_DEFAULT = KT_ECDSA }; + +static const int kRsaDefaultModSize = 2048; +static const int kRsaDefaultExponent = 0x10001; // = 2^16+1 = 65537 +// TODO(bugs.webrtc.org/364338811): raise the bar to 2048 bits. +static const int kRsaMinModSize = 1024; +static const int kRsaMaxModSize = 8192; + +// Certificate default validity lifetime. +static const int kDefaultCertificateLifetimeInSeconds = + 60 * 60 * 24 * 30; // 30 days +// Certificate validity window. +// This is to compensate for slightly incorrect system clocks. +static const int kCertificateWindowInSeconds = -60 * 60 * 24; + +struct RSAParams { + unsigned int mod_size; + unsigned int pub_exp; +}; + +enum ECCurve { EC_NIST_P256, /* EC_FANCY, */ EC_LAST }; + +class RTC_EXPORT KeyParams { + public: + // Generate a KeyParams object from a simple KeyType, using default params. + explicit KeyParams(KeyType key_type = KT_DEFAULT); + + // Generate a a KeyParams for RSA with explicit parameters. + static KeyParams RSA(int mod_size = kRsaDefaultModSize, + int pub_exp = kRsaDefaultExponent); + + // Generate a a KeyParams for ECDSA specifying the curve. + static KeyParams ECDSA(ECCurve curve = EC_NIST_P256); + + // Check validity of a KeyParams object. Since the factory functions have + // no way of returning errors, this function can be called after creation + // to make sure the parameters are OK. + bool IsValid() const; + + RSAParams rsa_params() const; + + ECCurve ec_curve() const; + + KeyType type() const { return type_; } + + private: + KeyType type_; + union { + RSAParams rsa; + ECCurve curve; + } params_; +}; + +// Parameters for generating a certificate. If `common_name` is non-empty, it +// will be used for the certificate's subject and issuer name, otherwise a +// random string will be used. +struct SSLIdentityParams { + std::string common_name; + time_t not_before; // Absolute time since epoch in seconds. + time_t not_after; // Absolute time since epoch in seconds. + KeyParams key_params; +}; + +// Our identity in an SSL negotiation: a keypair and certificate (both +// with the same public key). +// This too is pretty much immutable once created. +class RTC_EXPORT SSLIdentity { + public: + // Generates an identity (keypair and self-signed certificate). If + // `common_name` is non-empty, it will be used for the certificate's subject + // and issuer name, otherwise a random string will be used. The key type and + // parameters are defined in `key_param`. The certificate's lifetime in + // seconds from the current time is defined in `certificate_lifetime`; it + // should be a non-negative number. + // Returns null on failure. + // Caller is responsible for freeing the returned object. + static std::unique_ptr Create(absl::string_view common_name, + const KeyParams& key_param, + time_t certificate_lifetime); + static std::unique_ptr Create(absl::string_view common_name, + const KeyParams& key_param); + static std::unique_ptr Create(absl::string_view common_name, + KeyType key_type); + + // Allows fine-grained control over expiration time. + static std::unique_ptr CreateForTest( + const SSLIdentityParams& params); + + // Construct an identity from a private key and a certificate. + static std::unique_ptr CreateFromPEMStrings( + absl::string_view private_key, + absl::string_view certificate); + + // Construct an identity from a private key and a certificate chain. + static std::unique_ptr CreateFromPEMChainStrings( + absl::string_view private_key, + absl::string_view certificate_chain); + + virtual ~SSLIdentity() {} + + // Returns a new SSLIdentity object instance wrapping the same + // identity information. + std::unique_ptr Clone() const { return CloneInternal(); } + + // Returns a temporary reference to the end-entity (leaf) certificate. + virtual const SSLCertificate& certificate() const = 0; + // Returns a temporary reference to the entire certificate chain. + virtual const SSLCertChain& cert_chain() const = 0; + virtual std::string PrivateKeyToPEMString() const = 0; + virtual std::string PublicKeyToPEMString() const = 0; + + // Helpers for parsing converting between PEM and DER format. + static bool PemToDer(absl::string_view pem_type, + absl::string_view pem_string, + std::string* der); + static std::string DerToPem(absl::string_view pem_type, + const unsigned char* data, + size_t length); + + protected: + virtual std::unique_ptr CloneInternal() const = 0; +}; + +bool operator==(const SSLIdentity& a, const SSLIdentity& b); +bool operator!=(const SSLIdentity& a, const SSLIdentity& b); + +// Convert from ASN1 time as restricted by RFC 5280 to seconds from 1970-01-01 +// 00.00 ("epoch"). If the ASN1 time cannot be read, return -1. The data at +// `s` is not 0-terminated; its char count is defined by `length`. +int64_t ASN1TimeToSec(const unsigned char* s, size_t length, bool long_format); + +extern const char kPemTypeCertificate[]; +extern const char kPemTypeRsaPrivateKey[]; +extern const char kPemTypeEcPrivateKey[]; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::ASN1TimeToSec; +using ::webrtc::EC_LAST; +using ::webrtc::EC_NIST_P256; +using ::webrtc::ECCurve; +using ::webrtc::kCertificateWindowInSeconds; +using ::webrtc::kDefaultCertificateLifetimeInSeconds; +using ::webrtc::KeyParams; +using ::webrtc::KeyType; +using ::webrtc::kPemTypeCertificate; +using ::webrtc::kPemTypeEcPrivateKey; +using ::webrtc::kPemTypeRsaPrivateKey; +using ::webrtc::kRsaDefaultExponent; +using ::webrtc::kRsaDefaultModSize; +using ::webrtc::kRsaMaxModSize; +using ::webrtc::kRsaMinModSize; +using ::webrtc::KT_DEFAULT; +using ::webrtc::KT_ECDSA; +using ::webrtc::KT_LAST; +using ::webrtc::KT_RSA; +using ::webrtc::RSAParams; +using ::webrtc::SSLIdentity; +using ::webrtc::SSLIdentityParams; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SSL_IDENTITY_H_ diff --git a/pkg/apm/webrtc/rtc_base/ssl_roots.h b/pkg/apm/webrtc/rtc_base/ssl_roots.h new file mode 100644 index 00000000..c3f2701b --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ssl_roots.h @@ -0,0 +1,2485 @@ +/* + * 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 RTC_BASE_SSL_ROOTS_H_ +#define RTC_BASE_SSL_ROOTS_H_ + +// This file is the root certificates in C form. + +// It was generated at 2023-05-09T15:05:10+00:00 by the following script: +// `tools_webrtc/sslroots/generate_sslroots.py https://pki.goog/roots.pem` + +// clang-format off +// Don't bother formatting generated code, +// also it would breaks subject/issuer lines. + +// Source bundle `https://pki.goog/roots.pem` digest is [sha256:9c9b9685ad319b9747c3fe69b46a61c11a0efabdfa09ca6a8b0c3da421036d27] + +/* subject: Common Name: Baltimore CyberTrust Root, Organizational Unit: CyberTrust, Organization: Baltimore, Country: IE */ +/* issuer: Common Name: Baltimore CyberTrust Root, Organizational Unit: CyberTrust, Organization: Baltimore, Country: IE */ +/* link: https://crt.sh/?q=16af57a9f676b0ab126095aa5ebadef22ab31119d644ac95cd4b93dbf3f26aeb */ +const unsigned char kCertificateWithFingerprint_16af57a9f676b0ab126095aa5ebadef22ab31119d644ac95cd4b93dbf3f26aeb_certificate[891]={ +0x30,0x82,0x03,0x77,0x30,0x82,0x02,0x5F,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x02, +0x00,0x00,0xB9,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x30,0x5A,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x49, +0x45,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x0A,0x13,0x09,0x42,0x61,0x6C,0x74, +0x69,0x6D,0x6F,0x72,0x65,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0B,0x13,0x0A, +0x43,0x79,0x62,0x65,0x72,0x54,0x72,0x75,0x73,0x74,0x31,0x22,0x30,0x20,0x06,0x03, +0x55,0x04,0x03,0x13,0x19,0x42,0x61,0x6C,0x74,0x69,0x6D,0x6F,0x72,0x65,0x20,0x43, +0x79,0x62,0x65,0x72,0x54,0x72,0x75,0x73,0x74,0x20,0x52,0x6F,0x6F,0x74,0x30,0x1E, +0x17,0x0D,0x30,0x30,0x30,0x35,0x31,0x32,0x31,0x38,0x34,0x36,0x30,0x30,0x5A,0x17, +0x0D,0x32,0x35,0x30,0x35,0x31,0x32,0x32,0x33,0x35,0x39,0x30,0x30,0x5A,0x30,0x5A, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x49,0x45,0x31,0x12,0x30, +0x10,0x06,0x03,0x55,0x04,0x0A,0x13,0x09,0x42,0x61,0x6C,0x74,0x69,0x6D,0x6F,0x72, +0x65,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0B,0x13,0x0A,0x43,0x79,0x62,0x65, +0x72,0x54,0x72,0x75,0x73,0x74,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x03,0x13, +0x19,0x42,0x61,0x6C,0x74,0x69,0x6D,0x6F,0x72,0x65,0x20,0x43,0x79,0x62,0x65,0x72, +0x54,0x72,0x75,0x73,0x74,0x20,0x52,0x6F,0x6F,0x74,0x30,0x82,0x01,0x22,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01, +0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xA3,0x04,0xBB,0x22,0xAB, +0x98,0x3D,0x57,0xE8,0x26,0x72,0x9A,0xB5,0x79,0xD4,0x29,0xE2,0xE1,0xE8,0x95,0x80, +0xB1,0xB0,0xE3,0x5B,0x8E,0x2B,0x29,0x9A,0x64,0xDF,0xA1,0x5D,0xED,0xB0,0x09,0x05, +0x6D,0xDB,0x28,0x2E,0xCE,0x62,0xA2,0x62,0xFE,0xB4,0x88,0xDA,0x12,0xEB,0x38,0xEB, +0x21,0x9D,0xC0,0x41,0x2B,0x01,0x52,0x7B,0x88,0x77,0xD3,0x1C,0x8F,0xC7,0xBA,0xB9, +0x88,0xB5,0x6A,0x09,0xE7,0x73,0xE8,0x11,0x40,0xA7,0xD1,0xCC,0xCA,0x62,0x8D,0x2D, +0xE5,0x8F,0x0B,0xA6,0x50,0xD2,0xA8,0x50,0xC3,0x28,0xEA,0xF5,0xAB,0x25,0x87,0x8A, +0x9A,0x96,0x1C,0xA9,0x67,0xB8,0x3F,0x0C,0xD5,0xF7,0xF9,0x52,0x13,0x2F,0xC2,0x1B, +0xD5,0x70,0x70,0xF0,0x8F,0xC0,0x12,0xCA,0x06,0xCB,0x9A,0xE1,0xD9,0xCA,0x33,0x7A, +0x77,0xD6,0xF8,0xEC,0xB9,0xF1,0x68,0x44,0x42,0x48,0x13,0xD2,0xC0,0xC2,0xA4,0xAE, +0x5E,0x60,0xFE,0xB6,0xA6,0x05,0xFC,0xB4,0xDD,0x07,0x59,0x02,0xD4,0x59,0x18,0x98, +0x63,0xF5,0xA5,0x63,0xE0,0x90,0x0C,0x7D,0x5D,0xB2,0x06,0x7A,0xF3,0x85,0xEA,0xEB, +0xD4,0x03,0xAE,0x5E,0x84,0x3E,0x5F,0xFF,0x15,0xED,0x69,0xBC,0xF9,0x39,0x36,0x72, +0x75,0xCF,0x77,0x52,0x4D,0xF3,0xC9,0x90,0x2C,0xB9,0x3D,0xE5,0xC9,0x23,0x53,0x3F, +0x1F,0x24,0x98,0x21,0x5C,0x07,0x99,0x29,0xBD,0xC6,0x3A,0xEC,0xE7,0x6E,0x86,0x3A, +0x6B,0x97,0x74,0x63,0x33,0xBD,0x68,0x18,0x31,0xF0,0x78,0x8D,0x76,0xBF,0xFC,0x9E, +0x8E,0x5D,0x2A,0x86,0xA7,0x4D,0x90,0xDC,0x27,0x1A,0x39,0x02,0x03,0x01,0x00,0x01, +0xA3,0x45,0x30,0x43,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xE5, +0x9D,0x59,0x30,0x82,0x47,0x58,0xCC,0xAC,0xFA,0x08,0x54,0x36,0x86,0x7B,0x3A,0xB5, +0x04,0x4D,0xF0,0x30,0x12,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x08,0x30, +0x06,0x01,0x01,0xFF,0x02,0x01,0x03,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01, +0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x85,0x0C,0x5D,0x8E,0xE4, +0x6F,0x51,0x68,0x42,0x05,0xA0,0xDD,0xBB,0x4F,0x27,0x25,0x84,0x03,0xBD,0xF7,0x64, +0xFD,0x2D,0xD7,0x30,0xE3,0xA4,0x10,0x17,0xEB,0xDA,0x29,0x29,0xB6,0x79,0x3F,0x76, +0xF6,0x19,0x13,0x23,0xB8,0x10,0x0A,0xF9,0x58,0xA4,0xD4,0x61,0x70,0xBD,0x04,0x61, +0x6A,0x12,0x8A,0x17,0xD5,0x0A,0xBD,0xC5,0xBC,0x30,0x7C,0xD6,0xE9,0x0C,0x25,0x8D, +0x86,0x40,0x4F,0xEC,0xCC,0xA3,0x7E,0x38,0xC6,0x37,0x11,0x4F,0xED,0xDD,0x68,0x31, +0x8E,0x4C,0xD2,0xB3,0x01,0x74,0xEE,0xBE,0x75,0x5E,0x07,0x48,0x1A,0x7F,0x70,0xFF, +0x16,0x5C,0x84,0xC0,0x79,0x85,0xB8,0x05,0xFD,0x7F,0xBE,0x65,0x11,0xA3,0x0F,0xC0, +0x02,0xB4,0xF8,0x52,0x37,0x39,0x04,0xD5,0xA9,0x31,0x7A,0x18,0xBF,0xA0,0x2A,0xF4, +0x12,0x99,0xF7,0xA3,0x45,0x82,0xE3,0x3C,0x5E,0xF5,0x9D,0x9E,0xB5,0xC8,0x9E,0x7C, +0x2E,0xC8,0xA4,0x9E,0x4E,0x08,0x14,0x4B,0x6D,0xFD,0x70,0x6D,0x6B,0x1A,0x63,0xBD, +0x64,0xE6,0x1F,0xB7,0xCE,0xF0,0xF2,0x9F,0x2E,0xBB,0x1B,0xB7,0xF2,0x50,0x88,0x73, +0x92,0xC2,0xE2,0xE3,0x16,0x8D,0x9A,0x32,0x02,0xAB,0x8E,0x18,0xDD,0xE9,0x10,0x11, +0xEE,0x7E,0x35,0xAB,0x90,0xAF,0x3E,0x30,0x94,0x7A,0xD0,0x33,0x3D,0xA7,0x65,0x0F, +0xF5,0xFC,0x8E,0x9E,0x62,0xCF,0x47,0x44,0x2C,0x01,0x5D,0xBB,0x1D,0xB5,0x32,0xD2, +0x47,0xD2,0x38,0x2E,0xD0,0xFE,0x81,0xDC,0x32,0x6A,0x1E,0xB5,0xEE,0x3C,0xD5,0xFC, +0xE7,0x81,0x1D,0x19,0xC3,0x24,0x42,0xEA,0x63,0x39,0xA9, +}; + +/* subject: Common Name: DigiCert Assured ID Root CA, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* issuer: Common Name: DigiCert Assured ID Root CA, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* link: https://crt.sh/?q=3e9099b5015e8f486c00bcea9d111ee721faba355a89bcf1df69561e3dc6325c */ +const unsigned char kCertificateWithFingerprint_3e9099b5015e8f486c00bcea9d111ee721faba355a89bcf1df69561e3dc6325c_certificate[955]={ +0x30,0x82,0x03,0xB7,0x30,0x82,0x02,0x9F,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x0C, +0xE7,0xE0,0xE5,0x17,0xD8,0x46,0xFE,0x8F,0xE5,0x60,0xFC,0x1B,0xF0,0x30,0x39,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x65, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30, +0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77, +0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31, +0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x03,0x13,0x1B,0x44,0x69,0x67,0x69,0x43,0x65, +0x72,0x74,0x20,0x41,0x73,0x73,0x75,0x72,0x65,0x64,0x20,0x49,0x44,0x20,0x52,0x6F, +0x6F,0x74,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x31,0x31,0x30,0x30, +0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x31,0x31,0x31,0x31,0x30,0x30,0x30, +0x30,0x30,0x30,0x30,0x5A,0x30,0x65,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06, +0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44, +0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06, +0x03,0x55,0x04,0x0B,0x13,0x10,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65, +0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x03,0x13, +0x1B,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x41,0x73,0x73,0x75,0x72,0x65, +0x64,0x20,0x49,0x44,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x82,0x01,0x22, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03, +0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xAD,0x0E,0x15, +0xCE,0xE4,0x43,0x80,0x5C,0xB1,0x87,0xF3,0xB7,0x60,0xF9,0x71,0x12,0xA5,0xAE,0xDC, +0x26,0x94,0x88,0xAA,0xF4,0xCE,0xF5,0x20,0x39,0x28,0x58,0x60,0x0C,0xF8,0x80,0xDA, +0xA9,0x15,0x95,0x32,0x61,0x3C,0xB5,0xB1,0x28,0x84,0x8A,0x8A,0xDC,0x9F,0x0A,0x0C, +0x83,0x17,0x7A,0x8F,0x90,0xAC,0x8A,0xE7,0x79,0x53,0x5C,0x31,0x84,0x2A,0xF6,0x0F, +0x98,0x32,0x36,0x76,0xCC,0xDE,0xDD,0x3C,0xA8,0xA2,0xEF,0x6A,0xFB,0x21,0xF2,0x52, +0x61,0xDF,0x9F,0x20,0xD7,0x1F,0xE2,0xB1,0xD9,0xFE,0x18,0x64,0xD2,0x12,0x5B,0x5F, +0xF9,0x58,0x18,0x35,0xBC,0x47,0xCD,0xA1,0x36,0xF9,0x6B,0x7F,0xD4,0xB0,0x38,0x3E, +0xC1,0x1B,0xC3,0x8C,0x33,0xD9,0xD8,0x2F,0x18,0xFE,0x28,0x0F,0xB3,0xA7,0x83,0xD6, +0xC3,0x6E,0x44,0xC0,0x61,0x35,0x96,0x16,0xFE,0x59,0x9C,0x8B,0x76,0x6D,0xD7,0xF1, +0xA2,0x4B,0x0D,0x2B,0xFF,0x0B,0x72,0xDA,0x9E,0x60,0xD0,0x8E,0x90,0x35,0xC6,0x78, +0x55,0x87,0x20,0xA1,0xCF,0xE5,0x6D,0x0A,0xC8,0x49,0x7C,0x31,0x98,0x33,0x6C,0x22, +0xE9,0x87,0xD0,0x32,0x5A,0xA2,0xBA,0x13,0x82,0x11,0xED,0x39,0x17,0x9D,0x99,0x3A, +0x72,0xA1,0xE6,0xFA,0xA4,0xD9,0xD5,0x17,0x31,0x75,0xAE,0x85,0x7D,0x22,0xAE,0x3F, +0x01,0x46,0x86,0xF6,0x28,0x79,0xC8,0xB1,0xDA,0xE4,0x57,0x17,0xC4,0x7E,0x1C,0x0E, +0xB0,0xB4,0x92,0xA6,0x56,0xB3,0xBD,0xB2,0x97,0xED,0xAA,0xA7,0xF0,0xB7,0xC5,0xA8, +0x3F,0x95,0x16,0xD0,0xFF,0xA1,0x96,0xEB,0x08,0x5F,0x18,0x77,0x4F,0x02,0x03,0x01, +0x00,0x01,0xA3,0x63,0x30,0x61,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF, +0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16, +0x04,0x14,0x45,0xEB,0xA2,0xAF,0xF4,0x92,0xCB,0x82,0x31,0x2D,0x51,0x8B,0xA7,0xA7, +0x21,0x9D,0xF3,0x6D,0xC8,0x0F,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30, +0x16,0x80,0x14,0x45,0xEB,0xA2,0xAF,0xF4,0x92,0xCB,0x82,0x31,0x2D,0x51,0x8B,0xA7, +0xA7,0x21,0x9D,0xF3,0x6D,0xC8,0x0F,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0xA2,0x0E,0xBC,0xDF,0xE2, +0xED,0xF0,0xE3,0x72,0x73,0x7A,0x64,0x94,0xBF,0xF7,0x72,0x66,0xD8,0x32,0xE4,0x42, +0x75,0x62,0xAE,0x87,0xEB,0xF2,0xD5,0xD9,0xDE,0x56,0xB3,0x9F,0xCC,0xCE,0x14,0x28, +0xB9,0x0D,0x97,0x60,0x5C,0x12,0x4C,0x58,0xE4,0xD3,0x3D,0x83,0x49,0x45,0x58,0x97, +0x35,0x69,0x1A,0xA8,0x47,0xEA,0x56,0xC6,0x79,0xAB,0x12,0xD8,0x67,0x81,0x84,0xDF, +0x7F,0x09,0x3C,0x94,0xE6,0xB8,0x26,0x2C,0x20,0xBD,0x3D,0xB3,0x28,0x89,0xF7,0x5F, +0xFF,0x22,0xE2,0x97,0x84,0x1F,0xE9,0x65,0xEF,0x87,0xE0,0xDF,0xC1,0x67,0x49,0xB3, +0x5D,0xEB,0xB2,0x09,0x2A,0xEB,0x26,0xED,0x78,0xBE,0x7D,0x3F,0x2B,0xF3,0xB7,0x26, +0x35,0x6D,0x5F,0x89,0x01,0xB6,0x49,0x5B,0x9F,0x01,0x05,0x9B,0xAB,0x3D,0x25,0xC1, +0xCC,0xB6,0x7F,0xC2,0xF1,0x6F,0x86,0xC6,0xFA,0x64,0x68,0xEB,0x81,0x2D,0x94,0xEB, +0x42,0xB7,0xFA,0x8C,0x1E,0xDD,0x62,0xF1,0xBE,0x50,0x67,0xB7,0x6C,0xBD,0xF3,0xF1, +0x1F,0x6B,0x0C,0x36,0x07,0x16,0x7F,0x37,0x7C,0xA9,0x5B,0x6D,0x7A,0xF1,0x12,0x46, +0x60,0x83,0xD7,0x27,0x04,0xBE,0x4B,0xCE,0x97,0xBE,0xC3,0x67,0x2A,0x68,0x11,0xDF, +0x80,0xE7,0x0C,0x33,0x66,0xBF,0x13,0x0D,0x14,0x6E,0xF3,0x7F,0x1F,0x63,0x10,0x1E, +0xFA,0x8D,0x1B,0x25,0x6D,0x6C,0x8F,0xA5,0xB7,0x61,0x01,0xB1,0xD2,0xA3,0x26,0xA1, +0x10,0x71,0x9D,0xAD,0xE2,0xC3,0xF9,0xC3,0x99,0x51,0xB7,0x2B,0x07,0x08,0xCE,0x2E, +0xE6,0x50,0xB2,0xA7,0xFA,0x0A,0x45,0x2F,0xA2,0xF0,0xF2, +}; + +/* subject: Common Name: DigiCert Assured ID Root G2, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* issuer: Common Name: DigiCert Assured ID Root G2, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* link: https://crt.sh/?q=7d05ebb682339f8c9451ee094eebfefa7953a114edb2f44949452fab7d2fc185 */ +const unsigned char kCertificateWithFingerprint_7d05ebb682339f8c9451ee094eebfefa7953a114edb2f44949452fab7d2fc185_certificate[922]={ +0x30,0x82,0x03,0x96,0x30,0x82,0x02,0x7E,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x0B, +0x93,0x1C,0x3A,0xD6,0x39,0x67,0xEA,0x67,0x23,0xBF,0xC3,0xAF,0x9A,0xF4,0x4B,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x65, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30, +0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77, +0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31, +0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x03,0x13,0x1B,0x44,0x69,0x67,0x69,0x43,0x65, +0x72,0x74,0x20,0x41,0x73,0x73,0x75,0x72,0x65,0x64,0x20,0x49,0x44,0x20,0x52,0x6F, +0x6F,0x74,0x20,0x47,0x32,0x30,0x1E,0x17,0x0D,0x31,0x33,0x30,0x38,0x30,0x31,0x31, +0x32,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31,0x31,0x35,0x31,0x32, +0x30,0x30,0x30,0x30,0x5A,0x30,0x65,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06, +0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44, +0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06, +0x03,0x55,0x04,0x0B,0x13,0x10,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65, +0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x03,0x13, +0x1B,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x41,0x73,0x73,0x75,0x72,0x65, +0x64,0x20,0x49,0x44,0x20,0x52,0x6F,0x6F,0x74,0x20,0x47,0x32,0x30,0x82,0x01,0x22, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03, +0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xD9,0xE7,0x28, +0x2F,0x52,0x3F,0x36,0x72,0x49,0x88,0x93,0x34,0xF3,0xF8,0x6A,0x1E,0x31,0x54,0x80, +0x9F,0xAD,0x54,0x41,0xB5,0x47,0xDF,0x96,0xA8,0xD4,0xAF,0x80,0x2D,0xB9,0x0A,0xCF, +0x75,0xFD,0x89,0xA5,0x7D,0x24,0xFA,0xE3,0x22,0x0C,0x2B,0xBC,0x95,0x17,0x0B,0x33, +0xBF,0x19,0x4D,0x41,0x06,0x90,0x00,0xBD,0x0C,0x4D,0x10,0xFE,0x07,0xB5,0xE7,0x1C, +0x6E,0x22,0x55,0x31,0x65,0x97,0xBD,0xD3,0x17,0xD2,0x1E,0x62,0xF3,0xDB,0xEA,0x6C, +0x50,0x8C,0x3F,0x84,0x0C,0x96,0xCF,0xB7,0xCB,0x03,0xE0,0xCA,0x6D,0xA1,0x14,0x4C, +0x1B,0x89,0xDD,0xED,0x00,0xB0,0x52,0x7C,0xAF,0x91,0x6C,0xB1,0x38,0x13,0xD1,0xE9, +0x12,0x08,0xC0,0x00,0xB0,0x1C,0x2B,0x11,0xDA,0x77,0x70,0x36,0x9B,0xAE,0xCE,0x79, +0x87,0xDC,0x82,0x70,0xE6,0x09,0x74,0x70,0x55,0x69,0xAF,0xA3,0x68,0x9F,0xBF,0xDD, +0xB6,0x79,0xB3,0xF2,0x9D,0x70,0x29,0x55,0xF4,0xAB,0xFF,0x95,0x61,0xF3,0xC9,0x40, +0x6F,0x1D,0xD1,0xBE,0x93,0xBB,0xD3,0x88,0x2A,0xBB,0x9D,0xBF,0x72,0x5A,0x56,0x71, +0x3B,0x3F,0xD4,0xF3,0xD1,0x0A,0xFE,0x28,0xEF,0xA3,0xEE,0xD9,0x99,0xAF,0x03,0xD3, +0x8F,0x60,0xB7,0xF2,0x92,0xA1,0xB1,0xBD,0x89,0x89,0x1F,0x30,0xCD,0xC3,0xA6,0x2E, +0x62,0x33,0xAE,0x16,0x02,0x77,0x44,0x5A,0xE7,0x81,0x0A,0x3C,0xA7,0x44,0x2E,0x79, +0xB8,0x3F,0x04,0xBC,0x5C,0xA0,0x87,0xE1,0x1B,0xAF,0x51,0x8E,0xCD,0xEC,0x2C,0xFA, +0xF8,0xFE,0x6D,0xF0,0x3A,0x7C,0xAA,0x8B,0xE4,0x67,0x95,0x31,0x8D,0x02,0x03,0x01, +0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF, +0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01, +0xFF,0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16, +0x04,0x14,0xCE,0xC3,0x4A,0xB9,0x99,0x55,0xF2,0xB8,0xDB,0x60,0xBF,0xA9,0x7E,0xBD, +0x56,0xB5,0x97,0x36,0xA7,0xD6,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0xCA,0xA5,0x55,0x8C,0xE3,0xC8, +0x41,0x6E,0x69,0x27,0xA7,0x75,0x11,0xEF,0x3C,0x86,0x36,0x6F,0xD2,0x9D,0xC6,0x78, +0x38,0x1D,0x69,0x96,0xA2,0x92,0x69,0x2E,0x38,0x6C,0x9B,0x7D,0x04,0xD4,0x89,0xA5, +0xB1,0x31,0x37,0x8A,0xC9,0x21,0xCC,0xAB,0x6C,0xCD,0x8B,0x1C,0x9A,0xD6,0xBF,0x48, +0xD2,0x32,0x66,0xC1,0x8A,0xC0,0xF3,0x2F,0x3A,0xEF,0xC0,0xE3,0xD4,0x91,0x86,0xD1, +0x50,0xE3,0x03,0xDB,0x73,0x77,0x6F,0x4A,0x39,0x53,0xED,0xDE,0x26,0xC7,0xB5,0x7D, +0xAF,0x2B,0x42,0xD1,0x75,0x62,0xE3,0x4A,0x2B,0x02,0xC7,0x50,0x4B,0xE0,0x69,0xE2, +0x96,0x6C,0x0E,0x44,0x66,0x10,0x44,0x8F,0xAD,0x05,0xEB,0xF8,0x79,0xAC,0xA6,0x1B, +0xE8,0x37,0x34,0x9D,0x53,0xC9,0x61,0xAA,0xA2,0x52,0xAF,0x4A,0x70,0x16,0x86,0xC2, +0x3A,0xC8,0xB1,0x13,0x70,0x36,0xD8,0xCF,0xEE,0xF4,0x0A,0x34,0xD5,0x5B,0x4C,0xFD, +0x07,0x9C,0xA2,0xBA,0xD9,0x01,0x72,0x5C,0xF3,0x4D,0xC1,0xDD,0x0E,0xB1,0x1C,0x0D, +0xC4,0x63,0xBE,0xAD,0xF4,0x14,0xFB,0x89,0xEC,0xA2,0x41,0x0E,0x4C,0xCC,0xC8,0x57, +0x40,0xD0,0x6E,0x03,0xAA,0xCD,0x0C,0x8E,0x89,0x99,0x99,0x6C,0xF0,0x3C,0x30,0xAF, +0x38,0xDF,0x6F,0xBC,0xA3,0xBE,0x29,0x20,0x27,0xAB,0x74,0xFF,0x13,0x22,0x78,0xDE, +0x97,0x52,0x55,0x1E,0x83,0xB5,0x54,0x20,0x03,0xEE,0xAE,0xC0,0x4F,0x56,0xDE,0x37, +0xCC,0xC3,0x7F,0xAA,0x04,0x27,0xBB,0xD3,0x77,0xB8,0x62,0xDB,0x17,0x7C,0x9C,0x28, +0x22,0x13,0x73,0x6C,0xCF,0x26,0xF5,0x8A,0x29,0xE7, +}; + +/* subject: Common Name: DigiCert Assured ID Root G3, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* issuer: Common Name: DigiCert Assured ID Root G3, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* link: https://crt.sh/?q=7e37cb8b4c47090cab36551ba6f45db840680fba166a952db100717f43053fc2 */ +const unsigned char kCertificateWithFingerprint_7e37cb8b4c47090cab36551ba6f45db840680fba166a952db100717f43053fc2_certificate[586]={ +0x30,0x82,0x02,0x46,0x30,0x82,0x01,0xCD,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x0B, +0xA1,0x5A,0xFA,0x1D,0xDF,0xA0,0xB5,0x49,0x44,0xAF,0xCD,0x24,0xA0,0x6C,0xEC,0x30, +0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x65,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,0x03, +0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49,0x6E, +0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,0x77,0x77,0x2E, +0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x24,0x30,0x22, +0x06,0x03,0x55,0x04,0x03,0x13,0x1B,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20, +0x41,0x73,0x73,0x75,0x72,0x65,0x64,0x20,0x49,0x44,0x20,0x52,0x6F,0x6F,0x74,0x20, +0x47,0x33,0x30,0x1E,0x17,0x0D,0x31,0x33,0x30,0x38,0x30,0x31,0x31,0x32,0x30,0x30, +0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31,0x31,0x35,0x31,0x32,0x30,0x30,0x30, +0x30,0x5A,0x30,0x65,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55, +0x53,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69, +0x43,0x65,0x72,0x74,0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04, +0x0B,0x13,0x10,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E, +0x63,0x6F,0x6D,0x31,0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x03,0x13,0x1B,0x44,0x69, +0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x41,0x73,0x73,0x75,0x72,0x65,0x64,0x20,0x49, +0x44,0x20,0x52,0x6F,0x6F,0x74,0x20,0x47,0x33,0x30,0x76,0x30,0x10,0x06,0x07,0x2A, +0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05,0x2B,0x81,0x04,0x00,0x22,0x03,0x62,0x00, +0x04,0x19,0xE7,0xBC,0xAC,0x44,0x65,0xED,0xCD,0xB8,0x3F,0x58,0xFB,0x8D,0xB1,0x57, +0xA9,0x44,0x2D,0x05,0x15,0xF2,0xEF,0x0B,0xFF,0x10,0x74,0x9F,0xB5,0x62,0x52,0x5F, +0x66,0x7E,0x1F,0xE5,0xDC,0x1B,0x45,0x79,0x0B,0xCC,0xC6,0x53,0x0A,0x9D,0x8D,0x5D, +0x02,0xD9,0xA9,0x59,0xDE,0x02,0x5A,0xF6,0x95,0x2A,0x0E,0x8D,0x38,0x4A,0x8A,0x49, +0xC6,0xBC,0xC6,0x03,0x38,0x07,0x5F,0x55,0xDA,0x7E,0x09,0x6E,0xE2,0x7F,0x5E,0xD0, +0x45,0x20,0x0F,0x59,0x76,0x10,0xD6,0xA0,0x24,0xF0,0x2D,0xDE,0x36,0xF2,0x6C,0x29, +0x39,0xA3,0x42,0x30,0x40,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04, +0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04, +0x14,0xCB,0xD0,0xBD,0xA9,0xE1,0x98,0x05,0x51,0xA1,0x4D,0x37,0xA2,0x83,0x79,0xCE, +0x8D,0x1D,0x2A,0xE4,0x84,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03, +0x03,0x03,0x67,0x00,0x30,0x64,0x02,0x30,0x25,0xA4,0x81,0x45,0x02,0x6B,0x12,0x4B, +0x75,0x74,0x4F,0xC8,0x23,0xE3,0x70,0xF2,0x75,0x72,0xDE,0x7C,0x89,0xF0,0xCF,0x91, +0x72,0x61,0x9E,0x5E,0x10,0x92,0x59,0x56,0xB9,0x83,0xC7,0x10,0xE7,0x38,0xE9,0x58, +0x26,0x36,0x7D,0xD5,0xE4,0x34,0x86,0x39,0x02,0x30,0x7C,0x36,0x53,0xF0,0x30,0xE5, +0x62,0x63,0x3A,0x99,0xE2,0xB6,0xA3,0x3B,0x9B,0x34,0xFA,0x1E,0xDA,0x10,0x92,0x71, +0x5E,0x91,0x13,0xA7,0xDD,0xA4,0x6E,0x92,0xCC,0x32,0xD6,0xF5,0x21,0x66,0xC7,0x2F, +0xEA,0x96,0x63,0x6A,0x65,0x45,0x92,0x95,0x01,0xB4, +}; + +/* subject: Common Name: DigiCert Global Root CA, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* issuer: Common Name: DigiCert Global Root CA, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* link: https://crt.sh/?q=4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161 */ +const unsigned char kCertificateWithFingerprint_4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161_certificate[947]={ +0x30,0x82,0x03,0xAF,0x30,0x82,0x02,0x97,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x08, +0x3B,0xE0,0x56,0x90,0x42,0x46,0xB1,0xA1,0x75,0x6A,0xC9,0x59,0x91,0xC7,0x4A,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x61, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30, +0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77, +0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31, +0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x13,0x17,0x44,0x69,0x67,0x69,0x43,0x65, +0x72,0x74,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43, +0x41,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x31,0x31,0x30,0x30,0x30,0x30,0x30,0x30, +0x30,0x5A,0x17,0x0D,0x33,0x31,0x31,0x31,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x30, +0x5A,0x30,0x61,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53, +0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43, +0x65,0x72,0x74,0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B, +0x13,0x10,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63, +0x6F,0x6D,0x31,0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x13,0x17,0x44,0x69,0x67, +0x69,0x43,0x65,0x72,0x74,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x52,0x6F,0x6F, +0x74,0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A, +0x02,0x82,0x01,0x01,0x00,0xE2,0x3B,0xE1,0x11,0x72,0xDE,0xA8,0xA4,0xD3,0xA3,0x57, +0xAA,0x50,0xA2,0x8F,0x0B,0x77,0x90,0xC9,0xA2,0xA5,0xEE,0x12,0xCE,0x96,0x5B,0x01, +0x09,0x20,0xCC,0x01,0x93,0xA7,0x4E,0x30,0xB7,0x53,0xF7,0x43,0xC4,0x69,0x00,0x57, +0x9D,0xE2,0x8D,0x22,0xDD,0x87,0x06,0x40,0x00,0x81,0x09,0xCE,0xCE,0x1B,0x83,0xBF, +0xDF,0xCD,0x3B,0x71,0x46,0xE2,0xD6,0x66,0xC7,0x05,0xB3,0x76,0x27,0x16,0x8F,0x7B, +0x9E,0x1E,0x95,0x7D,0xEE,0xB7,0x48,0xA3,0x08,0xDA,0xD6,0xAF,0x7A,0x0C,0x39,0x06, +0x65,0x7F,0x4A,0x5D,0x1F,0xBC,0x17,0xF8,0xAB,0xBE,0xEE,0x28,0xD7,0x74,0x7F,0x7A, +0x78,0x99,0x59,0x85,0x68,0x6E,0x5C,0x23,0x32,0x4B,0xBF,0x4E,0xC0,0xE8,0x5A,0x6D, +0xE3,0x70,0xBF,0x77,0x10,0xBF,0xFC,0x01,0xF6,0x85,0xD9,0xA8,0x44,0x10,0x58,0x32, +0xA9,0x75,0x18,0xD5,0xD1,0xA2,0xBE,0x47,0xE2,0x27,0x6A,0xF4,0x9A,0x33,0xF8,0x49, +0x08,0x60,0x8B,0xD4,0x5F,0xB4,0x3A,0x84,0xBF,0xA1,0xAA,0x4A,0x4C,0x7D,0x3E,0xCF, +0x4F,0x5F,0x6C,0x76,0x5E,0xA0,0x4B,0x37,0x91,0x9E,0xDC,0x22,0xE6,0x6D,0xCE,0x14, +0x1A,0x8E,0x6A,0xCB,0xFE,0xCD,0xB3,0x14,0x64,0x17,0xC7,0x5B,0x29,0x9E,0x32,0xBF, +0xF2,0xEE,0xFA,0xD3,0x0B,0x42,0xD4,0xAB,0xB7,0x41,0x32,0xDA,0x0C,0xD4,0xEF,0xF8, +0x81,0xD5,0xBB,0x8D,0x58,0x3F,0xB5,0x1B,0xE8,0x49,0x28,0xA2,0x70,0xDA,0x31,0x04, +0xDD,0xF7,0xB2,0x16,0xF2,0x4C,0x0A,0x4E,0x07,0xA8,0xED,0x4A,0x3D,0x5E,0xB5,0x7F, +0xA3,0x90,0xC3,0xAF,0x27,0x02,0x03,0x01,0x00,0x01,0xA3,0x63,0x30,0x61,0x30,0x0E, +0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0F, +0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30, +0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x03,0xDE,0x50,0x35,0x56,0xD1, +0x4C,0xBB,0x66,0xF0,0xA3,0xE2,0x1B,0x1B,0xC3,0x97,0xB2,0x3D,0xD1,0x55,0x30,0x1F, +0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x03,0xDE,0x50,0x35,0x56, +0xD1,0x4C,0xBB,0x66,0xF0,0xA3,0xE2,0x1B,0x1B,0xC3,0x97,0xB2,0x3D,0xD1,0x55,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82, +0x01,0x01,0x00,0xCB,0x9C,0x37,0xAA,0x48,0x13,0x12,0x0A,0xFA,0xDD,0x44,0x9C,0x4F, +0x52,0xB0,0xF4,0xDF,0xAE,0x04,0xF5,0x79,0x79,0x08,0xA3,0x24,0x18,0xFC,0x4B,0x2B, +0x84,0xC0,0x2D,0xB9,0xD5,0xC7,0xFE,0xF4,0xC1,0x1F,0x58,0xCB,0xB8,0x6D,0x9C,0x7A, +0x74,0xE7,0x98,0x29,0xAB,0x11,0xB5,0xE3,0x70,0xA0,0xA1,0xCD,0x4C,0x88,0x99,0x93, +0x8C,0x91,0x70,0xE2,0xAB,0x0F,0x1C,0xBE,0x93,0xA9,0xFF,0x63,0xD5,0xE4,0x07,0x60, +0xD3,0xA3,0xBF,0x9D,0x5B,0x09,0xF1,0xD5,0x8E,0xE3,0x53,0xF4,0x8E,0x63,0xFA,0x3F, +0xA7,0xDB,0xB4,0x66,0xDF,0x62,0x66,0xD6,0xD1,0x6E,0x41,0x8D,0xF2,0x2D,0xB5,0xEA, +0x77,0x4A,0x9F,0x9D,0x58,0xE2,0x2B,0x59,0xC0,0x40,0x23,0xED,0x2D,0x28,0x82,0x45, +0x3E,0x79,0x54,0x92,0x26,0x98,0xE0,0x80,0x48,0xA8,0x37,0xEF,0xF0,0xD6,0x79,0x60, +0x16,0xDE,0xAC,0xE8,0x0E,0xCD,0x6E,0xAC,0x44,0x17,0x38,0x2F,0x49,0xDA,0xE1,0x45, +0x3E,0x2A,0xB9,0x36,0x53,0xCF,0x3A,0x50,0x06,0xF7,0x2E,0xE8,0xC4,0x57,0x49,0x6C, +0x61,0x21,0x18,0xD5,0x04,0xAD,0x78,0x3C,0x2C,0x3A,0x80,0x6B,0xA7,0xEB,0xAF,0x15, +0x14,0xE9,0xD8,0x89,0xC1,0xB9,0x38,0x6C,0xE2,0x91,0x6C,0x8A,0xFF,0x64,0xB9,0x77, +0x25,0x57,0x30,0xC0,0x1B,0x24,0xA3,0xE1,0xDC,0xE9,0xDF,0x47,0x7C,0xB5,0xB4,0x24, +0x08,0x05,0x30,0xEC,0x2D,0xBD,0x0B,0xBF,0x45,0xBF,0x50,0xB9,0xA9,0xF3,0xEB,0x98, +0x01,0x12,0xAD,0xC8,0x88,0xC6,0x98,0x34,0x5F,0x8D,0x0A,0x3C,0xC6,0xE9,0xD5,0x95, +0x95,0x6D,0xDE, +}; + +/* subject: Common Name: DigiCert Global Root G2, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* issuer: Common Name: DigiCert Global Root G2, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* link: https://crt.sh/?q=cb3ccbb76031e5e0138f8dd39a23f9de47ffc35e43c1144cea27d46a5ab1cb5f */ +const unsigned char kCertificateWithFingerprint_cb3ccbb76031e5e0138f8dd39a23f9de47ffc35e43c1144cea27d46a5ab1cb5f_certificate[914]={ +0x30,0x82,0x03,0x8E,0x30,0x82,0x02,0x76,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x03, +0x3A,0xF1,0xE6,0xA7,0x11,0xA9,0xA0,0xBB,0x28,0x64,0xB1,0x1D,0x09,0xFA,0xE5,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x61, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30, +0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77, +0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31, +0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x13,0x17,0x44,0x69,0x67,0x69,0x43,0x65, +0x72,0x74,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x52,0x6F,0x6F,0x74,0x20,0x47, +0x32,0x30,0x1E,0x17,0x0D,0x31,0x33,0x30,0x38,0x30,0x31,0x31,0x32,0x30,0x30,0x30, +0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31,0x31,0x35,0x31,0x32,0x30,0x30,0x30,0x30, +0x5A,0x30,0x61,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53, +0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43, +0x65,0x72,0x74,0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B, +0x13,0x10,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63, +0x6F,0x6D,0x31,0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x13,0x17,0x44,0x69,0x67, +0x69,0x43,0x65,0x72,0x74,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x52,0x6F,0x6F, +0x74,0x20,0x47,0x32,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A, +0x02,0x82,0x01,0x01,0x00,0xBB,0x37,0xCD,0x34,0xDC,0x7B,0x6B,0xC9,0xB2,0x68,0x90, +0xAD,0x4A,0x75,0xFF,0x46,0xBA,0x21,0x0A,0x08,0x8D,0xF5,0x19,0x54,0xC9,0xFB,0x88, +0xDB,0xF3,0xAE,0xF2,0x3A,0x89,0x91,0x3C,0x7A,0xE6,0xAB,0x06,0x1A,0x6B,0xCF,0xAC, +0x2D,0xE8,0x5E,0x09,0x24,0x44,0xBA,0x62,0x9A,0x7E,0xD6,0xA3,0xA8,0x7E,0xE0,0x54, +0x75,0x20,0x05,0xAC,0x50,0xB7,0x9C,0x63,0x1A,0x6C,0x30,0xDC,0xDA,0x1F,0x19,0xB1, +0xD7,0x1E,0xDE,0xFD,0xD7,0xE0,0xCB,0x94,0x83,0x37,0xAE,0xEC,0x1F,0x43,0x4E,0xDD, +0x7B,0x2C,0xD2,0xBD,0x2E,0xA5,0x2F,0xE4,0xA9,0xB8,0xAD,0x3A,0xD4,0x99,0xA4,0xB6, +0x25,0xE9,0x9B,0x6B,0x00,0x60,0x92,0x60,0xFF,0x4F,0x21,0x49,0x18,0xF7,0x67,0x90, +0xAB,0x61,0x06,0x9C,0x8F,0xF2,0xBA,0xE9,0xB4,0xE9,0x92,0x32,0x6B,0xB5,0xF3,0x57, +0xE8,0x5D,0x1B,0xCD,0x8C,0x1D,0xAB,0x95,0x04,0x95,0x49,0xF3,0x35,0x2D,0x96,0xE3, +0x49,0x6D,0xDD,0x77,0xE3,0xFB,0x49,0x4B,0xB4,0xAC,0x55,0x07,0xA9,0x8F,0x95,0xB3, +0xB4,0x23,0xBB,0x4C,0x6D,0x45,0xF0,0xF6,0xA9,0xB2,0x95,0x30,0xB4,0xFD,0x4C,0x55, +0x8C,0x27,0x4A,0x57,0x14,0x7C,0x82,0x9D,0xCD,0x73,0x92,0xD3,0x16,0x4A,0x06,0x0C, +0x8C,0x50,0xD1,0x8F,0x1E,0x09,0xBE,0x17,0xA1,0xE6,0x21,0xCA,0xFD,0x83,0xE5,0x10, +0xBC,0x83,0xA5,0x0A,0xC4,0x67,0x28,0xF6,0x73,0x14,0x14,0x3D,0x46,0x76,0xC3,0x87, +0x14,0x89,0x21,0x34,0x4D,0xAF,0x0F,0x45,0x0C,0xA6,0x49,0xA1,0xBA,0xBB,0x9C,0xC5, +0xB1,0x33,0x83,0x29,0x85,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x0F, +0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30, +0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x86,0x30, +0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x4E,0x22,0x54,0x20,0x18,0x95, +0xE6,0xE3,0x6E,0xE6,0x0F,0xFA,0xFA,0xB9,0x12,0xED,0x06,0x17,0x8F,0x39,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01, +0x01,0x00,0x60,0x67,0x28,0x94,0x6F,0x0E,0x48,0x63,0xEB,0x31,0xDD,0xEA,0x67,0x18, +0xD5,0x89,0x7D,0x3C,0xC5,0x8B,0x4A,0x7F,0xE9,0xBE,0xDB,0x2B,0x17,0xDF,0xB0,0x5F, +0x73,0x77,0x2A,0x32,0x13,0x39,0x81,0x67,0x42,0x84,0x23,0xF2,0x45,0x67,0x35,0xEC, +0x88,0xBF,0xF8,0x8F,0xB0,0x61,0x0C,0x34,0xA4,0xAE,0x20,0x4C,0x84,0xC6,0xDB,0xF8, +0x35,0xE1,0x76,0xD9,0xDF,0xA6,0x42,0xBB,0xC7,0x44,0x08,0x86,0x7F,0x36,0x74,0x24, +0x5A,0xDA,0x6C,0x0D,0x14,0x59,0x35,0xBD,0xF2,0x49,0xDD,0xB6,0x1F,0xC9,0xB3,0x0D, +0x47,0x2A,0x3D,0x99,0x2F,0xBB,0x5C,0xBB,0xB5,0xD4,0x20,0xE1,0x99,0x5F,0x53,0x46, +0x15,0xDB,0x68,0x9B,0xF0,0xF3,0x30,0xD5,0x3E,0x31,0xE2,0x8D,0x84,0x9E,0xE3,0x8A, +0xDA,0xDA,0x96,0x3E,0x35,0x13,0xA5,0x5F,0xF0,0xF9,0x70,0x50,0x70,0x47,0x41,0x11, +0x57,0x19,0x4E,0xC0,0x8F,0xAE,0x06,0xC4,0x95,0x13,0x17,0x2F,0x1B,0x25,0x9F,0x75, +0xF2,0xB1,0x8E,0x99,0xA1,0x6F,0x13,0xB1,0x41,0x71,0xFE,0x88,0x2A,0xC8,0x4F,0x10, +0x20,0x55,0xD7,0xF3,0x14,0x45,0xE5,0xE0,0x44,0xF4,0xEA,0x87,0x95,0x32,0x93,0x0E, +0xFE,0x53,0x46,0xFA,0x2C,0x9D,0xFF,0x8B,0x22,0xB9,0x4B,0xD9,0x09,0x45,0xA4,0xDE, +0xA4,0xB8,0x9A,0x58,0xDD,0x1B,0x7D,0x52,0x9F,0x8E,0x59,0x43,0x88,0x81,0xA4,0x9E, +0x26,0xD5,0x6F,0xAD,0xDD,0x0D,0xC6,0x37,0x7D,0xED,0x03,0x92,0x1B,0xE5,0x77,0x5F, +0x76,0xEE,0x3C,0x8D,0xC4,0x5D,0x56,0x5B,0xA2,0xD9,0x66,0x6E,0xB3,0x35,0x37,0xE5, +0x32,0xB6, +}; + +/* subject: Common Name: DigiCert Global Root G3, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* issuer: Common Name: DigiCert Global Root G3, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* link: https://crt.sh/?q=31ad6648f8104138c738f39ea4320133393e3a18cc02296ef97c2ac9ef6731d0 */ +const unsigned char kCertificateWithFingerprint_31ad6648f8104138c738f39ea4320133393e3a18cc02296ef97c2ac9ef6731d0_certificate[579]={ +0x30,0x82,0x02,0x3F,0x30,0x82,0x01,0xC5,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x05, +0x55,0x56,0xBC,0xF2,0x5E,0xA4,0x35,0x35,0xC3,0xA4,0x0F,0xD5,0xAB,0x45,0x72,0x30, +0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x61,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,0x03, +0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49,0x6E, +0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,0x77,0x77,0x2E, +0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x20,0x30,0x1E, +0x06,0x03,0x55,0x04,0x03,0x13,0x17,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20, +0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x52,0x6F,0x6F,0x74,0x20,0x47,0x33,0x30,0x1E, +0x17,0x0D,0x31,0x33,0x30,0x38,0x30,0x31,0x31,0x32,0x30,0x30,0x30,0x30,0x5A,0x17, +0x0D,0x33,0x38,0x30,0x31,0x31,0x35,0x31,0x32,0x30,0x30,0x30,0x30,0x5A,0x30,0x61, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30, +0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77, +0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31, +0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x13,0x17,0x44,0x69,0x67,0x69,0x43,0x65, +0x72,0x74,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x52,0x6F,0x6F,0x74,0x20,0x47, +0x33,0x30,0x76,0x30,0x10,0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05, +0x2B,0x81,0x04,0x00,0x22,0x03,0x62,0x00,0x04,0xDD,0xA7,0xD9,0xBB,0x8A,0xB8,0x0B, +0xFB,0x0B,0x7F,0x21,0xD2,0xF0,0xBE,0xBE,0x73,0xF3,0x33,0x5D,0x1A,0xBC,0x34,0xEA, +0xDE,0xC6,0x9B,0xBC,0xD0,0x95,0xF6,0xF0,0xCC,0xD0,0x0B,0xBA,0x61,0x5B,0x51,0x46, +0x7E,0x9E,0x2D,0x9F,0xEE,0x8E,0x63,0x0C,0x17,0xEC,0x07,0x70,0xF5,0xCF,0x84,0x2E, +0x40,0x83,0x9C,0xE8,0x3F,0x41,0x6D,0x3B,0xAD,0xD3,0xA4,0x14,0x59,0x36,0x78,0x9D, +0x03,0x43,0xEE,0x10,0x13,0x6C,0x72,0xDE,0xAE,0x88,0xA7,0xA1,0x6B,0xB5,0x43,0xCE, +0x67,0xDC,0x23,0xFF,0x03,0x1C,0xA3,0xE2,0x3E,0xA3,0x42,0x30,0x40,0x30,0x0F,0x06, +0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E, +0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x1D, +0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xB3,0xDB,0x48,0xA4,0xF9,0xA1,0xC5, +0xD8,0xAE,0x36,0x41,0xCC,0x11,0x63,0x69,0x62,0x29,0xBC,0x4B,0xC6,0x30,0x0A,0x06, +0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x03,0x68,0x00,0x30,0x65,0x02,0x31, +0x00,0xAD,0xBC,0xF2,0x6C,0x3F,0x12,0x4A,0xD1,0x2D,0x39,0xC3,0x0A,0x09,0x97,0x73, +0xF4,0x88,0x36,0x8C,0x88,0x27,0xBB,0xE6,0x88,0x8D,0x50,0x85,0xA7,0x63,0xF9,0x9E, +0x32,0xDE,0x66,0x93,0x0F,0xF1,0xCC,0xB1,0x09,0x8F,0xDD,0x6C,0xAB,0xFA,0x6B,0x7F, +0xA0,0x02,0x30,0x39,0x66,0x5B,0xC2,0x64,0x8D,0xB8,0x9E,0x50,0xDC,0xA8,0xD5,0x49, +0xA2,0xED,0xC7,0xDC,0xD1,0x49,0x7F,0x17,0x01,0xB8,0xC8,0x86,0x8F,0x4E,0x8C,0x88, +0x2B,0xA8,0x9A,0xA9,0x8A,0xC5,0xD1,0x00,0xBD,0xF8,0x54,0xE2,0x9A,0xE5,0x5B,0x7C, +0xB3,0x27,0x17, +}; + +/* subject: Common Name: DigiCert High Assurance EV Root CA, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* issuer: Common Name: DigiCert High Assurance EV Root CA, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* link: https://crt.sh/?q=7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf */ +const unsigned char kCertificateWithFingerprint_7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf_certificate[969]={ +0x30,0x82,0x03,0xC5,0x30,0x82,0x02,0xAD,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x02, +0xAC,0x5C,0x26,0x6A,0x0B,0x40,0x9B,0x8F,0x0B,0x79,0xF2,0xAE,0x46,0x25,0x77,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x6C, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30, +0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77, +0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31, +0x2B,0x30,0x29,0x06,0x03,0x55,0x04,0x03,0x13,0x22,0x44,0x69,0x67,0x69,0x43,0x65, +0x72,0x74,0x20,0x48,0x69,0x67,0x68,0x20,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63, +0x65,0x20,0x45,0x56,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D, +0x30,0x36,0x31,0x31,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33, +0x31,0x31,0x31,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x30,0x6C,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06, +0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49, +0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,0x77,0x77, +0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x2B,0x30, +0x29,0x06,0x03,0x55,0x04,0x03,0x13,0x22,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x48,0x69,0x67,0x68,0x20,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63,0x65,0x20, +0x45,0x56,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01, +0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xC6,0xCC,0xE5,0x73,0xE6, +0xFB,0xD4,0xBB,0xE5,0x2D,0x2D,0x32,0xA6,0xDF,0xE5,0x81,0x3F,0xC9,0xCD,0x25,0x49, +0xB6,0x71,0x2A,0xC3,0xD5,0x94,0x34,0x67,0xA2,0x0A,0x1C,0xB0,0x5F,0x69,0xA6,0x40, +0xB1,0xC4,0xB7,0xB2,0x8F,0xD0,0x98,0xA4,0xA9,0x41,0x59,0x3A,0xD3,0xDC,0x94,0xD6, +0x3C,0xDB,0x74,0x38,0xA4,0x4A,0xCC,0x4D,0x25,0x82,0xF7,0x4A,0xA5,0x53,0x12,0x38, +0xEE,0xF3,0x49,0x6D,0x71,0x91,0x7E,0x63,0xB6,0xAB,0xA6,0x5F,0xC3,0xA4,0x84,0xF8, +0x4F,0x62,0x51,0xBE,0xF8,0xC5,0xEC,0xDB,0x38,0x92,0xE3,0x06,0xE5,0x08,0x91,0x0C, +0xC4,0x28,0x41,0x55,0xFB,0xCB,0x5A,0x89,0x15,0x7E,0x71,0xE8,0x35,0xBF,0x4D,0x72, +0x09,0x3D,0xBE,0x3A,0x38,0x50,0x5B,0x77,0x31,0x1B,0x8D,0xB3,0xC7,0x24,0x45,0x9A, +0xA7,0xAC,0x6D,0x00,0x14,0x5A,0x04,0xB7,0xBA,0x13,0xEB,0x51,0x0A,0x98,0x41,0x41, +0x22,0x4E,0x65,0x61,0x87,0x81,0x41,0x50,0xA6,0x79,0x5C,0x89,0xDE,0x19,0x4A,0x57, +0xD5,0x2E,0xE6,0x5D,0x1C,0x53,0x2C,0x7E,0x98,0xCD,0x1A,0x06,0x16,0xA4,0x68,0x73, +0xD0,0x34,0x04,0x13,0x5C,0xA1,0x71,0xD3,0x5A,0x7C,0x55,0xDB,0x5E,0x64,0xE1,0x37, +0x87,0x30,0x56,0x04,0xE5,0x11,0xB4,0x29,0x80,0x12,0xF1,0x79,0x39,0x88,0xA2,0x02, +0x11,0x7C,0x27,0x66,0xB7,0x88,0xB7,0x78,0xF2,0xCA,0x0A,0xA8,0x38,0xAB,0x0A,0x64, +0xC2,0xBF,0x66,0x5D,0x95,0x84,0xC1,0xA1,0x25,0x1E,0x87,0x5D,0x1A,0x50,0x0B,0x20, +0x12,0xCC,0x41,0xBB,0x6E,0x0B,0x51,0x38,0xB8,0x4B,0xCB,0x02,0x03,0x01,0x00,0x01, +0xA3,0x63,0x30,0x61,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04, +0x03,0x02,0x01,0x86,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05, +0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14, +0xB1,0x3E,0xC3,0x69,0x03,0xF8,0xBF,0x47,0x01,0xD4,0x98,0x26,0x1A,0x08,0x02,0xEF, +0x63,0x64,0x2B,0xC3,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80, +0x14,0xB1,0x3E,0xC3,0x69,0x03,0xF8,0xBF,0x47,0x01,0xD4,0x98,0x26,0x1A,0x08,0x02, +0xEF,0x63,0x64,0x2B,0xC3,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x1C,0x1A,0x06,0x97,0xDC,0xD7,0x9C, +0x9F,0x3C,0x88,0x66,0x06,0x08,0x57,0x21,0xDB,0x21,0x47,0xF8,0x2A,0x67,0xAA,0xBF, +0x18,0x32,0x76,0x40,0x10,0x57,0xC1,0x8A,0xF3,0x7A,0xD9,0x11,0x65,0x8E,0x35,0xFA, +0x9E,0xFC,0x45,0xB5,0x9E,0xD9,0x4C,0x31,0x4B,0xB8,0x91,0xE8,0x43,0x2C,0x8E,0xB3, +0x78,0xCE,0xDB,0xE3,0x53,0x79,0x71,0xD6,0xE5,0x21,0x94,0x01,0xDA,0x55,0x87,0x9A, +0x24,0x64,0xF6,0x8A,0x66,0xCC,0xDE,0x9C,0x37,0xCD,0xA8,0x34,0xB1,0x69,0x9B,0x23, +0xC8,0x9E,0x78,0x22,0x2B,0x70,0x43,0xE3,0x55,0x47,0x31,0x61,0x19,0xEF,0x58,0xC5, +0x85,0x2F,0x4E,0x30,0xF6,0xA0,0x31,0x16,0x23,0xC8,0xE7,0xE2,0x65,0x16,0x33,0xCB, +0xBF,0x1A,0x1B,0xA0,0x3D,0xF8,0xCA,0x5E,0x8B,0x31,0x8B,0x60,0x08,0x89,0x2D,0x0C, +0x06,0x5C,0x52,0xB7,0xC4,0xF9,0x0A,0x98,0xD1,0x15,0x5F,0x9F,0x12,0xBE,0x7C,0x36, +0x63,0x38,0xBD,0x44,0xA4,0x7F,0xE4,0x26,0x2B,0x0A,0xC4,0x97,0x69,0x0D,0xE9,0x8C, +0xE2,0xC0,0x10,0x57,0xB8,0xC8,0x76,0x12,0x91,0x55,0xF2,0x48,0x69,0xD8,0xBC,0x2A, +0x02,0x5B,0x0F,0x44,0xD4,0x20,0x31,0xDB,0xF4,0xBA,0x70,0x26,0x5D,0x90,0x60,0x9E, +0xBC,0x4B,0x17,0x09,0x2F,0xB4,0xCB,0x1E,0x43,0x68,0xC9,0x07,0x27,0xC1,0xD2,0x5C, +0xF7,0xEA,0x21,0xB9,0x68,0x12,0x9C,0x3C,0x9C,0xBF,0x9E,0xFC,0x80,0x5C,0x9B,0x63, +0xCD,0xEC,0x47,0xAA,0x25,0x27,0x67,0xA0,0x37,0xF3,0x00,0x82,0x7D,0x54,0xD7,0xA9, +0xF8,0xE9,0x2E,0x13,0xA3,0x77,0xE8,0x1F,0x4A, +}; + +/* subject: Common Name: DigiCert Trusted Root G4, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* issuer: Common Name: DigiCert Trusted Root G4, Organizational Unit: www.digicert.com, Organization: DigiCert Inc, Country: US */ +/* link: https://crt.sh/?q=552f7bdcf1a7af9e6ce672017f4f12abf77240c78e761ac203d1d9d20ac89988 */ +const unsigned char kCertificateWithFingerprint_552f7bdcf1a7af9e6ce672017f4f12abf77240c78e761ac203d1d9d20ac89988_certificate[1428]={ +0x30,0x82,0x05,0x90,0x30,0x82,0x03,0x78,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x05, +0x9B,0x1B,0x57,0x9E,0x8E,0x21,0x32,0xE2,0x39,0x07,0xBD,0xA7,0x77,0x75,0x5C,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C,0x05,0x00,0x30,0x62, +0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30, +0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, +0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77, +0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31, +0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x44,0x69,0x67,0x69,0x43,0x65, +0x72,0x74,0x20,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x20,0x52,0x6F,0x6F,0x74,0x20, +0x47,0x34,0x30,0x1E,0x17,0x0D,0x31,0x33,0x30,0x38,0x30,0x31,0x31,0x32,0x30,0x30, +0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31,0x31,0x35,0x31,0x32,0x30,0x30,0x30, +0x30,0x5A,0x30,0x62,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55, +0x53,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69, +0x43,0x65,0x72,0x74,0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04, +0x0B,0x13,0x10,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E, +0x63,0x6F,0x6D,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x44,0x69, +0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x20,0x52, +0x6F,0x6F,0x74,0x20,0x47,0x34,0x30,0x82,0x02,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x02,0x0F,0x00,0x30,0x82, +0x02,0x0A,0x02,0x82,0x02,0x01,0x00,0xBF,0xE6,0x90,0x73,0x68,0xDE,0xBB,0xE4,0x5D, +0x4A,0x3C,0x30,0x22,0x30,0x69,0x33,0xEC,0xC2,0xA7,0x25,0x2E,0xC9,0x21,0x3D,0xF2, +0x8A,0xD8,0x59,0xC2,0xE1,0x29,0xA7,0x3D,0x58,0xAB,0x76,0x9A,0xCD,0xAE,0x7B,0x1B, +0x84,0x0D,0xC4,0x30,0x1F,0xF3,0x1B,0xA4,0x38,0x16,0xEB,0x56,0xC6,0x97,0x6D,0x1D, +0xAB,0xB2,0x79,0xF2,0xCA,0x11,0xD2,0xE4,0x5F,0xD6,0x05,0x3C,0x52,0x0F,0x52,0x1F, +0xC6,0x9E,0x15,0xA5,0x7E,0xBE,0x9F,0xA9,0x57,0x16,0x59,0x55,0x72,0xAF,0x68,0x93, +0x70,0xC2,0xB2,0xBA,0x75,0x99,0x6A,0x73,0x32,0x94,0xD1,0x10,0x44,0x10,0x2E,0xDF, +0x82,0xF3,0x07,0x84,0xE6,0x74,0x3B,0x6D,0x71,0xE2,0x2D,0x0C,0x1B,0xEE,0x20,0xD5, +0xC9,0x20,0x1D,0x63,0x29,0x2D,0xCE,0xEC,0x5E,0x4E,0xC8,0x93,0xF8,0x21,0x61,0x9B, +0x34,0xEB,0x05,0xC6,0x5E,0xEC,0x5B,0x1A,0xBC,0xEB,0xC9,0xCF,0xCD,0xAC,0x34,0x40, +0x5F,0xB1,0x7A,0x66,0xEE,0x77,0xC8,0x48,0xA8,0x66,0x57,0x57,0x9F,0x54,0x58,0x8E, +0x0C,0x2B,0xB7,0x4F,0xA7,0x30,0xD9,0x56,0xEE,0xCA,0x7B,0x5D,0xE3,0xAD,0xC9,0x4F, +0x5E,0xE5,0x35,0xE7,0x31,0xCB,0xDA,0x93,0x5E,0xDC,0x8E,0x8F,0x80,0xDA,0xB6,0x91, +0x98,0x40,0x90,0x79,0xC3,0x78,0xC7,0xB6,0xB1,0xC4,0xB5,0x6A,0x18,0x38,0x03,0x10, +0x8D,0xD8,0xD4,0x37,0xA4,0x2E,0x05,0x7D,0x88,0xF5,0x82,0x3E,0x10,0x91,0x70,0xAB, +0x55,0x82,0x41,0x32,0xD7,0xDB,0x04,0x73,0x2A,0x6E,0x91,0x01,0x7C,0x21,0x4C,0xD4, +0xBC,0xAE,0x1B,0x03,0x75,0x5D,0x78,0x66,0xD9,0x3A,0x31,0x44,0x9A,0x33,0x40,0xBF, +0x08,0xD7,0x5A,0x49,0xA4,0xC2,0xE6,0xA9,0xA0,0x67,0xDD,0xA4,0x27,0xBC,0xA1,0x4F, +0x39,0xB5,0x11,0x58,0x17,0xF7,0x24,0x5C,0x46,0x8F,0x64,0xF7,0xC1,0x69,0x88,0x76, +0x98,0x76,0x3D,0x59,0x5D,0x42,0x76,0x87,0x89,0x97,0x69,0x7A,0x48,0xF0,0xE0,0xA2, +0x12,0x1B,0x66,0x9A,0x74,0xCA,0xDE,0x4B,0x1E,0xE7,0x0E,0x63,0xAE,0xE6,0xD4,0xEF, +0x92,0x92,0x3A,0x9E,0x3D,0xDC,0x00,0xE4,0x45,0x25,0x89,0xB6,0x9A,0x44,0x19,0x2B, +0x7E,0xC0,0x94,0xB4,0xD2,0x61,0x6D,0xEB,0x33,0xD9,0xC5,0xDF,0x4B,0x04,0x00,0xCC, +0x7D,0x1C,0x95,0xC3,0x8F,0xF7,0x21,0xB2,0xB2,0x11,0xB7,0xBB,0x7F,0xF2,0xD5,0x8C, +0x70,0x2C,0x41,0x60,0xAA,0xB1,0x63,0x18,0x44,0x95,0x1A,0x76,0x62,0x7E,0xF6,0x80, +0xB0,0xFB,0xE8,0x64,0xA6,0x33,0xD1,0x89,0x07,0xE1,0xBD,0xB7,0xE6,0x43,0xA4,0x18, +0xB8,0xA6,0x77,0x01,0xE1,0x0F,0x94,0x0C,0x21,0x1D,0xB2,0x54,0x29,0x25,0x89,0x6C, +0xE5,0x0E,0x52,0x51,0x47,0x74,0xBE,0x26,0xAC,0xB6,0x41,0x75,0xDE,0x7A,0xAC,0x5F, +0x8D,0x3F,0xC9,0xBC,0xD3,0x41,0x11,0x12,0x5B,0xE5,0x10,0x50,0xEB,0x31,0xC5,0xCA, +0x72,0x16,0x22,0x09,0xDF,0x7C,0x4C,0x75,0x3F,0x63,0xEC,0x21,0x5F,0xC4,0x20,0x51, +0x6B,0x6F,0xB1,0xAB,0x86,0x8B,0x4F,0xC2,0xD6,0x45,0x5F,0x9D,0x20,0xFC,0xA1,0x1E, +0xC5,0xC0,0x8F,0xA2,0xB1,0x7E,0x0A,0x26,0x99,0xF5,0xE4,0x69,0x2F,0x98,0x1D,0x2D, +0xF5,0xD9,0xA9,0xB2,0x1D,0xE5,0x1B,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40, +0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01, +0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01, +0x86,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xEC,0xD7,0xE3,0x82, +0xD2,0x71,0x5D,0x64,0x4C,0xDF,0x2E,0x67,0x3F,0xE7,0xBA,0x98,0xAE,0x1C,0x0F,0x4F, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C,0x05,0x00,0x03, +0x82,0x02,0x01,0x00,0xBB,0x61,0xD9,0x7D,0xA9,0x6C,0xBE,0x17,0xC4,0x91,0x1B,0xC3, +0xA1,0xA2,0x00,0x8D,0xE3,0x64,0x68,0x0F,0x56,0xCF,0x77,0xAE,0x70,0xF9,0xFD,0x9A, +0x4A,0x99,0xB9,0xC9,0x78,0x5C,0x0C,0x0C,0x5F,0xE4,0xE6,0x14,0x29,0x56,0x0B,0x36, +0x49,0x5D,0x44,0x63,0xE0,0xAD,0x9C,0x96,0x18,0x66,0x1B,0x23,0x0D,0x3D,0x79,0xE9, +0x6D,0x6B,0xD6,0x54,0xF8,0xD2,0x3C,0xC1,0x43,0x40,0xAE,0x1D,0x50,0xF5,0x52,0xFC, +0x90,0x3B,0xBB,0x98,0x99,0x69,0x6B,0xC7,0xC1,0xA7,0xA8,0x68,0xA4,0x27,0xDC,0x9D, +0xF9,0x27,0xAE,0x30,0x85,0xB9,0xF6,0x67,0x4D,0x3A,0x3E,0x8F,0x59,0x39,0x22,0x53, +0x44,0xEB,0xC8,0x5D,0x03,0xCA,0xED,0x50,0x7A,0x7D,0x62,0x21,0x0A,0x80,0xC8,0x73, +0x66,0xD1,0xA0,0x05,0x60,0x5F,0xE8,0xA5,0xB4,0xA7,0xAF,0xA8,0xF7,0x6D,0x35,0x9C, +0x7C,0x5A,0x8A,0xD6,0xA2,0x38,0x99,0xF3,0x78,0x8B,0xF4,0x4D,0xD2,0x20,0x0B,0xDE, +0x04,0xEE,0x8C,0x9B,0x47,0x81,0x72,0x0D,0xC0,0x14,0x32,0xEF,0x30,0x59,0x2E,0xAE, +0xE0,0x71,0xF2,0x56,0xE4,0x6A,0x97,0x6F,0x92,0x50,0x6D,0x96,0x8D,0x68,0x7A,0x9A, +0xB2,0x36,0x14,0x7A,0x06,0xF2,0x24,0xB9,0x09,0x11,0x50,0xD7,0x08,0xB1,0xB8,0x89, +0x7A,0x84,0x23,0x61,0x42,0x29,0xE5,0xA3,0xCD,0xA2,0x20,0x41,0xD7,0xD1,0x9C,0x64, +0xD9,0xEA,0x26,0xA1,0x8B,0x14,0xD7,0x4C,0x19,0xB2,0x50,0x41,0x71,0x3D,0x3F,0x4D, +0x70,0x23,0x86,0x0C,0x4A,0xDC,0x81,0xD2,0xCC,0x32,0x94,0x84,0x0D,0x08,0x09,0x97, +0x1C,0x4F,0xC0,0xEE,0x6B,0x20,0x74,0x30,0xD2,0xE0,0x39,0x34,0x10,0x85,0x21,0x15, +0x01,0x08,0xE8,0x55,0x32,0xDE,0x71,0x49,0xD9,0x28,0x17,0x50,0x4D,0xE6,0xBE,0x4D, +0xD1,0x75,0xAC,0xD0,0xCA,0xFB,0x41,0xB8,0x43,0xA5,0xAA,0xD3,0xC3,0x05,0x44,0x4F, +0x2C,0x36,0x9B,0xE2,0xFA,0xE2,0x45,0xB8,0x23,0x53,0x6C,0x06,0x6F,0x67,0x55,0x7F, +0x46,0xB5,0x4C,0x3F,0x6E,0x28,0x5A,0x79,0x26,0xD2,0xA4,0xA8,0x62,0x97,0xD2,0x1E, +0xE2,0xED,0x4A,0x8B,0xBC,0x1B,0xFD,0x47,0x4A,0x0D,0xDF,0x67,0x66,0x7E,0xB2,0x5B, +0x41,0xD0,0x3B,0xE4,0xF4,0x3B,0xF4,0x04,0x63,0xE9,0xEF,0xC2,0x54,0x00,0x51,0xA0, +0x8A,0x2A,0xC9,0xCE,0x78,0xCC,0xD5,0xEA,0x87,0x04,0x18,0xB3,0xCE,0xAF,0x49,0x88, +0xAF,0xF3,0x92,0x99,0xB6,0xB3,0xE6,0x61,0x0F,0xD2,0x85,0x00,0xE7,0x50,0x1A,0xE4, +0x1B,0x95,0x9D,0x19,0xA1,0xB9,0x9C,0xB1,0x9B,0xB1,0x00,0x1E,0xEF,0xD0,0x0F,0x4F, +0x42,0x6C,0xC9,0x0A,0xBC,0xEE,0x43,0xFA,0x3A,0x71,0xA5,0xC8,0x4D,0x26,0xA5,0x35, +0xFD,0x89,0x5D,0xBC,0x85,0x62,0x1D,0x32,0xD2,0xA0,0x2B,0x54,0xED,0x9A,0x57,0xC1, +0xDB,0xFA,0x10,0xCF,0x19,0xB7,0x8B,0x4A,0x1B,0x8F,0x01,0xB6,0x27,0x95,0x53,0xE8, +0xB6,0x89,0x6D,0x5B,0xBC,0x68,0xD4,0x23,0xE8,0x8B,0x51,0xA2,0x56,0xF9,0xF0,0xA6, +0x80,0xA0,0xD6,0x1E,0xB3,0xBC,0x0F,0x0F,0x53,0x75,0x29,0xAA,0xEA,0x13,0x77,0xE4, +0xDE,0x8C,0x81,0x21,0xAD,0x07,0x10,0x47,0x11,0xAD,0x87,0x3D,0x07,0xD1,0x75,0xBC, +0xCF,0xF3,0x66,0x7E, +}; + +/* subject: Common Name: Entrust Root Certification Authority; Organizational Unit: (c) 2006 Entrust, Inc., www.entrust.net/CPS is incorporated by reference; Organization: Entrust, Inc.; Country: US */ +/* issuer: Common Name: Entrust Root Certification Authority; Organizational Unit: (c) 2006 Entrust, Inc., www.entrust.net/CPS is incorporated by reference; Organization: Entrust, Inc.; Country: US */ +/* link: https://crt.sh/?q=73c176434f1bc6d5adf45b0e76e727287c8de57616c1e6e6141a2b2cbc7d8e4c */ +const unsigned char kCertificateWithFingerprint_73c176434f1bc6d5adf45b0e76e727287c8de57616c1e6e6141a2b2cbc7d8e4c_certificate[1173]={ +0x30,0x82,0x04,0x91,0x30,0x82,0x03,0x79,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x45, +0x6B,0x50,0x54,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x30,0x81,0xB0,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02, +0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x45,0x6E,0x74, +0x72,0x75,0x73,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x39,0x30,0x37,0x06,0x03, +0x55,0x04,0x0B,0x13,0x30,0x77,0x77,0x77,0x2E,0x65,0x6E,0x74,0x72,0x75,0x73,0x74, +0x2E,0x6E,0x65,0x74,0x2F,0x43,0x50,0x53,0x20,0x69,0x73,0x20,0x69,0x6E,0x63,0x6F, +0x72,0x70,0x6F,0x72,0x61,0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x72,0x65,0x66,0x65, +0x72,0x65,0x6E,0x63,0x65,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x0B,0x13,0x16, +0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x36,0x20,0x45,0x6E,0x74,0x72,0x75,0x73,0x74, +0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55,0x04,0x03,0x13, +0x24,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65, +0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68, +0x6F,0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x31,0x32,0x37,0x32, +0x30,0x32,0x33,0x34,0x32,0x5A,0x17,0x0D,0x32,0x36,0x31,0x31,0x32,0x37,0x32,0x30, +0x35,0x33,0x34,0x32,0x5A,0x30,0x81,0xB0,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D, +0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x39,0x30, +0x37,0x06,0x03,0x55,0x04,0x0B,0x13,0x30,0x77,0x77,0x77,0x2E,0x65,0x6E,0x74,0x72, +0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x2F,0x43,0x50,0x53,0x20,0x69,0x73,0x20,0x69, +0x6E,0x63,0x6F,0x72,0x70,0x6F,0x72,0x61,0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x72, +0x65,0x66,0x65,0x72,0x65,0x6E,0x63,0x65,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04, +0x0B,0x13,0x16,0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x36,0x20,0x45,0x6E,0x74,0x72, +0x75,0x73,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55, +0x04,0x03,0x13,0x24,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x20,0x52,0x6F,0x6F,0x74, +0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41, +0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00, +0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xB6,0x95,0xB6,0x43,0x42,0xFA,0xC6, +0x6D,0x2A,0x6F,0x48,0xDF,0x94,0x4C,0x39,0x57,0x05,0xEE,0xC3,0x79,0x11,0x41,0x68, +0x36,0xED,0xEC,0xFE,0x9A,0x01,0x8F,0xA1,0x38,0x28,0xFC,0xF7,0x10,0x46,0x66,0x2E, +0x4D,0x1E,0x1A,0xB1,0x1A,0x4E,0xC6,0xD1,0xC0,0x95,0x88,0xB0,0xC9,0xFF,0x31,0x8B, +0x33,0x03,0xDB,0xB7,0x83,0x7B,0x3E,0x20,0x84,0x5E,0xED,0xB2,0x56,0x28,0xA7,0xF8, +0xE0,0xB9,0x40,0x71,0x37,0xC5,0xCB,0x47,0x0E,0x97,0x2A,0x68,0xC0,0x22,0x95,0x62, +0x15,0xDB,0x47,0xD9,0xF5,0xD0,0x2B,0xFF,0x82,0x4B,0xC9,0xAD,0x3E,0xDE,0x4C,0xDB, +0x90,0x80,0x50,0x3F,0x09,0x8A,0x84,0x00,0xEC,0x30,0x0A,0x3D,0x18,0xCD,0xFB,0xFD, +0x2A,0x59,0x9A,0x23,0x95,0x17,0x2C,0x45,0x9E,0x1F,0x6E,0x43,0x79,0x6D,0x0C,0x5C, +0x98,0xFE,0x48,0xA7,0xC5,0x23,0x47,0x5C,0x5E,0xFD,0x6E,0xE7,0x1E,0xB4,0xF6,0x68, +0x45,0xD1,0x86,0x83,0x5B,0xA2,0x8A,0x8D,0xB1,0xE3,0x29,0x80,0xFE,0x25,0x71,0x88, +0xAD,0xBE,0xBC,0x8F,0xAC,0x52,0x96,0x4B,0xAA,0x51,0x8D,0xE4,0x13,0x31,0x19,0xE8, +0x4E,0x4D,0x9F,0xDB,0xAC,0xB3,0x6A,0xD5,0xBC,0x39,0x54,0x71,0xCA,0x7A,0x7A,0x7F, +0x90,0xDD,0x7D,0x1D,0x80,0xD9,0x81,0xBB,0x59,0x26,0xC2,0x11,0xFE,0xE6,0x93,0xE2, +0xF7,0x80,0xE4,0x65,0xFB,0x34,0x37,0x0E,0x29,0x80,0x70,0x4D,0xAF,0x38,0x86,0x2E, +0x9E,0x7F,0x57,0xAF,0x9E,0x17,0xAE,0xEB,0x1C,0xCB,0x28,0x21,0x5F,0xB6,0x1C,0xD8, +0xE7,0xA2,0x04,0x22,0xF9,0xD3,0xDA,0xD8,0xCB,0x02,0x03,0x01,0x00,0x01,0xA3,0x81, +0xB0,0x30,0x81,0xAD,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04, +0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05, +0x30,0x03,0x01,0x01,0xFF,0x30,0x2B,0x06,0x03,0x55,0x1D,0x10,0x04,0x24,0x30,0x22, +0x80,0x0F,0x32,0x30,0x30,0x36,0x31,0x31,0x32,0x37,0x32,0x30,0x32,0x33,0x34,0x32, +0x5A,0x81,0x0F,0x32,0x30,0x32,0x36,0x31,0x31,0x32,0x37,0x32,0x30,0x35,0x33,0x34, +0x32,0x5A,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x68, +0x90,0xE4,0x67,0xA4,0xA6,0x53,0x80,0xC7,0x86,0x66,0xA4,0xF1,0xF7,0x4B,0x43,0xFB, +0x84,0xBD,0x6D,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x68,0x90, +0xE4,0x67,0xA4,0xA6,0x53,0x80,0xC7,0x86,0x66,0xA4,0xF1,0xF7,0x4B,0x43,0xFB,0x84, +0xBD,0x6D,0x30,0x1D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x41,0x00,0x04, +0x10,0x30,0x0E,0x1B,0x08,0x56,0x37,0x2E,0x31,0x3A,0x34,0x2E,0x30,0x03,0x02,0x04, +0x90,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00, +0x03,0x82,0x01,0x01,0x00,0x93,0xD4,0x30,0xB0,0xD7,0x03,0x20,0x2A,0xD0,0xF9,0x63, +0xE8,0x91,0x0C,0x05,0x20,0xA9,0x5F,0x19,0xCA,0x7B,0x72,0x4E,0xD4,0xB1,0xDB,0xD0, +0x96,0xFB,0x54,0x5A,0x19,0x2C,0x0C,0x08,0xF7,0xB2,0xBC,0x85,0xA8,0x9D,0x7F,0x6D, +0x3B,0x52,0xB3,0x2A,0xDB,0xE7,0xD4,0x84,0x8C,0x63,0xF6,0x0F,0xCB,0x26,0x01,0x91, +0x50,0x6C,0xF4,0x5F,0x14,0xE2,0x93,0x74,0xC0,0x13,0x9E,0x30,0x3A,0x50,0xE3,0xB4, +0x60,0xC5,0x1C,0xF0,0x22,0x44,0x8D,0x71,0x47,0xAC,0xC8,0x1A,0xC9,0xE9,0x9B,0x9A, +0x00,0x60,0x13,0xFF,0x70,0x7E,0x5F,0x11,0x4D,0x49,0x1B,0xB3,0x15,0x52,0x7B,0xC9, +0x54,0xDA,0xBF,0x9D,0x95,0xAF,0x6B,0x9A,0xD8,0x9E,0xE9,0xF1,0xE4,0x43,0x8D,0xE2, +0x11,0x44,0x3A,0xBF,0xAF,0xBD,0x83,0x42,0x73,0x52,0x8B,0xAA,0xBB,0xA7,0x29,0xCF, +0xF5,0x64,0x1C,0x0A,0x4D,0xD1,0xBC,0xAA,0xAC,0x9F,0x2A,0xD0,0xFF,0x7F,0x7F,0xDA, +0x7D,0xEA,0xB1,0xED,0x30,0x25,0xC1,0x84,0xDA,0x34,0xD2,0x5B,0x78,0x83,0x56,0xEC, +0x9C,0x36,0xC3,0x26,0xE2,0x11,0xF6,0x67,0x49,0x1D,0x92,0xAB,0x8C,0xFB,0xEB,0xFF, +0x7A,0xEE,0x85,0x4A,0xA7,0x50,0x80,0xF0,0xA7,0x5C,0x4A,0x94,0x2E,0x5F,0x05,0x99, +0x3C,0x52,0x41,0xE0,0xCD,0xB4,0x63,0xCF,0x01,0x43,0xBA,0x9C,0x83,0xDC,0x8F,0x60, +0x3B,0xF3,0x5A,0xB4,0xB4,0x7B,0xAE,0xDA,0x0B,0x90,0x38,0x75,0xEF,0x81,0x1D,0x66, +0xD2,0xF7,0x57,0x70,0x36,0xB3,0xBF,0xFC,0x28,0xAF,0x71,0x25,0x85,0x5B,0x13,0xFE, +0x1E,0x7F,0x5A,0xB4,0x3C, +}; + +/* subject: Common Name: Entrust Root Certification Authority - EC1; Organizational Unit: (c) 2012 Entrust, Inc. - for authorized use only, See www.entrust.net/legal-terms; Organization: Entrust, Inc.; Country: US */ +/* issuer: Common Name: Entrust Root Certification Authority - EC1; Organizational Unit: (c) 2012 Entrust, Inc. - for authorized use only, See www.entrust.net/legal-terms; Organization: Entrust, Inc.; Country: US */ +/* link: https://crt.sh/?q=02ed0eb28c14da45165c566791700d6451d7fb56f0b2ab1d3b8eb070e56edff5 */ +const unsigned char kCertificateWithFingerprint_02ed0eb28c14da45165c566791700d6451d7fb56f0b2ab1d3b8eb070e56edff5_certificate[765]={ +0x30,0x82,0x02,0xF9,0x30,0x82,0x02,0x80,0xA0,0x03,0x02,0x01,0x02,0x02,0x0D,0x00, +0xA6,0x8B,0x79,0x29,0x00,0x00,0x00,0x00,0x50,0xD0,0x91,0xF9,0x30,0x0A,0x06,0x08, +0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x81,0xBF,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04, +0x0A,0x13,0x0D,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E, +0x31,0x28,0x30,0x26,0x06,0x03,0x55,0x04,0x0B,0x13,0x1F,0x53,0x65,0x65,0x20,0x77, +0x77,0x77,0x2E,0x65,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x2F,0x6C, +0x65,0x67,0x61,0x6C,0x2D,0x74,0x65,0x72,0x6D,0x73,0x31,0x39,0x30,0x37,0x06,0x03, +0x55,0x04,0x0B,0x13,0x30,0x28,0x63,0x29,0x20,0x32,0x30,0x31,0x32,0x20,0x45,0x6E, +0x74,0x72,0x75,0x73,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x66,0x6F, +0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65, +0x20,0x6F,0x6E,0x6C,0x79,0x31,0x33,0x30,0x31,0x06,0x03,0x55,0x04,0x03,0x13,0x2A, +0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72, +0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F, +0x72,0x69,0x74,0x79,0x20,0x2D,0x20,0x45,0x43,0x31,0x30,0x1E,0x17,0x0D,0x31,0x32, +0x31,0x32,0x31,0x38,0x31,0x35,0x32,0x35,0x33,0x36,0x5A,0x17,0x0D,0x33,0x37,0x31, +0x32,0x31,0x38,0x31,0x35,0x35,0x35,0x33,0x36,0x5A,0x30,0x81,0xBF,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03, +0x55,0x04,0x0A,0x13,0x0D,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2C,0x20,0x49,0x6E, +0x63,0x2E,0x31,0x28,0x30,0x26,0x06,0x03,0x55,0x04,0x0B,0x13,0x1F,0x53,0x65,0x65, +0x20,0x77,0x77,0x77,0x2E,0x65,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74, +0x2F,0x6C,0x65,0x67,0x61,0x6C,0x2D,0x74,0x65,0x72,0x6D,0x73,0x31,0x39,0x30,0x37, +0x06,0x03,0x55,0x04,0x0B,0x13,0x30,0x28,0x63,0x29,0x20,0x32,0x30,0x31,0x32,0x20, +0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20, +0x66,0x6F,0x72,0x20,0x61,0x75,0x74,0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75, +0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31,0x33,0x30,0x31,0x06,0x03,0x55,0x04,0x03, +0x13,0x2A,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43, +0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74, +0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x2D,0x20,0x45,0x43,0x31,0x30,0x76,0x30,0x10, +0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05,0x2B,0x81,0x04,0x00,0x22, +0x03,0x62,0x00,0x04,0x84,0x13,0xC9,0xD0,0xBA,0x6D,0x41,0x7B,0xE2,0x6C,0xD0,0xEB, +0x55,0x5F,0x66,0x02,0x1A,0x24,0xF4,0x5B,0x89,0x69,0x47,0xE3,0xB8,0xC2,0x7D,0xF1, +0xF2,0x02,0xC5,0x9F,0xA0,0xF6,0x5B,0xD5,0x8B,0x06,0x19,0x86,0x4F,0x53,0x10,0x6D, +0x07,0x24,0x27,0xA1,0xA0,0xF8,0xD5,0x47,0x19,0x61,0x4C,0x7D,0xCA,0x93,0x27,0xEA, +0x74,0x0C,0xEF,0x6F,0x96,0x09,0xFE,0x63,0xEC,0x70,0x5D,0x36,0xAD,0x67,0x77,0xAE, +0xC9,0x9D,0x7C,0x55,0x44,0x3A,0xA2,0x63,0x51,0x1F,0xF5,0xE3,0x62,0xD4,0xA9,0x47, +0x07,0x3E,0xCC,0x20,0xA3,0x42,0x30,0x40,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01, +0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01, +0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E, +0x04,0x16,0x04,0x14,0xB7,0x63,0xE7,0x1A,0xDD,0x8D,0xE9,0x08,0xA6,0x55,0x83,0xA4, +0xE0,0x6A,0x50,0x41,0x65,0x11,0x42,0x49,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE, +0x3D,0x04,0x03,0x03,0x03,0x67,0x00,0x30,0x64,0x02,0x30,0x61,0x79,0xD8,0xE5,0x42, +0x47,0xDF,0x1C,0xAE,0x53,0x99,0x17,0xB6,0x6F,0x1C,0x7D,0xE1,0xBF,0x11,0x94,0xD1, +0x03,0x88,0x75,0xE4,0x8D,0x89,0xA4,0x8A,0x77,0x46,0xDE,0x6D,0x61,0xEF,0x02,0xF5, +0xFB,0xB5,0xDF,0xCC,0xFE,0x4E,0xFF,0xFE,0xA9,0xE6,0xA7,0x02,0x30,0x5B,0x99,0xD7, +0x85,0x37,0x06,0xB5,0x7B,0x08,0xFD,0xEB,0x27,0x8B,0x4A,0x94,0xF9,0xE1,0xFA,0xA7, +0x8E,0x26,0x08,0xE8,0x7C,0x92,0x68,0x6D,0x73,0xD8,0x6F,0x26,0xAC,0x21,0x02,0xB8, +0x99,0xB7,0x26,0x41,0x5B,0x25,0x60,0xAE,0xD0,0x48,0x1A,0xEE,0x06, +}; + +/* subject: Common Name: Entrust Root Certification Authority - G2; Organizational Unit: (c) 2009 Entrust, Inc. - for authorized use only, See www.entrust.net/legal-terms; Organization: Entrust, Inc.; Country: US */ +/* issuer: Common Name: Entrust Root Certification Authority - G2; Organizational Unit: (c) 2009 Entrust, Inc. - for authorized use only, See www.entrust.net/legal-terms; Organization: Entrust, Inc.; Country: US */ +/* link: https://crt.sh/?q=43df5774b03e7fef5fe40d931a7bedf1bb2e6b42738c4e6d3841103d3aa7f339 */ +const unsigned char kCertificateWithFingerprint_43df5774b03e7fef5fe40d931a7bedf1bb2e6b42738c4e6d3841103d3aa7f339_certificate[1090]={ +0x30,0x82,0x04,0x3E,0x30,0x82,0x03,0x26,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x4A, +0x53,0x8C,0x28,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B, +0x05,0x00,0x30,0x81,0xBE,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02, +0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x45,0x6E,0x74, +0x72,0x75,0x73,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x28,0x30,0x26,0x06,0x03, +0x55,0x04,0x0B,0x13,0x1F,0x53,0x65,0x65,0x20,0x77,0x77,0x77,0x2E,0x65,0x6E,0x74, +0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x2F,0x6C,0x65,0x67,0x61,0x6C,0x2D,0x74, +0x65,0x72,0x6D,0x73,0x31,0x39,0x30,0x37,0x06,0x03,0x55,0x04,0x0B,0x13,0x30,0x28, +0x63,0x29,0x20,0x32,0x30,0x30,0x39,0x20,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2C, +0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x66,0x6F,0x72,0x20,0x61,0x75,0x74,0x68, +0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x31, +0x32,0x30,0x30,0x06,0x03,0x55,0x04,0x03,0x13,0x29,0x45,0x6E,0x74,0x72,0x75,0x73, +0x74,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61, +0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x2D, +0x20,0x47,0x32,0x30,0x1E,0x17,0x0D,0x30,0x39,0x30,0x37,0x30,0x37,0x31,0x37,0x32, +0x35,0x35,0x34,0x5A,0x17,0x0D,0x33,0x30,0x31,0x32,0x30,0x37,0x31,0x37,0x35,0x35, +0x35,0x34,0x5A,0x30,0x81,0xBE,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x55,0x53,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0A,0x13,0x0D,0x45,0x6E, +0x74,0x72,0x75,0x73,0x74,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x28,0x30,0x26,0x06, +0x03,0x55,0x04,0x0B,0x13,0x1F,0x53,0x65,0x65,0x20,0x77,0x77,0x77,0x2E,0x65,0x6E, +0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x2F,0x6C,0x65,0x67,0x61,0x6C,0x2D, +0x74,0x65,0x72,0x6D,0x73,0x31,0x39,0x30,0x37,0x06,0x03,0x55,0x04,0x0B,0x13,0x30, +0x28,0x63,0x29,0x20,0x32,0x30,0x30,0x39,0x20,0x45,0x6E,0x74,0x72,0x75,0x73,0x74, +0x2C,0x20,0x49,0x6E,0x63,0x2E,0x20,0x2D,0x20,0x66,0x6F,0x72,0x20,0x61,0x75,0x74, +0x68,0x6F,0x72,0x69,0x7A,0x65,0x64,0x20,0x75,0x73,0x65,0x20,0x6F,0x6E,0x6C,0x79, +0x31,0x32,0x30,0x30,0x06,0x03,0x55,0x04,0x03,0x13,0x29,0x45,0x6E,0x74,0x72,0x75, +0x73,0x74,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63, +0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20, +0x2D,0x20,0x47,0x32,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A, +0x02,0x82,0x01,0x01,0x00,0xBA,0x84,0xB6,0x72,0xDB,0x9E,0x0C,0x6B,0xE2,0x99,0xE9, +0x30,0x01,0xA7,0x76,0xEA,0x32,0xB8,0x95,0x41,0x1A,0xC9,0xDA,0x61,0x4E,0x58,0x72, +0xCF,0xFE,0xF6,0x82,0x79,0xBF,0x73,0x61,0x06,0x0A,0xA5,0x27,0xD8,0xB3,0x5F,0xD3, +0x45,0x4E,0x1C,0x72,0xD6,0x4E,0x32,0xF2,0x72,0x8A,0x0F,0xF7,0x83,0x19,0xD0,0x6A, +0x80,0x80,0x00,0x45,0x1E,0xB0,0xC7,0xE7,0x9A,0xBF,0x12,0x57,0x27,0x1C,0xA3,0x68, +0x2F,0x0A,0x87,0xBD,0x6A,0x6B,0x0E,0x5E,0x65,0xF3,0x1C,0x77,0xD5,0xD4,0x85,0x8D, +0x70,0x21,0xB4,0xB3,0x32,0xE7,0x8B,0xA2,0xD5,0x86,0x39,0x02,0xB1,0xB8,0xD2,0x47, +0xCE,0xE4,0xC9,0x49,0xC4,0x3B,0xA7,0xDE,0xFB,0x54,0x7D,0x57,0xBE,0xF0,0xE8,0x6E, +0xC2,0x79,0xB2,0x3A,0x0B,0x55,0xE2,0x50,0x98,0x16,0x32,0x13,0x5C,0x2F,0x78,0x56, +0xC1,0xC2,0x94,0xB3,0xF2,0x5A,0xE4,0x27,0x9A,0x9F,0x24,0xD7,0xC6,0xEC,0xD0,0x9B, +0x25,0x82,0xE3,0xCC,0xC2,0xC4,0x45,0xC5,0x8C,0x97,0x7A,0x06,0x6B,0x2A,0x11,0x9F, +0xA9,0x0A,0x6E,0x48,0x3B,0x6F,0xDB,0xD4,0x11,0x19,0x42,0xF7,0x8F,0x07,0xBF,0xF5, +0x53,0x5F,0x9C,0x3E,0xF4,0x17,0x2C,0xE6,0x69,0xAC,0x4E,0x32,0x4C,0x62,0x77,0xEA, +0xB7,0xE8,0xE5,0xBB,0x34,0xBC,0x19,0x8B,0xAE,0x9C,0x51,0xE7,0xB7,0x7E,0xB5,0x53, +0xB1,0x33,0x22,0xE5,0x6D,0xCF,0x70,0x3C,0x1A,0xFA,0xE2,0x9B,0x67,0xB6,0x83,0xF4, +0x8D,0xA5,0xAF,0x62,0x4C,0x4D,0xE0,0x58,0xAC,0x64,0x34,0x12,0x03,0xF8,0xB6,0x8D, +0x94,0x63,0x24,0xA4,0x71,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x0E, +0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F, +0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30, +0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x6A,0x72,0x26,0x7A,0xD0,0x1E, +0xEF,0x7D,0xE7,0x3B,0x69,0x51,0xD4,0x6C,0x8D,0x9F,0x90,0x12,0x66,0xAB,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01, +0x01,0x00,0x79,0x9F,0x1D,0x96,0xC6,0xB6,0x79,0x3F,0x22,0x8D,0x87,0xD3,0x87,0x03, +0x04,0x60,0x6A,0x6B,0x9A,0x2E,0x59,0x89,0x73,0x11,0xAC,0x43,0xD1,0xF5,0x13,0xFF, +0x8D,0x39,0x2B,0xC0,0xF2,0xBD,0x4F,0x70,0x8C,0xA9,0x2F,0xEA,0x17,0xC4,0x0B,0x54, +0x9E,0xD4,0x1B,0x96,0x98,0x33,0x3C,0xA8,0xAD,0x62,0xA2,0x00,0x76,0xAB,0x59,0x69, +0x6E,0x06,0x1D,0x7E,0xC4,0xB9,0x44,0x8D,0x98,0xAF,0x12,0xD4,0x61,0xDB,0x0A,0x19, +0x46,0x47,0xF3,0xEB,0xF7,0x63,0xC1,0x40,0x05,0x40,0xA5,0xD2,0xB7,0xF4,0xB5,0x9A, +0x36,0xBF,0xA9,0x88,0x76,0x88,0x04,0x55,0x04,0x2B,0x9C,0x87,0x7F,0x1A,0x37,0x3C, +0x7E,0x2D,0xA5,0x1A,0xD8,0xD4,0x89,0x5E,0xCA,0xBD,0xAC,0x3D,0x6C,0xD8,0x6D,0xAF, +0xD5,0xF3,0x76,0x0F,0xCD,0x3B,0x88,0x38,0x22,0x9D,0x6C,0x93,0x9A,0xC4,0x3D,0xBF, +0x82,0x1B,0x65,0x3F,0xA6,0x0F,0x5D,0xAA,0xFC,0xE5,0xB2,0x15,0xCA,0xB5,0xAD,0xC6, +0xBC,0x3D,0xD0,0x84,0xE8,0xEA,0x06,0x72,0xB0,0x4D,0x39,0x32,0x78,0xBF,0x3E,0x11, +0x9C,0x0B,0xA4,0x9D,0x9A,0x21,0xF3,0xF0,0x9B,0x0B,0x30,0x78,0xDB,0xC1,0xDC,0x87, +0x43,0xFE,0xBC,0x63,0x9A,0xCA,0xC5,0xC2,0x1C,0xC9,0xC7,0x8D,0xFF,0x3B,0x12,0x58, +0x08,0xE6,0xB6,0x3D,0xEC,0x7A,0x2C,0x4E,0xFB,0x83,0x96,0xCE,0x0C,0x3C,0x69,0x87, +0x54,0x73,0xA4,0x73,0xC2,0x93,0xFF,0x51,0x10,0xAC,0x15,0x54,0x01,0xD8,0xFC,0x05, +0xB1,0x89,0xA1,0x7F,0x74,0x83,0x9A,0x49,0xD7,0xDC,0x4E,0x7B,0x8A,0x48,0x6F,0x8B, +0x45,0xF6, +}; + +/* subject: Common Name: Entrust.net Certification Authority (2048); Organizational Unit: (c) 1999 Entrust.net Limited, www.entrust.net/CPS_2048 incorp. by ref. (limits liab.); Organization: Entrust.net */ +/* issuer: Common Name: Entrust.net Certification Authority (2048); Organizational Unit: (c) 1999 Entrust.net Limited, www.entrust.net/CPS_2048 incorp. by ref. (limits liab.); Organization: Entrust.net */ +/* link: https://crt.sh/?q=6dc47172e01cbcb0bf62580d895fe2b8ac9ad4f873801e0c10b9c837d21eb177 */ +const unsigned char kCertificateWithFingerprint_6dc47172e01cbcb0bf62580d895fe2b8ac9ad4f873801e0c10b9c837d21eb177_certificate[1070]={ +0x30,0x82,0x04,0x2A,0x30,0x82,0x03,0x12,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x38, +0x63,0xDE,0xF8,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05, +0x05,0x00,0x30,0x81,0xB4,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x13,0x0B, +0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x31,0x40,0x30,0x3E,0x06, +0x03,0x55,0x04,0x0B,0x14,0x37,0x77,0x77,0x77,0x2E,0x65,0x6E,0x74,0x72,0x75,0x73, +0x74,0x2E,0x6E,0x65,0x74,0x2F,0x43,0x50,0x53,0x5F,0x32,0x30,0x34,0x38,0x20,0x69, +0x6E,0x63,0x6F,0x72,0x70,0x2E,0x20,0x62,0x79,0x20,0x72,0x65,0x66,0x2E,0x20,0x28, +0x6C,0x69,0x6D,0x69,0x74,0x73,0x20,0x6C,0x69,0x61,0x62,0x2E,0x29,0x31,0x25,0x30, +0x23,0x06,0x03,0x55,0x04,0x0B,0x13,0x1C,0x28,0x63,0x29,0x20,0x31,0x39,0x39,0x39, +0x20,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x20,0x4C,0x69,0x6D, +0x69,0x74,0x65,0x64,0x31,0x33,0x30,0x31,0x06,0x03,0x55,0x04,0x03,0x13,0x2A,0x45, +0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x20,0x28,0x32,0x30,0x34,0x38,0x29,0x30,0x1E,0x17,0x0D,0x39,0x39,0x31, +0x32,0x32,0x34,0x31,0x37,0x35,0x30,0x35,0x31,0x5A,0x17,0x0D,0x32,0x39,0x30,0x37, +0x32,0x34,0x31,0x34,0x31,0x35,0x31,0x32,0x5A,0x30,0x81,0xB4,0x31,0x14,0x30,0x12, +0x06,0x03,0x55,0x04,0x0A,0x13,0x0B,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E, +0x65,0x74,0x31,0x40,0x30,0x3E,0x06,0x03,0x55,0x04,0x0B,0x14,0x37,0x77,0x77,0x77, +0x2E,0x65,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65,0x74,0x2F,0x43,0x50,0x53, +0x5F,0x32,0x30,0x34,0x38,0x20,0x69,0x6E,0x63,0x6F,0x72,0x70,0x2E,0x20,0x62,0x79, +0x20,0x72,0x65,0x66,0x2E,0x20,0x28,0x6C,0x69,0x6D,0x69,0x74,0x73,0x20,0x6C,0x69, +0x61,0x62,0x2E,0x29,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0B,0x13,0x1C,0x28, +0x63,0x29,0x20,0x31,0x39,0x39,0x39,0x20,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E, +0x6E,0x65,0x74,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x33,0x30,0x31,0x06, +0x03,0x55,0x04,0x03,0x13,0x2A,0x45,0x6E,0x74,0x72,0x75,0x73,0x74,0x2E,0x6E,0x65, +0x74,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20, +0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x28,0x32,0x30,0x34,0x38,0x29, +0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01, +0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01, +0x00,0xAD,0x4D,0x4B,0xA9,0x12,0x86,0xB2,0xEA,0xA3,0x20,0x07,0x15,0x16,0x64,0x2A, +0x2B,0x4B,0xD1,0xBF,0x0B,0x4A,0x4D,0x8E,0xED,0x80,0x76,0xA5,0x67,0xB7,0x78,0x40, +0xC0,0x73,0x42,0xC8,0x68,0xC0,0xDB,0x53,0x2B,0xDD,0x5E,0xB8,0x76,0x98,0x35,0x93, +0x8B,0x1A,0x9D,0x7C,0x13,0x3A,0x0E,0x1F,0x5B,0xB7,0x1E,0xCF,0xE5,0x24,0x14,0x1E, +0xB1,0x81,0xA9,0x8D,0x7D,0xB8,0xCC,0x6B,0x4B,0x03,0xF1,0x02,0x0C,0xDC,0xAB,0xA5, +0x40,0x24,0x00,0x7F,0x74,0x94,0xA1,0x9D,0x08,0x29,0xB3,0x88,0x0B,0xF5,0x87,0x77, +0x9D,0x55,0xCD,0xE4,0xC3,0x7E,0xD7,0x6A,0x64,0xAB,0x85,0x14,0x86,0x95,0x5B,0x97, +0x32,0x50,0x6F,0x3D,0xC8,0xBA,0x66,0x0C,0xE3,0xFC,0xBD,0xB8,0x49,0xC1,0x76,0x89, +0x49,0x19,0xFD,0xC0,0xA8,0xBD,0x89,0xA3,0x67,0x2F,0xC6,0x9F,0xBC,0x71,0x19,0x60, +0xB8,0x2D,0xE9,0x2C,0xC9,0x90,0x76,0x66,0x7B,0x94,0xE2,0xAF,0x78,0xD6,0x65,0x53, +0x5D,0x3C,0xD6,0x9C,0xB2,0xCF,0x29,0x03,0xF9,0x2F,0xA4,0x50,0xB2,0xD4,0x48,0xCE, +0x05,0x32,0x55,0x8A,0xFD,0xB2,0x64,0x4C,0x0E,0xE4,0x98,0x07,0x75,0xDB,0x7F,0xDF, +0xB9,0x08,0x55,0x60,0x85,0x30,0x29,0xF9,0x7B,0x48,0xA4,0x69,0x86,0xE3,0x35,0x3F, +0x1E,0x86,0x5D,0x7A,0x7A,0x15,0xBD,0xEF,0x00,0x8E,0x15,0x22,0x54,0x17,0x00,0x90, +0x26,0x93,0xBC,0x0E,0x49,0x68,0x91,0xBF,0xF8,0x47,0xD3,0x9D,0x95,0x42,0xC1,0x0E, +0x4D,0xDF,0x6F,0x26,0xCF,0xC3,0x18,0x21,0x62,0x66,0x43,0x70,0xD6,0xD5,0xC0,0x07, +0xE1,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x0E,0x06,0x03,0x55,0x1D, +0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D, +0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55, +0x1D,0x0E,0x04,0x16,0x04,0x14,0x55,0xE4,0x81,0xD1,0x11,0x80,0xBE,0xD8,0x89,0xB9, +0x08,0xA3,0x31,0xF9,0xA1,0x24,0x09,0x16,0xB9,0x70,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x3B,0x9B, +0x8F,0x56,0x9B,0x30,0xE7,0x53,0x99,0x7C,0x7A,0x79,0xA7,0x4D,0x97,0xD7,0x19,0x95, +0x90,0xFB,0x06,0x1F,0xCA,0x33,0x7C,0x46,0x63,0x8F,0x96,0x66,0x24,0xFA,0x40,0x1B, +0x21,0x27,0xCA,0xE6,0x72,0x73,0xF2,0x4F,0xFE,0x31,0x99,0xFD,0xC8,0x0C,0x4C,0x68, +0x53,0xC6,0x80,0x82,0x13,0x98,0xFA,0xB6,0xAD,0xDA,0x5D,0x3D,0xF1,0xCE,0x6E,0xF6, +0x15,0x11,0x94,0x82,0x0C,0xEE,0x3F,0x95,0xAF,0x11,0xAB,0x0F,0xD7,0x2F,0xDE,0x1F, +0x03,0x8F,0x57,0x2C,0x1E,0xC9,0xBB,0x9A,0x1A,0x44,0x95,0xEB,0x18,0x4F,0xA6,0x1F, +0xCD,0x7D,0x57,0x10,0x2F,0x9B,0x04,0x09,0x5A,0x84,0xB5,0x6E,0xD8,0x1D,0x3A,0xE1, +0xD6,0x9E,0xD1,0x6C,0x79,0x5E,0x79,0x1C,0x14,0xC5,0xE3,0xD0,0x4C,0x93,0x3B,0x65, +0x3C,0xED,0xDF,0x3D,0xBE,0xA6,0xE5,0x95,0x1A,0xC3,0xB5,0x19,0xC3,0xBD,0x5E,0x5B, +0xBB,0xFF,0x23,0xEF,0x68,0x19,0xCB,0x12,0x93,0x27,0x5C,0x03,0x2D,0x6F,0x30,0xD0, +0x1E,0xB6,0x1A,0xAC,0xDE,0x5A,0xF7,0xD1,0xAA,0xA8,0x27,0xA6,0xFE,0x79,0x81,0xC4, +0x79,0x99,0x33,0x57,0xBA,0x12,0xB0,0xA9,0xE0,0x42,0x6C,0x93,0xCA,0x56,0xDE,0xFE, +0x6D,0x84,0x0B,0x08,0x8B,0x7E,0x8D,0xEA,0xD7,0x98,0x21,0xC6,0xF3,0xE7,0x3C,0x79, +0x2F,0x5E,0x9C,0xD1,0x4C,0x15,0x8D,0xE1,0xEC,0x22,0x37,0xCC,0x9A,0x43,0x0B,0x97, +0xDC,0x80,0x90,0x8D,0xB3,0x67,0x9B,0x6F,0x48,0x08,0x15,0x56,0xCF,0xBF,0xF1,0x2B, +0x7C,0x5E,0x9A,0x76,0xE9,0x59,0x90,0xC5,0x7C,0x83,0x35,0x11,0x65,0x51, +}; + +/* subject: Common Name: AffirmTrust Commercial, Organization: AffirmTrust, Country: US */ +/* issuer: Common Name: AffirmTrust Commercial, Organization: AffirmTrust, Country: US */ +/* link: https://crt.sh/?q=0376ab1d54c5f9803ce4b2e201a0ee7eef7b57b636e8a93c9b8d4860c96f5fa7 */ +const unsigned char kCertificateWithFingerprint_0376ab1d54c5f9803ce4b2e201a0ee7eef7b57b636e8a93c9b8d4860c96f5fa7_certificate[848]={ +0x30,0x82,0x03,0x4C,0x30,0x82,0x02,0x34,0xA0,0x03,0x02,0x01,0x02,0x02,0x08,0x77, +0x77,0x06,0x27,0x26,0xA9,0xB1,0x7C,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x44,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0B, +0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31,0x1F,0x30,0x1D,0x06, +0x03,0x55,0x04,0x03,0x0C,0x16,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73, +0x74,0x20,0x43,0x6F,0x6D,0x6D,0x65,0x72,0x63,0x69,0x61,0x6C,0x30,0x1E,0x17,0x0D, +0x31,0x30,0x30,0x31,0x32,0x39,0x31,0x34,0x30,0x36,0x30,0x36,0x5A,0x17,0x0D,0x33, +0x30,0x31,0x32,0x33,0x31,0x31,0x34,0x30,0x36,0x30,0x36,0x5A,0x30,0x44,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06, +0x03,0x55,0x04,0x0A,0x0C,0x0B,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73, +0x74,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x03,0x0C,0x16,0x41,0x66,0x66,0x69, +0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x20,0x43,0x6F,0x6D,0x6D,0x65,0x72,0x63,0x69, +0x61,0x6C,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82, +0x01,0x01,0x00,0xF6,0x1B,0x4F,0x67,0x07,0x2B,0xA1,0x15,0xF5,0x06,0x22,0xCB,0x1F, +0x01,0xB2,0xE3,0x73,0x45,0x06,0x44,0x49,0x2C,0xBB,0x49,0x25,0x14,0xD6,0xCE,0xC3, +0xB7,0xAB,0x2C,0x4F,0xC6,0x41,0x32,0x94,0x57,0xFA,0x12,0xA7,0x5B,0x0E,0xE2,0x8F, +0x1F,0x1E,0x86,0x19,0xA7,0xAA,0xB5,0x2D,0xB9,0x5F,0x0D,0x8A,0xC2,0xAF,0x85,0x35, +0x79,0x32,0x2D,0xBB,0x1C,0x62,0x37,0xF2,0xB1,0x5B,0x4A,0x3D,0xCA,0xCD,0x71,0x5F, +0xE9,0x42,0xBE,0x94,0xE8,0xC8,0xDE,0xF9,0x22,0x48,0x64,0xC6,0xE5,0xAB,0xC6,0x2B, +0x6D,0xAD,0x05,0xF0,0xFA,0xD5,0x0B,0xCF,0x9A,0xE5,0xF0,0x50,0xA4,0x8B,0x3B,0x47, +0xA5,0x23,0x5B,0x7A,0x7A,0xF8,0x33,0x3F,0xB8,0xEF,0x99,0x97,0xE3,0x20,0xC1,0xD6, +0x28,0x89,0xCF,0x94,0xFB,0xB9,0x45,0xED,0xE3,0x40,0x17,0x11,0xD4,0x74,0xF0,0x0B, +0x31,0xE2,0x2B,0x26,0x6A,0x9B,0x4C,0x57,0xAE,0xAC,0x20,0x3E,0xBA,0x45,0x7A,0x05, +0xF3,0xBD,0x9B,0x69,0x15,0xAE,0x7D,0x4E,0x20,0x63,0xC4,0x35,0x76,0x3A,0x07,0x02, +0xC9,0x37,0xFD,0xC7,0x47,0xEE,0xE8,0xF1,0x76,0x1D,0x73,0x15,0xF2,0x97,0xA4,0xB5, +0xC8,0x7A,0x79,0xD9,0x42,0xAA,0x2B,0x7F,0x5C,0xFE,0xCE,0x26,0x4F,0xA3,0x66,0x81, +0x35,0xAF,0x44,0xBA,0x54,0x1E,0x1C,0x30,0x32,0x65,0x9D,0xE6,0x3C,0x93,0x5E,0x50, +0x4E,0x7A,0xE3,0x3A,0xD4,0x6E,0xCC,0x1A,0xFB,0xF9,0xD2,0x37,0xAE,0x24,0x2A,0xAB, +0x57,0x03,0x22,0x28,0x0D,0x49,0x75,0x7F,0xB7,0x28,0xDA,0x75,0xBF,0x8E,0xE3,0xDC, +0x0E,0x79,0x31,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06,0x03, +0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x9D,0x93,0xC6,0x53,0x8B,0x5E,0xCA,0xAF,0x3F, +0x9F,0x1E,0x0F,0xE5,0x99,0x95,0xBC,0x24,0xF6,0x94,0x8F,0x30,0x0F,0x06,0x03,0x55, +0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03, +0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00, +0x58,0xAC,0xF4,0x04,0x0E,0xCD,0xC0,0x0D,0xFF,0x0A,0xFD,0xD4,0xBA,0x16,0x5F,0x29, +0xBD,0x7B,0x68,0x99,0x58,0x49,0xD2,0xB4,0x1D,0x37,0x4D,0x7F,0x27,0x7D,0x46,0x06, +0x5D,0x43,0xC6,0x86,0x2E,0x3E,0x73,0xB2,0x26,0x7D,0x4F,0x93,0xA9,0xB6,0xC4,0x2A, +0x9A,0xAB,0x21,0x97,0x14,0xB1,0xDE,0x8C,0xD3,0xAB,0x89,0x15,0xD8,0x6B,0x24,0xD4, +0xF1,0x16,0xAE,0xD8,0xA4,0x5C,0xD4,0x7F,0x51,0x8E,0xED,0x18,0x01,0xB1,0x93,0x63, +0xBD,0xBC,0xF8,0x61,0x80,0x9A,0x9E,0xB1,0xCE,0x42,0x70,0xE2,0xA9,0x7D,0x06,0x25, +0x7D,0x27,0xA1,0xFE,0x6F,0xEC,0xB3,0x1E,0x24,0xDA,0xE3,0x4B,0x55,0x1A,0x00,0x3B, +0x35,0xB4,0x3B,0xD9,0xD7,0x5D,0x30,0xFD,0x81,0x13,0x89,0xF2,0xC2,0x06,0x2B,0xED, +0x67,0xC4,0x8E,0xC9,0x43,0xB2,0x5C,0x6B,0x15,0x89,0x02,0xBC,0x62,0xFC,0x4E,0xF2, +0xB5,0x33,0xAA,0xB2,0x6F,0xD3,0x0A,0xA2,0x50,0xE3,0xF6,0x3B,0xE8,0x2E,0x44,0xC2, +0xDB,0x66,0x38,0xA9,0x33,0x56,0x48,0xF1,0x6D,0x1B,0x33,0x8D,0x0D,0x8C,0x3F,0x60, +0x37,0x9D,0xD3,0xCA,0x6D,0x7E,0x34,0x7E,0x0D,0x9F,0x72,0x76,0x8B,0x1B,0x9F,0x72, +0xFD,0x52,0x35,0x41,0x45,0x02,0x96,0x2F,0x1C,0xB2,0x9A,0x73,0x49,0x21,0xB1,0x49, +0x47,0x45,0x47,0xB4,0xEF,0x6A,0x34,0x11,0xC9,0x4D,0x9A,0xCC,0x59,0xB7,0xD6,0x02, +0x9E,0x5A,0x4E,0x65,0xB5,0x94,0xAE,0x1B,0xDF,0x29,0xB0,0x16,0xF1,0xBF,0x00,0x9E, +0x07,0x3A,0x17,0x64,0xB5,0x04,0xB5,0x23,0x21,0x99,0x0A,0x95,0x3B,0x97,0x7C,0xEF, +}; + +/* subject: Common Name: AffirmTrust Networking, Organization: AffirmTrust, Country: US */ +/* issuer: Common Name: AffirmTrust Networking, Organization: AffirmTrust, Country: US */ +/* link: https://crt.sh/?q=0a81ec5a929777f145904af38d5d509f66b5e2c58fcdb531058b0e17f3f0b41b */ +const unsigned char kCertificateWithFingerprint_0a81ec5a929777f145904af38d5d509f66b5e2c58fcdb531058b0e17f3f0b41b_certificate[848]={ +0x30,0x82,0x03,0x4C,0x30,0x82,0x02,0x34,0xA0,0x03,0x02,0x01,0x02,0x02,0x08,0x7C, +0x4F,0x04,0x39,0x1C,0xD4,0x99,0x2D,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x44,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0B, +0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31,0x1F,0x30,0x1D,0x06, +0x03,0x55,0x04,0x03,0x0C,0x16,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73, +0x74,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x69,0x6E,0x67,0x30,0x1E,0x17,0x0D, +0x31,0x30,0x30,0x31,0x32,0x39,0x31,0x34,0x30,0x38,0x32,0x34,0x5A,0x17,0x0D,0x33, +0x30,0x31,0x32,0x33,0x31,0x31,0x34,0x30,0x38,0x32,0x34,0x5A,0x30,0x44,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06, +0x03,0x55,0x04,0x0A,0x0C,0x0B,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73, +0x74,0x31,0x1F,0x30,0x1D,0x06,0x03,0x55,0x04,0x03,0x0C,0x16,0x41,0x66,0x66,0x69, +0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x69, +0x6E,0x67,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82, +0x01,0x01,0x00,0xB4,0x84,0xCC,0x33,0x17,0x2E,0x6B,0x94,0x6C,0x6B,0x61,0x52,0xA0, +0xEB,0xA3,0xCF,0x79,0x94,0x4C,0xE5,0x94,0x80,0x99,0xCB,0x55,0x64,0x44,0x65,0x8F, +0x67,0x64,0xE2,0x06,0xE3,0x5C,0x37,0x49,0xF6,0x2F,0x9B,0x84,0x84,0x1E,0x2D,0xF2, +0x60,0x9D,0x30,0x4E,0xCC,0x84,0x85,0xE2,0x2C,0xCF,0x1E,0x9E,0xFE,0x36,0xAB,0x33, +0x77,0x35,0x44,0xD8,0x35,0x96,0x1A,0x3D,0x36,0xE8,0x7A,0x0E,0xD8,0xD5,0x47,0xA1, +0x6A,0x69,0x8B,0xD9,0xFC,0xBB,0x3A,0xAE,0x79,0x5A,0xD5,0xF4,0xD6,0x71,0xBB,0x9A, +0x90,0x23,0x6B,0x9A,0xB7,0x88,0x74,0x87,0x0C,0x1E,0x5F,0xB9,0x9E,0x2D,0xFA,0xAB, +0x53,0x2B,0xDC,0xBB,0x76,0x3E,0x93,0x4C,0x08,0x08,0x8C,0x1E,0xA2,0x23,0x1C,0xD4, +0x6A,0xAD,0x22,0xBA,0x99,0x01,0x2E,0x6D,0x65,0xCB,0xBE,0x24,0x66,0x55,0x24,0x4B, +0x40,0x44,0xB1,0x1B,0xD7,0xE1,0xC2,0x85,0xC0,0xDE,0x10,0x3F,0x3D,0xED,0xB8,0xFC, +0xF1,0xF1,0x23,0x53,0xDC,0xBF,0x65,0x97,0x6F,0xD9,0xF9,0x40,0x71,0x8D,0x7D,0xBD, +0x95,0xD4,0xCE,0xBE,0xA0,0x5E,0x27,0x23,0xDE,0xFD,0xA6,0xD0,0x26,0x0E,0x00,0x29, +0xEB,0x3C,0x46,0xF0,0x3D,0x60,0xBF,0x3F,0x50,0xD2,0xDC,0x26,0x41,0x51,0x9E,0x14, +0x37,0x42,0x04,0xA3,0x70,0x57,0xA8,0x1B,0x87,0xED,0x2D,0xFA,0x7B,0xEE,0x8C,0x0A, +0xE3,0xA9,0x66,0x89,0x19,0xCB,0x41,0xF9,0xDD,0x44,0x36,0x61,0xCF,0xE2,0x77,0x46, +0xC8,0x7D,0xF6,0xF4,0x92,0x81,0x36,0xFD,0xDB,0x34,0xF1,0x72,0x7E,0xF3,0x0C,0x16, +0xBD,0xB4,0x15,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06,0x03, +0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x07,0x1F,0xD2,0xE7,0x9C,0xDA,0xC2,0x6E,0xA2, +0x40,0xB4,0xB0,0x7A,0x50,0x10,0x50,0x74,0xC4,0xC8,0xBD,0x30,0x0F,0x06,0x03,0x55, +0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03, +0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00, +0x89,0x57,0xB2,0x16,0x7A,0xA8,0xC2,0xFD,0xD6,0xD9,0x9B,0x9B,0x34,0xC2,0x9C,0xB4, +0x32,0x14,0x4D,0xA7,0xA4,0xDF,0xEC,0xBE,0xA7,0xBE,0xF8,0x43,0xDB,0x91,0x37,0xCE, +0xB4,0x32,0x2E,0x50,0x55,0x1A,0x35,0x4E,0x76,0x43,0x71,0x20,0xEF,0x93,0x77,0x4E, +0x15,0x70,0x2E,0x87,0xC3,0xC1,0x1D,0x6D,0xDC,0xCB,0xB5,0x27,0xD4,0x2C,0x56,0xD1, +0x52,0x53,0x3A,0x44,0xD2,0x73,0xC8,0xC4,0x1B,0x05,0x65,0x5A,0x62,0x92,0x9C,0xEE, +0x41,0x8D,0x31,0xDB,0xE7,0x34,0xEA,0x59,0x21,0xD5,0x01,0x7A,0xD7,0x64,0xB8,0x64, +0x39,0xCD,0xC9,0xED,0xAF,0xED,0x4B,0x03,0x48,0xA7,0xA0,0x99,0x01,0x80,0xDC,0x65, +0xA3,0x36,0xAE,0x65,0x59,0x48,0x4F,0x82,0x4B,0xC8,0x65,0xF1,0x57,0x1D,0xE5,0x59, +0x2E,0x0A,0x3F,0x6C,0xD8,0xD1,0xF5,0xE5,0x09,0xB4,0x6C,0x54,0x00,0x0A,0xE0,0x15, +0x4D,0x87,0x75,0x6D,0xB7,0x58,0x96,0x5A,0xDD,0x6D,0xD2,0x00,0xA0,0xF4,0x9B,0x48, +0xBE,0xC3,0x37,0xA4,0xBA,0x36,0xE0,0x7C,0x87,0x85,0x97,0x1A,0x15,0xA2,0xDE,0x2E, +0xA2,0x5B,0xBD,0xAF,0x18,0xF9,0x90,0x50,0xCD,0x70,0x59,0xF8,0x27,0x67,0x47,0xCB, +0xC7,0xA0,0x07,0x3A,0x7D,0xD1,0x2C,0x5D,0x6C,0x19,0x3A,0x66,0xB5,0x7D,0xFD,0x91, +0x6F,0x82,0xB1,0xBE,0x08,0x93,0xDB,0x14,0x47,0xF1,0xA2,0x37,0xC7,0x45,0x9E,0x3C, +0xC7,0x77,0xAF,0x64,0xA8,0x93,0xDF,0xF6,0x69,0x83,0x82,0x60,0xF2,0x49,0x42,0x34, +0xED,0x5A,0x00,0x54,0x85,0x1C,0x16,0x36,0x92,0x0C,0x5C,0xFA,0xA6,0xAD,0xBF,0xDB, +}; + +/* subject: Common Name: AffirmTrust Premium, Organization: AffirmTrust, Country: US */ +/* issuer: Common Name: AffirmTrust Premium, Organization: AffirmTrust, Country: US */ +/* link: https://crt.sh/?q=70a73f7f376b60074248904534b11482d5bf0e698ecc498df52577ebf2e93b9a */ +const unsigned char kCertificateWithFingerprint_70a73f7f376b60074248904534b11482d5bf0e698ecc498df52577ebf2e93b9a_certificate[1354]={ +0x30,0x82,0x05,0x46,0x30,0x82,0x03,0x2E,0xA0,0x03,0x02,0x01,0x02,0x02,0x08,0x6D, +0x8C,0x14,0x46,0xB1,0xA6,0x0A,0xEE,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x0C,0x05,0x00,0x30,0x41,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0B, +0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31,0x1C,0x30,0x1A,0x06, +0x03,0x55,0x04,0x03,0x0C,0x13,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73, +0x74,0x20,0x50,0x72,0x65,0x6D,0x69,0x75,0x6D,0x30,0x1E,0x17,0x0D,0x31,0x30,0x30, +0x31,0x32,0x39,0x31,0x34,0x31,0x30,0x33,0x36,0x5A,0x17,0x0D,0x34,0x30,0x31,0x32, +0x33,0x31,0x31,0x34,0x31,0x30,0x33,0x36,0x5A,0x30,0x41,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04, +0x0A,0x0C,0x0B,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31,0x1C, +0x30,0x1A,0x06,0x03,0x55,0x04,0x03,0x0C,0x13,0x41,0x66,0x66,0x69,0x72,0x6D,0x54, +0x72,0x75,0x73,0x74,0x20,0x50,0x72,0x65,0x6D,0x69,0x75,0x6D,0x30,0x82,0x02,0x22, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03, +0x82,0x02,0x0F,0x00,0x30,0x82,0x02,0x0A,0x02,0x82,0x02,0x01,0x00,0xC4,0x12,0xDF, +0xA9,0x5F,0xFE,0x41,0xDD,0xDD,0xF5,0x9F,0x8A,0xE3,0xF6,0xAC,0xE1,0x3C,0x78,0x9A, +0xBC,0xD8,0xF0,0x7F,0x7A,0xA0,0x33,0x2A,0xDC,0x8D,0x20,0x5B,0xAE,0x2D,0x6F,0xE7, +0x93,0xD9,0x36,0x70,0x6A,0x68,0xCF,0x8E,0x51,0xA3,0x85,0x5B,0x67,0x04,0xA0,0x10, +0x24,0x6F,0x5D,0x28,0x82,0xC1,0x97,0x57,0xD8,0x48,0x29,0x13,0xB6,0xE1,0xBE,0x91, +0x4D,0xDF,0x85,0x0C,0x53,0x18,0x9A,0x1E,0x24,0xA2,0x4F,0x8F,0xF0,0xA2,0x85,0x0B, +0xCB,0xF4,0x29,0x7F,0xD2,0xA4,0x58,0xEE,0x26,0x4D,0xC9,0xAA,0xA8,0x7B,0x9A,0xD9, +0xFA,0x38,0xDE,0x44,0x57,0x15,0xE5,0xF8,0x8C,0xC8,0xD9,0x48,0xE2,0x0D,0x16,0x27, +0x1D,0x1E,0xC8,0x83,0x85,0x25,0xB7,0xBA,0xAA,0x55,0x41,0xCC,0x03,0x22,0x4B,0x2D, +0x91,0x8D,0x8B,0xE6,0x89,0xAF,0x66,0xC7,0xE9,0xFF,0x2B,0xE9,0x3C,0xAC,0xDA,0xD2, +0xB3,0xC3,0xE1,0x68,0x9C,0x89,0xF8,0x7A,0x00,0x56,0xDE,0xF4,0x55,0x95,0x6C,0xFB, +0xBA,0x64,0xDD,0x62,0x8B,0xDF,0x0B,0x77,0x32,0xEB,0x62,0xCC,0x26,0x9A,0x9B,0xBB, +0xAA,0x62,0x83,0x4C,0xB4,0x06,0x7A,0x30,0xC8,0x29,0xBF,0xED,0x06,0x4D,0x97,0xB9, +0x1C,0xC4,0x31,0x2B,0xD5,0x5F,0xBC,0x53,0x12,0x17,0x9C,0x99,0x57,0x29,0x66,0x77, +0x61,0x21,0x31,0x07,0x2E,0x25,0x49,0x9D,0x18,0xF2,0xEE,0xF3,0x2B,0x71,0x8C,0xB5, +0xBA,0x39,0x07,0x49,0x77,0xFC,0xEF,0x2E,0x92,0x90,0x05,0x8D,0x2D,0x2F,0x77,0x7B, +0xEF,0x43,0xBF,0x35,0xBB,0x9A,0xD8,0xF9,0x73,0xA7,0x2C,0xF2,0xD0,0x57,0xEE,0x28, +0x4E,0x26,0x5F,0x8F,0x90,0x68,0x09,0x2F,0xB8,0xF8,0xDC,0x06,0xE9,0x2E,0x9A,0x3E, +0x51,0xA7,0xD1,0x22,0xC4,0x0A,0xA7,0x38,0x48,0x6C,0xB3,0xF9,0xFF,0x7D,0xAB,0x86, +0x57,0xE3,0xBA,0xD6,0x85,0x78,0x77,0xBA,0x43,0xEA,0x48,0x7F,0xF6,0xD8,0xBE,0x23, +0x6D,0x1E,0xBF,0xD1,0x36,0x6C,0x58,0x5C,0xF1,0xEE,0xA4,0x19,0x54,0x1A,0xF5,0x03, +0xD2,0x76,0xE6,0xE1,0x8C,0xBD,0x3C,0xB3,0xD3,0x48,0x4B,0xE2,0xC8,0xF8,0x7F,0x92, +0xA8,0x76,0x46,0x9C,0x42,0x65,0x3E,0xA4,0x1E,0xC1,0x07,0x03,0x5A,0x46,0x2D,0xB8, +0x97,0xF3,0xB7,0xD5,0xB2,0x55,0x21,0xEF,0xBA,0xDC,0x4C,0x00,0x97,0xFB,0x14,0x95, +0x27,0x33,0xBF,0xE8,0x43,0x47,0x46,0xD2,0x08,0x99,0x16,0x60,0x3B,0x9A,0x7E,0xD2, +0xE6,0xED,0x38,0xEA,0xEC,0x01,0x1E,0x3C,0x48,0x56,0x49,0x09,0xC7,0x4C,0x37,0x00, +0x9E,0x88,0x0E,0xC0,0x73,0xE1,0x6F,0x66,0xE9,0x72,0x47,0x30,0x3E,0x10,0xE5,0x0B, +0x03,0xC9,0x9A,0x42,0x00,0x6C,0xC5,0x94,0x7E,0x61,0xC4,0x8A,0xDF,0x7F,0x82,0x1A, +0x0B,0x59,0xC4,0x59,0x32,0x77,0xB3,0xBC,0x60,0x69,0x56,0x39,0xFD,0xB4,0x06,0x7B, +0x2C,0xD6,0x64,0x36,0xD9,0xBD,0x48,0xED,0x84,0x1F,0x7E,0xA5,0x22,0x8F,0x2A,0xB8, +0x42,0xF4,0x82,0xB7,0xD4,0x53,0x90,0x78,0x4E,0x2D,0x1A,0xFD,0x81,0x6F,0x44,0xD7, +0x3B,0x01,0x74,0x96,0x42,0xE0,0x00,0xE2,0x2E,0x6B,0xEA,0xC5,0xEE,0x72,0xAC,0xBB, +0xBF,0xFE,0xEA,0xAA,0xA8,0xF8,0xDC,0xF6,0xB2,0x79,0x8A,0xB6,0x67,0x02,0x03,0x01, +0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04, +0x14,0x9D,0xC0,0x67,0xA6,0x0C,0x22,0xD9,0x26,0xF5,0x45,0xAB,0xA6,0x65,0x52,0x11, +0x27,0xD8,0x45,0xAC,0x63,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04, +0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D, +0x01,0x01,0x0C,0x05,0x00,0x03,0x82,0x02,0x01,0x00,0xB3,0x57,0x4D,0x10,0x62,0x4E, +0x3A,0xE4,0xAC,0xEA,0xB8,0x1C,0xAF,0x32,0x23,0xC8,0xB3,0x49,0x5A,0x51,0x9C,0x76, +0x28,0x8D,0x79,0xAA,0x57,0x46,0x17,0xD5,0xF5,0x52,0xF6,0xB7,0x44,0xE8,0x08,0x44, +0xBF,0x18,0x84,0xD2,0x0B,0x80,0xCD,0xC5,0x12,0xFD,0x00,0x55,0x05,0x61,0x87,0x41, +0xDC,0xB5,0x24,0x9E,0x3C,0xC4,0xD8,0xC8,0xFB,0x70,0x9E,0x2F,0x78,0x96,0x83,0x20, +0x36,0xDE,0x7C,0x0F,0x69,0x13,0x88,0xA5,0x75,0x36,0x98,0x08,0xA6,0xC6,0xDF,0xAC, +0xCE,0xE3,0x58,0xD6,0xB7,0x3E,0xDE,0xBA,0xF3,0xEB,0x34,0x40,0xD8,0xA2,0x81,0xF5, +0x78,0x3F,0x2F,0xD5,0xA5,0xFC,0xD9,0xA2,0xD4,0x5E,0x04,0x0E,0x17,0xAD,0xFE,0x41, +0xF0,0xE5,0xB2,0x72,0xFA,0x44,0x82,0x33,0x42,0xE8,0x2D,0x58,0xF7,0x56,0x8C,0x62, +0x3F,0xBA,0x42,0xB0,0x9C,0x0C,0x5C,0x7E,0x2E,0x65,0x26,0x5C,0x53,0x4F,0x00,0xB2, +0x78,0x7E,0xA1,0x0D,0x99,0x2D,0x8D,0xB8,0x1D,0x8E,0xA2,0xC4,0xB0,0xFD,0x60,0xD0, +0x30,0xA4,0x8E,0xC8,0x04,0x62,0xA9,0xC4,0xED,0x35,0xDE,0x7A,0x97,0xED,0x0E,0x38, +0x5E,0x92,0x2F,0x93,0x70,0xA5,0xA9,0x9C,0x6F,0xA7,0x7D,0x13,0x1D,0x7E,0xC6,0x08, +0x48,0xB1,0x5E,0x67,0xEB,0x51,0x08,0x25,0xE9,0xE6,0x25,0x6B,0x52,0x29,0x91,0x9C, +0xD2,0x39,0x73,0x08,0x57,0xDE,0x99,0x06,0xB4,0x5B,0x9D,0x10,0x06,0xE1,0xC2,0x00, +0xA8,0xB8,0x1C,0x4A,0x02,0x0A,0x14,0xD0,0xC1,0x41,0xCA,0xFB,0x8C,0x35,0x21,0x7D, +0x82,0x38,0xF2,0xA9,0x54,0x91,0x19,0x35,0x93,0x94,0x6D,0x6A,0x3A,0xC5,0xB2,0xD0, +0xBB,0x89,0x86,0x93,0xE8,0x9B,0xC9,0x0F,0x3A,0xA7,0x7A,0xB8,0xA1,0xF0,0x78,0x46, +0xFA,0xFC,0x37,0x2F,0xE5,0x8A,0x84,0xF3,0xDF,0xFE,0x04,0xD9,0xA1,0x68,0xA0,0x2F, +0x24,0xE2,0x09,0x95,0x06,0xD5,0x95,0xCA,0xE1,0x24,0x96,0xEB,0x7C,0xF6,0x93,0x05, +0xBB,0xED,0x73,0xE9,0x2D,0xD1,0x75,0x39,0xD7,0xE7,0x24,0xDB,0xD8,0x4E,0x5F,0x43, +0x8F,0x9E,0xD0,0x14,0x39,0xBF,0x55,0x70,0x48,0x99,0x57,0x31,0xB4,0x9C,0xEE,0x4A, +0x98,0x03,0x96,0x30,0x1F,0x60,0x06,0xEE,0x1B,0x23,0xFE,0x81,0x60,0x23,0x1A,0x47, +0x62,0x85,0xA5,0xCC,0x19,0x34,0x80,0x6F,0xB3,0xAC,0x1A,0xE3,0x9F,0xF0,0x7B,0x48, +0xAD,0xD5,0x01,0xD9,0x67,0xB6,0xA9,0x72,0x93,0xEA,0x2D,0x66,0xB5,0xB2,0xB8,0xE4, +0x3D,0x3C,0xB2,0xEF,0x4C,0x8C,0xEA,0xEB,0x07,0xBF,0xAB,0x35,0x9A,0x55,0x86,0xBC, +0x18,0xA6,0xB5,0xA8,0x5E,0xB4,0x83,0x6C,0x6B,0x69,0x40,0xD3,0x9F,0xDC,0xF1,0xC3, +0x69,0x6B,0xB9,0xE1,0x6D,0x09,0xF4,0xF1,0xAA,0x50,0x76,0x0A,0x7A,0x7D,0x7A,0x17, +0xA1,0x55,0x96,0x42,0x99,0x31,0x09,0xDD,0x60,0x11,0x8D,0x05,0x30,0x7E,0xE6,0x8E, +0x46,0xD1,0x9D,0x14,0xDA,0xC7,0x17,0xE4,0x05,0x96,0x8C,0xC4,0x24,0xB5,0x1B,0xCF, +0x14,0x07,0xB2,0x40,0xF8,0xA3,0x9E,0x41,0x86,0xBC,0x04,0xD0,0x6B,0x96,0xC8,0x2A, +0x80,0x34,0xFD,0xBF,0xEF,0x06,0xA3,0xDD,0x58,0xC5,0x85,0x3D,0x3E,0x8F,0xFE,0x9E, +0x29,0xE0,0xB6,0xB8,0x09,0x68,0x19,0x1C,0x18,0x43, +}; + +/* subject: Common Name: AffirmTrust Premium ECC, Organization: AffirmTrust, Country: US */ +/* issuer: Common Name: AffirmTrust Premium ECC, Organization: AffirmTrust, Country: US */ +/* link: https://crt.sh/?q=bd71fdf6da97e4cf62d1647add2581b07d79adf8397eb4ecba9c5e8488821423 */ +const unsigned char kCertificateWithFingerprint_bd71fdf6da97e4cf62d1647add2581b07d79adf8397eb4ecba9c5e8488821423_certificate[514]={ +0x30,0x82,0x01,0xFE,0x30,0x82,0x01,0x85,0xA0,0x03,0x02,0x01,0x02,0x02,0x08,0x74, +0x97,0x25,0x8A,0xC7,0x3F,0x7A,0x54,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D, +0x04,0x03,0x03,0x30,0x45,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02, +0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0B,0x41,0x66,0x66, +0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31,0x20,0x30,0x1E,0x06,0x03,0x55,0x04, +0x03,0x0C,0x17,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x20,0x50, +0x72,0x65,0x6D,0x69,0x75,0x6D,0x20,0x45,0x43,0x43,0x30,0x1E,0x17,0x0D,0x31,0x30, +0x30,0x31,0x32,0x39,0x31,0x34,0x32,0x30,0x32,0x34,0x5A,0x17,0x0D,0x34,0x30,0x31, +0x32,0x33,0x31,0x31,0x34,0x32,0x30,0x32,0x34,0x5A,0x30,0x45,0x31,0x0B,0x30,0x09, +0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55, +0x04,0x0A,0x0C,0x0B,0x41,0x66,0x66,0x69,0x72,0x6D,0x54,0x72,0x75,0x73,0x74,0x31, +0x20,0x30,0x1E,0x06,0x03,0x55,0x04,0x03,0x0C,0x17,0x41,0x66,0x66,0x69,0x72,0x6D, +0x54,0x72,0x75,0x73,0x74,0x20,0x50,0x72,0x65,0x6D,0x69,0x75,0x6D,0x20,0x45,0x43, +0x43,0x30,0x76,0x30,0x10,0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05, +0x2B,0x81,0x04,0x00,0x22,0x03,0x62,0x00,0x04,0x0D,0x30,0x5E,0x1B,0x15,0x9D,0x03, +0xD0,0xA1,0x79,0x35,0xB7,0x3A,0x3C,0x92,0x7A,0xCA,0x15,0x1C,0xCD,0x62,0xF3,0x9C, +0x26,0x5C,0x07,0x3D,0xE5,0x54,0xFA,0xA3,0xD6,0xCC,0x12,0xEA,0xF4,0x14,0x5F,0xE8, +0x8E,0x19,0xAB,0x2F,0x2E,0x48,0xE6,0xAC,0x18,0x43,0x78,0xAC,0xD0,0x37,0xC3,0xBD, +0xB2,0xCD,0x2C,0xE6,0x47,0xE2,0x1A,0xE6,0x63,0xB8,0x3D,0x2E,0x2F,0x78,0xC4,0x4F, +0xDB,0xF4,0x0F,0xA4,0x68,0x4C,0x55,0x72,0x6B,0x95,0x1D,0x4E,0x18,0x42,0x95,0x78, +0xCC,0x37,0x3C,0x91,0xE2,0x9B,0x65,0x2B,0x29,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06, +0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x9A,0xAF,0x29,0x7A,0xC0,0x11,0x35,0x35, +0x26,0x51,0x30,0x00,0xC3,0x6A,0xFE,0x40,0xD5,0xAE,0xD6,0x3C,0x30,0x0F,0x06,0x03, +0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06, +0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0A,0x06, +0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x03,0x67,0x00,0x30,0x64,0x02,0x30, +0x17,0x09,0xF3,0x87,0x88,0x50,0x5A,0xAF,0xC8,0xC0,0x42,0xBF,0x47,0x5F,0xF5,0x6C, +0x6A,0x86,0xE0,0xC4,0x27,0x74,0xE4,0x38,0x53,0xD7,0x05,0x7F,0x1B,0x34,0xE3,0xC6, +0x2F,0xB3,0xCA,0x09,0x3C,0x37,0x9D,0xD7,0xE7,0xB8,0x46,0xF1,0xFD,0xA1,0xE2,0x71, +0x02,0x30,0x42,0x59,0x87,0x43,0xD4,0x51,0xDF,0xBA,0xD3,0x09,0x32,0x5A,0xCE,0x88, +0x7E,0x57,0x3D,0x9C,0x5F,0x42,0x6B,0xF5,0x07,0x2D,0xB5,0xF0,0x82,0x93,0xF9,0x59, +0x6F,0xAE,0x64,0xFA,0x58,0xE5,0x8B,0x1E,0xE3,0x63,0xBE,0xB5,0x81,0xCD,0x6F,0x02, +0x8C,0x79, +}; + +/* subject: Common Name: GlobalSign Root CA, Organizational Unit: Root CA, Organization: GlobalSign nv-sa, Country: BE */ +/* issuer: Common Name: GlobalSign Root CA, Organizational Unit: Root CA, Organization: GlobalSign nv-sa, Country: BE */ +/* link: https://crt.sh/?q=ebd41040e4bb3ec742c9e381d31ef2a41a48b6685c96e7cef3c1df6cd4331c99 */ +const unsigned char kCertificateWithFingerprint_ebd41040e4bb3ec742c9e381d31ef2a41a48b6685c96e7cef3c1df6cd4331c99_certificate[889]={ +0x30,0x82,0x03,0x75,0x30,0x82,0x02,0x5D,0xA0,0x03,0x02,0x01,0x02,0x02,0x0B,0x04, +0x00,0x00,0x00,0x00,0x01,0x15,0x4B,0x5A,0xC3,0x94,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x57,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x42,0x45,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04, +0x0A,0x13,0x10,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x20,0x6E,0x76, +0x2D,0x73,0x61,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x0B,0x13,0x07,0x52,0x6F, +0x6F,0x74,0x20,0x43,0x41,0x31,0x1B,0x30,0x19,0x06,0x03,0x55,0x04,0x03,0x13,0x12, +0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x20,0x52,0x6F,0x6F,0x74,0x20, +0x43,0x41,0x30,0x1E,0x17,0x0D,0x39,0x38,0x30,0x39,0x30,0x31,0x31,0x32,0x30,0x30, +0x30,0x30,0x5A,0x17,0x0D,0x32,0x38,0x30,0x31,0x32,0x38,0x31,0x32,0x30,0x30,0x30, +0x30,0x5A,0x30,0x57,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x42, +0x45,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0A,0x13,0x10,0x47,0x6C,0x6F,0x62, +0x61,0x6C,0x53,0x69,0x67,0x6E,0x20,0x6E,0x76,0x2D,0x73,0x61,0x31,0x10,0x30,0x0E, +0x06,0x03,0x55,0x04,0x0B,0x13,0x07,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x31,0x1B, +0x30,0x19,0x06,0x03,0x55,0x04,0x03,0x13,0x12,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53, +0x69,0x67,0x6E,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82, +0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xDA,0x0E,0xE6,0x99, +0x8D,0xCE,0xA3,0xE3,0x4F,0x8A,0x7E,0xFB,0xF1,0x8B,0x83,0x25,0x6B,0xEA,0x48,0x1F, +0xF1,0x2A,0xB0,0xB9,0x95,0x11,0x04,0xBD,0xF0,0x63,0xD1,0xE2,0x67,0x66,0xCF,0x1C, +0xDD,0xCF,0x1B,0x48,0x2B,0xEE,0x8D,0x89,0x8E,0x9A,0xAF,0x29,0x80,0x65,0xAB,0xE9, +0xC7,0x2D,0x12,0xCB,0xAB,0x1C,0x4C,0x70,0x07,0xA1,0x3D,0x0A,0x30,0xCD,0x15,0x8D, +0x4F,0xF8,0xDD,0xD4,0x8C,0x50,0x15,0x1C,0xEF,0x50,0xEE,0xC4,0x2E,0xF7,0xFC,0xE9, +0x52,0xF2,0x91,0x7D,0xE0,0x6D,0xD5,0x35,0x30,0x8E,0x5E,0x43,0x73,0xF2,0x41,0xE9, +0xD5,0x6A,0xE3,0xB2,0x89,0x3A,0x56,0x39,0x38,0x6F,0x06,0x3C,0x88,0x69,0x5B,0x2A, +0x4D,0xC5,0xA7,0x54,0xB8,0x6C,0x89,0xCC,0x9B,0xF9,0x3C,0xCA,0xE5,0xFD,0x89,0xF5, +0x12,0x3C,0x92,0x78,0x96,0xD6,0xDC,0x74,0x6E,0x93,0x44,0x61,0xD1,0x8D,0xC7,0x46, +0xB2,0x75,0x0E,0x86,0xE8,0x19,0x8A,0xD5,0x6D,0x6C,0xD5,0x78,0x16,0x95,0xA2,0xE9, +0xC8,0x0A,0x38,0xEB,0xF2,0x24,0x13,0x4F,0x73,0x54,0x93,0x13,0x85,0x3A,0x1B,0xBC, +0x1E,0x34,0xB5,0x8B,0x05,0x8C,0xB9,0x77,0x8B,0xB1,0xDB,0x1F,0x20,0x91,0xAB,0x09, +0x53,0x6E,0x90,0xCE,0x7B,0x37,0x74,0xB9,0x70,0x47,0x91,0x22,0x51,0x63,0x16,0x79, +0xAE,0xB1,0xAE,0x41,0x26,0x08,0xC8,0x19,0x2B,0xD1,0x46,0xAA,0x48,0xD6,0x64,0x2A, +0xD7,0x83,0x34,0xFF,0x2C,0x2A,0xC1,0x6C,0x19,0x43,0x4A,0x07,0x85,0xE7,0xD3,0x7C, +0xF6,0x21,0x68,0xEF,0xEA,0xF2,0x52,0x9F,0x7F,0x93,0x90,0xCF,0x02,0x03,0x01,0x00, +0x01,0xA3,0x42,0x30,0x40,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04, +0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04, +0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04, +0x14,0x60,0x7B,0x66,0x1A,0x45,0x0D,0x97,0xCA,0x89,0x50,0x2F,0x7D,0x04,0xCD,0x34, +0xA8,0xFF,0xFC,0xFD,0x4B,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0xD6,0x73,0xE7,0x7C,0x4F,0x76,0xD0, +0x8D,0xBF,0xEC,0xBA,0xA2,0xBE,0x34,0xC5,0x28,0x32,0xB5,0x7C,0xFC,0x6C,0x9C,0x2C, +0x2B,0xBD,0x09,0x9E,0x53,0xBF,0x6B,0x5E,0xAA,0x11,0x48,0xB6,0xE5,0x08,0xA3,0xB3, +0xCA,0x3D,0x61,0x4D,0xD3,0x46,0x09,0xB3,0x3E,0xC3,0xA0,0xE3,0x63,0x55,0x1B,0xF2, +0xBA,0xEF,0xAD,0x39,0xE1,0x43,0xB9,0x38,0xA3,0xE6,0x2F,0x8A,0x26,0x3B,0xEF,0xA0, +0x50,0x56,0xF9,0xC6,0x0A,0xFD,0x38,0xCD,0xC4,0x0B,0x70,0x51,0x94,0x97,0x98,0x04, +0xDF,0xC3,0x5F,0x94,0xD5,0x15,0xC9,0x14,0x41,0x9C,0xC4,0x5D,0x75,0x64,0x15,0x0D, +0xFF,0x55,0x30,0xEC,0x86,0x8F,0xFF,0x0D,0xEF,0x2C,0xB9,0x63,0x46,0xF6,0xAA,0xFC, +0xDF,0xBC,0x69,0xFD,0x2E,0x12,0x48,0x64,0x9A,0xE0,0x95,0xF0,0xA6,0xEF,0x29,0x8F, +0x01,0xB1,0x15,0xB5,0x0C,0x1D,0xA5,0xFE,0x69,0x2C,0x69,0x24,0x78,0x1E,0xB3,0xA7, +0x1C,0x71,0x62,0xEE,0xCA,0xC8,0x97,0xAC,0x17,0x5D,0x8A,0xC2,0xF8,0x47,0x86,0x6E, +0x2A,0xC4,0x56,0x31,0x95,0xD0,0x67,0x89,0x85,0x2B,0xF9,0x6C,0xA6,0x5D,0x46,0x9D, +0x0C,0xAA,0x82,0xE4,0x99,0x51,0xDD,0x70,0xB7,0xDB,0x56,0x3D,0x61,0xE4,0x6A,0xE1, +0x5C,0xD6,0xF6,0xFE,0x3D,0xDE,0x41,0xCC,0x07,0xAE,0x63,0x52,0xBF,0x53,0x53,0xF4, +0x2B,0xE9,0xC7,0xFD,0xB6,0xF7,0x82,0x5F,0x85,0xD2,0x41,0x18,0xDB,0x81,0xB3,0x04, +0x1C,0xC5,0x1F,0xA4,0x80,0x6F,0x15,0x20,0xC9,0xDE,0x0C,0x88,0x0A,0x1D,0xD6,0x66, +0x55,0xE2,0xFC,0x48,0xC9,0x29,0x26,0x69,0xE0, +}; + +/* subject: Common Name: GlobalSign, Organization: GlobalSign, Organizational Unit: GlobalSign Root CA - R3 */ +/* issuer: Common Name: GlobalSign, Organization: GlobalSign, Organizational Unit: GlobalSign Root CA - R3 */ +/* link: https://crt.sh/?q=cbb522d7b7f127ad6a0113865bdf1cd4102e7d0759af635a7cf4720dc963c53b */ +const unsigned char kCertificateWithFingerprint_cbb522d7b7f127ad6a0113865bdf1cd4102e7d0759af635a7cf4720dc963c53b_certificate[867]={ +0x30,0x82,0x03,0x5F,0x30,0x82,0x02,0x47,0xA0,0x03,0x02,0x01,0x02,0x02,0x0B,0x04, +0x00,0x00,0x00,0x00,0x01,0x21,0x58,0x53,0x08,0xA2,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x4C,0x31,0x20,0x30,0x1E,0x06, +0x03,0x55,0x04,0x0B,0x13,0x17,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E, +0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x52,0x33,0x31,0x13,0x30, +0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69, +0x67,0x6E,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0A,0x47,0x6C,0x6F, +0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x30,0x1E,0x17,0x0D,0x30,0x39,0x30,0x33,0x31, +0x38,0x31,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x39,0x30,0x33,0x31,0x38, +0x31,0x30,0x30,0x30,0x30,0x30,0x5A,0x30,0x4C,0x31,0x20,0x30,0x1E,0x06,0x03,0x55, +0x04,0x0B,0x13,0x17,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x20,0x52, +0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x52,0x33,0x31,0x13,0x30,0x11,0x06, +0x03,0x55,0x04,0x0A,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E, +0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61, +0x6C,0x53,0x69,0x67,0x6E,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48, +0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01, +0x0A,0x02,0x82,0x01,0x01,0x00,0xCC,0x25,0x76,0x90,0x79,0x06,0x78,0x22,0x16,0xF5, +0xC0,0x83,0xB6,0x84,0xCA,0x28,0x9E,0xFD,0x05,0x76,0x11,0xC5,0xAD,0x88,0x72,0xFC, +0x46,0x02,0x43,0xC7,0xB2,0x8A,0x9D,0x04,0x5F,0x24,0xCB,0x2E,0x4B,0xE1,0x60,0x82, +0x46,0xE1,0x52,0xAB,0x0C,0x81,0x47,0x70,0x6C,0xDD,0x64,0xD1,0xEB,0xF5,0x2C,0xA3, +0x0F,0x82,0x3D,0x0C,0x2B,0xAE,0x97,0xD7,0xB6,0x14,0x86,0x10,0x79,0xBB,0x3B,0x13, +0x80,0x77,0x8C,0x08,0xE1,0x49,0xD2,0x6A,0x62,0x2F,0x1F,0x5E,0xFA,0x96,0x68,0xDF, +0x89,0x27,0x95,0x38,0x9F,0x06,0xD7,0x3E,0xC9,0xCB,0x26,0x59,0x0D,0x73,0xDE,0xB0, +0xC8,0xE9,0x26,0x0E,0x83,0x15,0xC6,0xEF,0x5B,0x8B,0xD2,0x04,0x60,0xCA,0x49,0xA6, +0x28,0xF6,0x69,0x3B,0xF6,0xCB,0xC8,0x28,0x91,0xE5,0x9D,0x8A,0x61,0x57,0x37,0xAC, +0x74,0x14,0xDC,0x74,0xE0,0x3A,0xEE,0x72,0x2F,0x2E,0x9C,0xFB,0xD0,0xBB,0xBF,0xF5, +0x3D,0x00,0xE1,0x06,0x33,0xE8,0x82,0x2B,0xAE,0x53,0xA6,0x3A,0x16,0x73,0x8C,0xDD, +0x41,0x0E,0x20,0x3A,0xC0,0xB4,0xA7,0xA1,0xE9,0xB2,0x4F,0x90,0x2E,0x32,0x60,0xE9, +0x57,0xCB,0xB9,0x04,0x92,0x68,0x68,0xE5,0x38,0x26,0x60,0x75,0xB2,0x9F,0x77,0xFF, +0x91,0x14,0xEF,0xAE,0x20,0x49,0xFC,0xAD,0x40,0x15,0x48,0xD1,0x02,0x31,0x61,0x19, +0x5E,0xB8,0x97,0xEF,0xAD,0x77,0xB7,0x64,0x9A,0x7A,0xBF,0x5F,0xC1,0x13,0xEF,0x9B, +0x62,0xFB,0x0D,0x6C,0xE0,0x54,0x69,0x16,0xA9,0x03,0xDA,0x6E,0xE9,0x83,0x93,0x71, +0x76,0xC6,0x69,0x85,0x82,0x17,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30, +0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30, +0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF, +0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x8F,0xF0,0x4B,0x7F,0xA8, +0x2E,0x45,0x24,0xAE,0x4D,0x50,0xFA,0x63,0x9A,0x8B,0xDE,0xE2,0xDD,0x1B,0xBC,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82, +0x01,0x01,0x00,0x4B,0x40,0xDB,0xC0,0x50,0xAA,0xFE,0xC8,0x0C,0xEF,0xF7,0x96,0x54, +0x45,0x49,0xBB,0x96,0x00,0x09,0x41,0xAC,0xB3,0x13,0x86,0x86,0x28,0x07,0x33,0xCA, +0x6B,0xE6,0x74,0xB9,0xBA,0x00,0x2D,0xAE,0xA4,0x0A,0xD3,0xF5,0xF1,0xF1,0x0F,0x8A, +0xBF,0x73,0x67,0x4A,0x83,0xC7,0x44,0x7B,0x78,0xE0,0xAF,0x6E,0x6C,0x6F,0x03,0x29, +0x8E,0x33,0x39,0x45,0xC3,0x8E,0xE4,0xB9,0x57,0x6C,0xAA,0xFC,0x12,0x96,0xEC,0x53, +0xC6,0x2D,0xE4,0x24,0x6C,0xB9,0x94,0x63,0xFB,0xDC,0x53,0x68,0x67,0x56,0x3E,0x83, +0xB8,0xCF,0x35,0x21,0xC3,0xC9,0x68,0xFE,0xCE,0xDA,0xC2,0x53,0xAA,0xCC,0x90,0x8A, +0xE9,0xF0,0x5D,0x46,0x8C,0x95,0xDD,0x7A,0x58,0x28,0x1A,0x2F,0x1D,0xDE,0xCD,0x00, +0x37,0x41,0x8F,0xED,0x44,0x6D,0xD7,0x53,0x28,0x97,0x7E,0xF3,0x67,0x04,0x1E,0x15, +0xD7,0x8A,0x96,0xB4,0xD3,0xDE,0x4C,0x27,0xA4,0x4C,0x1B,0x73,0x73,0x76,0xF4,0x17, +0x99,0xC2,0x1F,0x7A,0x0E,0xE3,0x2D,0x08,0xAD,0x0A,0x1C,0x2C,0xFF,0x3C,0xAB,0x55, +0x0E,0x0F,0x91,0x7E,0x36,0xEB,0xC3,0x57,0x49,0xBE,0xE1,0x2E,0x2D,0x7C,0x60,0x8B, +0xC3,0x41,0x51,0x13,0x23,0x9D,0xCE,0xF7,0x32,0x6B,0x94,0x01,0xA8,0x99,0xE7,0x2C, +0x33,0x1F,0x3A,0x3B,0x25,0xD2,0x86,0x40,0xCE,0x3B,0x2C,0x86,0x78,0xC9,0x61,0x2F, +0x14,0xBA,0xEE,0xDB,0x55,0x6F,0xDF,0x84,0xEE,0x05,0x09,0x4D,0xBD,0x28,0xD8,0x72, +0xCE,0xD3,0x62,0x50,0x65,0x1E,0xEB,0x92,0x97,0x83,0x31,0xD9,0xB3,0xB5,0xCA,0x47, +0x58,0x3F,0x5F, +}; + +/* subject: Common Name: GlobalSign, Organization: GlobalSign, Organizational Unit: GlobalSign ECC Root CA - R5 */ +/* issuer: Common Name: GlobalSign, Organization: GlobalSign, Organizational Unit: GlobalSign ECC Root CA - R5 */ +/* link: https://crt.sh/?q=179fbc148a3dd00fd24ea13458cc43bfa7f59c8182d783a513f6ebec100c8924 */ +const unsigned char kCertificateWithFingerprint_179fbc148a3dd00fd24ea13458cc43bfa7f59c8182d783a513f6ebec100c8924_certificate[546]={ +0x30,0x82,0x02,0x1E,0x30,0x82,0x01,0xA4,0xA0,0x03,0x02,0x01,0x02,0x02,0x11,0x60, +0x59,0x49,0xE0,0x26,0x2E,0xBB,0x55,0xF9,0x0A,0x77,0x8A,0x71,0xF9,0x4A,0xD8,0x6C, +0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x50,0x31,0x24, +0x30,0x22,0x06,0x03,0x55,0x04,0x0B,0x13,0x1B,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53, +0x69,0x67,0x6E,0x20,0x45,0x43,0x43,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x20, +0x2D,0x20,0x52,0x35,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x47, +0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x31,0x13,0x30,0x11,0x06,0x03,0x55, +0x04,0x03,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x30,0x1E, +0x17,0x0D,0x31,0x32,0x31,0x31,0x31,0x33,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17, +0x0D,0x33,0x38,0x30,0x31,0x31,0x39,0x30,0x33,0x31,0x34,0x30,0x37,0x5A,0x30,0x50, +0x31,0x24,0x30,0x22,0x06,0x03,0x55,0x04,0x0B,0x13,0x1B,0x47,0x6C,0x6F,0x62,0x61, +0x6C,0x53,0x69,0x67,0x6E,0x20,0x45,0x43,0x43,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43, +0x41,0x20,0x2D,0x20,0x52,0x35,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13, +0x0A,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x31,0x13,0x30,0x11,0x06, +0x03,0x55,0x04,0x03,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E, +0x30,0x76,0x30,0x10,0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05,0x2B, +0x81,0x04,0x00,0x22,0x03,0x62,0x00,0x04,0x47,0x45,0x0E,0x96,0xFB,0x7D,0x5D,0xBF, +0xE9,0x39,0xD1,0x21,0xF8,0x9F,0x0B,0xB6,0xD5,0x7B,0x1E,0x92,0x3A,0x48,0x59,0x1C, +0xF0,0x62,0x31,0x2D,0xC0,0x7A,0x28,0xFE,0x1A,0xA7,0x5C,0xB3,0xB6,0xCC,0x97,0xE7, +0x45,0xD4,0x58,0xFA,0xD1,0x77,0x6D,0x43,0xA2,0xC0,0x87,0x65,0x34,0x0A,0x1F,0x7A, +0xDD,0xEB,0x3C,0x33,0xA1,0xC5,0x9D,0x4D,0xA4,0x6F,0x41,0x95,0x38,0x7F,0xC9,0x1E, +0x84,0xEB,0xD1,0x9E,0x49,0x92,0x87,0x94,0x87,0x0C,0x3A,0x85,0x4A,0x66,0x9F,0x9D, +0x59,0x93,0x4D,0x97,0x61,0x06,0x86,0x4A,0xA3,0x42,0x30,0x40,0x30,0x0E,0x06,0x03, +0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03, +0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06, +0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x3D,0xE6,0x29,0x48,0x9B,0xEA,0x07,0xCA, +0x21,0x44,0x4A,0x26,0xDE,0x6E,0xDE,0xD2,0x83,0xD0,0x9F,0x59,0x30,0x0A,0x06,0x08, +0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x03,0x68,0x00,0x30,0x65,0x02,0x31,0x00, +0xE5,0x69,0x12,0xC9,0x6E,0xDB,0xC6,0x31,0xBA,0x09,0x41,0xE1,0x97,0xF8,0xFB,0xFD, +0x9A,0xE2,0x7D,0x12,0xC9,0xED,0x7C,0x64,0xD3,0xCB,0x05,0x25,0x8B,0x56,0xD9,0xA0, +0xE7,0x5E,0x5D,0x4E,0x0B,0x83,0x9C,0x5B,0x76,0x29,0xA0,0x09,0x26,0x21,0x6A,0x62, +0x02,0x30,0x71,0xD2,0xB5,0x8F,0x5C,0xEA,0x3B,0xE1,0x78,0x09,0x85,0xA8,0x75,0x92, +0x3B,0xC8,0x5C,0xFD,0x48,0xEF,0x0D,0x74,0x22,0xA8,0x08,0xE2,0x6E,0xC5,0x49,0xCE, +0xC7,0x0C,0xBC,0xA7,0x61,0x69,0xF1,0xF7,0x3B,0xE1,0x2A,0xCB,0xF9,0x2B,0xF3,0x66, +0x90,0x37, +}; + +/* subject: Common Name: GlobalSign, Organization: GlobalSign, Organizational Unit: GlobalSign Root CA - R6 */ +/* issuer: Common Name: GlobalSign, Organization: GlobalSign, Organizational Unit: GlobalSign Root CA - R6 */ +/* link: https://crt.sh/?q=2cabeafe37d06ca22aba7391c0033d25982952c453647349763a3ab5ad6ccf69 */ +const unsigned char kCertificateWithFingerprint_2cabeafe37d06ca22aba7391c0033d25982952c453647349763a3ab5ad6ccf69_certificate[1415]={ +0x30,0x82,0x05,0x83,0x30,0x82,0x03,0x6B,0xA0,0x03,0x02,0x01,0x02,0x02,0x0E,0x45, +0xE6,0xBB,0x03,0x83,0x33,0xC3,0x85,0x65,0x48,0xE6,0xFF,0x45,0x51,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C,0x05,0x00,0x30,0x4C,0x31,0x20, +0x30,0x1E,0x06,0x03,0x55,0x04,0x0B,0x13,0x17,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53, +0x69,0x67,0x6E,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x52,0x36, +0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61, +0x6C,0x53,0x69,0x67,0x6E,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0A, +0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x30,0x1E,0x17,0x0D,0x31,0x34, +0x31,0x32,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x34,0x31, +0x32,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x30,0x4C,0x31,0x20,0x30,0x1E, +0x06,0x03,0x55,0x04,0x0B,0x13,0x17,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67, +0x6E,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x52,0x36,0x31,0x13, +0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53, +0x69,0x67,0x6E,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0A,0x47,0x6C, +0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x30,0x82,0x02,0x22,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x02,0x0F,0x00, +0x30,0x82,0x02,0x0A,0x02,0x82,0x02,0x01,0x00,0x95,0x07,0xE8,0x73,0xCA,0x66,0xF9, +0xEC,0x14,0xCA,0x7B,0x3C,0xF7,0x0D,0x08,0xF1,0xB4,0x45,0x0B,0x2C,0x82,0xB4,0x48, +0xC6,0xEB,0x5B,0x3C,0xAE,0x83,0xB8,0x41,0x92,0x33,0x14,0xA4,0x6F,0x7F,0xE9,0x2A, +0xCC,0xC6,0xB0,0x88,0x6B,0xC5,0xB6,0x89,0xD1,0xC6,0xB2,0xFF,0x14,0xCE,0x51,0x14, +0x21,0xEC,0x4A,0xDD,0x1B,0x5A,0xC6,0xD6,0x87,0xEE,0x4D,0x3A,0x15,0x06,0xED,0x64, +0x66,0x0B,0x92,0x80,0xCA,0x44,0xDE,0x73,0x94,0x4E,0xF3,0xA7,0x89,0x7F,0x4F,0x78, +0x63,0x08,0xC8,0x12,0x50,0x6D,0x42,0x66,0x2F,0x4D,0xB9,0x79,0x28,0x4D,0x52,0x1A, +0x8A,0x1A,0x80,0xB7,0x19,0x81,0x0E,0x7E,0xC4,0x8A,0xBC,0x64,0x4C,0x21,0x1C,0x43, +0x68,0xD7,0x3D,0x3C,0x8A,0xC5,0xB2,0x66,0xD5,0x90,0x9A,0xB7,0x31,0x06,0xC5,0xBE, +0xE2,0x6D,0x32,0x06,0xA6,0x1E,0xF9,0xB9,0xEB,0xAA,0xA3,0xB8,0xBF,0xBE,0x82,0x63, +0x50,0xD0,0xF0,0x18,0x89,0xDF,0xE4,0x0F,0x79,0xF5,0xEA,0xA2,0x1F,0x2A,0xD2,0x70, +0x2E,0x7B,0xE7,0xBC,0x93,0xBB,0x6D,0x53,0xE2,0x48,0x7C,0x8C,0x10,0x07,0x38,0xFF, +0x66,0xB2,0x77,0x61,0x7E,0xE0,0xEA,0x8C,0x3C,0xAA,0xB4,0xA4,0xF6,0xF3,0x95,0x4A, +0x12,0x07,0x6D,0xFD,0x8C,0xB2,0x89,0xCF,0xD0,0xA0,0x61,0x77,0xC8,0x58,0x74,0xB0, +0xD4,0x23,0x3A,0xF7,0x5D,0x3A,0xCA,0xA2,0xDB,0x9D,0x09,0xDE,0x5D,0x44,0x2D,0x90, +0xF1,0x81,0xCD,0x57,0x92,0xFA,0x7E,0xBC,0x50,0x04,0x63,0x34,0xDF,0x6B,0x93,0x18, +0xBE,0x6B,0x36,0xB2,0x39,0xE4,0xAC,0x24,0x36,0xB7,0xF0,0xEF,0xB6,0x1C,0x13,0x57, +0x93,0xB6,0xDE,0xB2,0xF8,0xE2,0x85,0xB7,0x73,0xA2,0xB8,0x35,0xAA,0x45,0xF2,0xE0, +0x9D,0x36,0xA1,0x6F,0x54,0x8A,0xF1,0x72,0x56,0x6E,0x2E,0x88,0xC5,0x51,0x42,0x44, +0x15,0x94,0xEE,0xA3,0xC5,0x38,0x96,0x9B,0x4E,0x4E,0x5A,0x0B,0x47,0xF3,0x06,0x36, +0x49,0x77,0x30,0xBC,0x71,0x37,0xE5,0xA6,0xEC,0x21,0x08,0x75,0xFC,0xE6,0x61,0x16, +0x3F,0x77,0xD5,0xD9,0x91,0x97,0x84,0x0A,0x6C,0xD4,0x02,0x4D,0x74,0xC0,0x14,0xED, +0xFD,0x39,0xFB,0x83,0xF2,0x5E,0x14,0xA1,0x04,0xB0,0x0B,0xE9,0xFE,0xEE,0x8F,0xE1, +0x6E,0x0B,0xB2,0x08,0xB3,0x61,0x66,0x09,0x6A,0xB1,0x06,0x3A,0x65,0x96,0x59,0xC0, +0xF0,0x35,0xFD,0xC9,0xDA,0x28,0x8D,0x1A,0x11,0x87,0x70,0x81,0x0A,0xA8,0x9A,0x75, +0x1D,0x9E,0x3A,0x86,0x05,0x00,0x9E,0xDB,0x80,0xD6,0x25,0xF9,0xDC,0x05,0x9E,0x27, +0x59,0x4C,0x76,0x39,0x5B,0xEA,0xF9,0xA5,0xA1,0xD8,0x83,0x0F,0xD1,0xFF,0xDF,0x30, +0x11,0xF9,0x85,0xCF,0x33,0x48,0xF5,0xCA,0x6D,0x64,0x14,0x2C,0x7A,0x58,0x4F,0xD3, +0x4B,0x08,0x49,0xC5,0x95,0x64,0x1A,0x63,0x0E,0x79,0x3D,0xF5,0xB3,0x8C,0xCA,0x58, +0xAD,0x9C,0x42,0x45,0x79,0x6E,0x0E,0x87,0x19,0x5C,0x54,0xB1,0x65,0xB6,0xBF,0x8C, +0x9B,0xDC,0x13,0xE9,0x0D,0x6F,0xB8,0x2E,0xDC,0x67,0x6E,0xC9,0x8B,0x11,0xB5,0x84, +0x14,0x8A,0x00,0x19,0x70,0x83,0x79,0x91,0x97,0x91,0xD4,0x1A,0x27,0xBF,0x37,0x1E, +0x32,0x07,0xD8,0x14,0x63,0x3C,0x28,0x4C,0xAF,0x02,0x03,0x01,0x00,0x01,0xA3,0x63, +0x30,0x61,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02, +0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03, +0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xAE,0x6C, +0x05,0xA3,0x93,0x13,0xE2,0xA2,0xE7,0xE2,0xD7,0x1C,0xD6,0xC7,0xF0,0x7F,0xC8,0x67, +0x53,0xA0,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xAE, +0x6C,0x05,0xA3,0x93,0x13,0xE2,0xA2,0xE7,0xE2,0xD7,0x1C,0xD6,0xC7,0xF0,0x7F,0xC8, +0x67,0x53,0xA0,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C, +0x05,0x00,0x03,0x82,0x02,0x01,0x00,0x83,0x25,0xED,0xE8,0xD1,0xFD,0x95,0x52,0xCD, +0x9E,0xC0,0x04,0xA0,0x91,0x69,0xE6,0x5C,0xD0,0x84,0xDE,0xDC,0xAD,0xA2,0x4F,0xE8, +0x47,0x78,0xD6,0x65,0x98,0xA9,0x5B,0xA8,0x3C,0x87,0x7C,0x02,0x8A,0xD1,0x6E,0xB7, +0x16,0x73,0xE6,0x5F,0xC0,0x54,0x98,0xD5,0x74,0xBE,0xC1,0xCD,0xE2,0x11,0x91,0xAD, +0x23,0x18,0x3D,0xDD,0xE1,0x72,0x44,0x96,0xB4,0x95,0x5E,0xC0,0x7B,0x8E,0x99,0x78, +0x16,0x43,0x13,0x56,0x57,0xB3,0xA2,0xB3,0x3B,0xB5,0x77,0xDC,0x40,0x72,0xAC,0xA3, +0xEB,0x9B,0x35,0x3E,0xB1,0x08,0x21,0xA1,0xE7,0xC4,0x43,0x37,0x79,0x32,0xBE,0xB5, +0xE7,0x9C,0x2C,0x4C,0xBC,0x43,0x29,0x99,0x8E,0x30,0xD3,0xAC,0x21,0xE0,0xE3,0x1D, +0xFA,0xD8,0x07,0x33,0x76,0x54,0x00,0x22,0x2A,0xB9,0x4D,0x20,0x2E,0x70,0x68,0xDA, +0xE5,0x53,0xFC,0x83,0x5C,0xD3,0x9D,0xF2,0xFF,0x44,0x0C,0x44,0x66,0xF2,0xD2,0xE3, +0xBD,0x46,0x00,0x1A,0x6D,0x02,0xBA,0x25,0x5D,0x8D,0xA1,0x31,0x51,0xDD,0x54,0x46, +0x1C,0x4D,0xDB,0x99,0x96,0xEF,0x1A,0x1C,0x04,0x5C,0xA6,0x15,0xEF,0x78,0xE0,0x79, +0xFE,0x5D,0xDB,0x3E,0xAA,0x4C,0x55,0xFD,0x9A,0x15,0xA9,0x6F,0xE1,0xA6,0xFB,0xDF, +0x70,0x30,0xE9,0xC3,0xEE,0x42,0x46,0xED,0xC2,0x93,0x05,0x89,0xFA,0x7D,0x63,0x7B, +0x3F,0xD0,0x71,0x81,0x7C,0x00,0xE8,0x98,0xAE,0x0E,0x78,0x34,0xC3,0x25,0xFB,0xAF, +0x0A,0x9F,0x20,0x6B,0xDD,0x3B,0x13,0x8F,0x12,0x8C,0xE2,0x41,0x1A,0x48,0x7A,0x73, +0xA0,0x77,0x69,0xC7,0xB6,0x5C,0x7F,0x82,0xC8,0x1E,0xFE,0x58,0x1B,0x28,0x2B,0xA8, +0x6C,0xAD,0x5E,0x6D,0xC0,0x05,0xD2,0x7B,0xB7,0xEB,0x80,0xFE,0x25,0x37,0xFE,0x02, +0x9B,0x68,0xAC,0x42,0x5D,0xC3,0xEE,0xF5,0xCC,0xDC,0xF0,0x50,0x75,0xD2,0x36,0x69, +0x9C,0xE6,0x7B,0x04,0xDF,0x6E,0x06,0x69,0xB6,0xDE,0x0A,0x09,0x48,0x59,0x87,0xEB, +0x7B,0x14,0x60,0x7A,0x64,0xAA,0x69,0x43,0xEF,0x91,0xC7,0x4C,0xEC,0x18,0xDD,0x6C, +0xEF,0x53,0x2D,0x8C,0x99,0xE1,0x5E,0xF2,0x72,0x3E,0xCF,0x54,0xC8,0xBD,0x67,0xEC, +0xA4,0x0F,0x4C,0x45,0xFF,0xD3,0xB9,0x30,0x23,0x07,0x4C,0x8F,0x10,0xBF,0x86,0x96, +0xD9,0x99,0x5A,0xB4,0x99,0x57,0x1C,0xA4,0xCC,0xBB,0x15,0x89,0x53,0xBA,0x2C,0x05, +0x0F,0xE4,0xC4,0x9E,0x19,0xB1,0x18,0x34,0xD5,0x4C,0x9D,0xBA,0xED,0xF7,0x1F,0xAF, +0x24,0x95,0x04,0x78,0xA8,0x03,0xBB,0xEE,0x81,0xE5,0xDA,0x5F,0x7C,0x8B,0x4A,0xA1, +0x90,0x74,0x25,0xA7,0xB3,0x3E,0x4B,0xC8,0x2C,0x56,0xBD,0xC7,0xC8,0xEF,0x38,0xE2, +0x5C,0x92,0xF0,0x79,0xF7,0x9C,0x84,0xBA,0x74,0x2D,0x61,0x01,0x20,0x7E,0x7E,0xD1, +0xF2,0x4F,0x07,0x59,0x5F,0x8B,0x2D,0x43,0x52,0xEB,0x46,0x0C,0x94,0xE1,0xF5,0x66, +0x47,0x79,0x77,0xD5,0x54,0x5B,0x1F,0xAD,0x24,0x37,0xCB,0x45,0x5A,0x4E,0xA0,0x44, +0x48,0xC8,0xD8,0xB0,0x99,0xC5,0x15,0x84,0x09,0xF6,0xD6,0x49,0x49,0xC0,0x65,0xB8, +0xE6,0x1A,0x71,0x6E,0xA0,0xA8,0xF1,0x82,0xE8,0x45,0x3E,0x6C,0xD6,0x02,0xD7,0x0A, +0x67,0x83,0x05,0x5A,0xC9,0xA4,0x10, +}; + +/* subject: Common Name: Go Daddy Root Certificate Authority - G2; Organization: GoDaddy.com, Inc.; Locality: Scottsdale; State/Province: Arizona; Country: US */ +/* issuer: Common Name: Go Daddy Root Certificate Authority - G2; Organization: GoDaddy.com, Inc.; Locality: Scottsdale; State/Province: Arizona; Country: US */ +/* link: https://crt.sh/?q=45140b3247eb9cc8c5b4f0d7b53091f73292089e6e5a63e2749dd3aca9198eda */ +const unsigned char kCertificateWithFingerprint_45140b3247eb9cc8c5b4f0d7b53091f73292089e6e5a63e2749dd3aca9198eda_certificate[969]={ +0x30,0x82,0x03,0xC5,0x30,0x82,0x02,0xAD,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x00, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30, +0x81,0x83,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31, +0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x08,0x13,0x07,0x41,0x72,0x69,0x7A,0x6F,0x6E, +0x61,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x07,0x13,0x0A,0x53,0x63,0x6F,0x74, +0x74,0x73,0x64,0x61,0x6C,0x65,0x31,0x1A,0x30,0x18,0x06,0x03,0x55,0x04,0x0A,0x13, +0x11,0x47,0x6F,0x44,0x61,0x64,0x64,0x79,0x2E,0x63,0x6F,0x6D,0x2C,0x20,0x49,0x6E, +0x63,0x2E,0x31,0x31,0x30,0x2F,0x06,0x03,0x55,0x04,0x03,0x13,0x28,0x47,0x6F,0x20, +0x44,0x61,0x64,0x64,0x79,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79, +0x20,0x2D,0x20,0x47,0x32,0x30,0x1E,0x17,0x0D,0x30,0x39,0x30,0x39,0x30,0x31,0x30, +0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x37,0x31,0x32,0x33,0x31,0x32,0x33, +0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0x83,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04, +0x06,0x13,0x02,0x55,0x53,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x08,0x13,0x07, +0x41,0x72,0x69,0x7A,0x6F,0x6E,0x61,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x07, +0x13,0x0A,0x53,0x63,0x6F,0x74,0x74,0x73,0x64,0x61,0x6C,0x65,0x31,0x1A,0x30,0x18, +0x06,0x03,0x55,0x04,0x0A,0x13,0x11,0x47,0x6F,0x44,0x61,0x64,0x64,0x79,0x2E,0x63, +0x6F,0x6D,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x31,0x30,0x2F,0x06,0x03,0x55,0x04, +0x03,0x13,0x28,0x47,0x6F,0x20,0x44,0x61,0x64,0x64,0x79,0x20,0x52,0x6F,0x6F,0x74, +0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74, +0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x2D,0x20,0x47,0x32,0x30,0x82,0x01,0x22,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82, +0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xBF,0x71,0x62,0x08, +0xF1,0xFA,0x59,0x34,0xF7,0x1B,0xC9,0x18,0xA3,0xF7,0x80,0x49,0x58,0xE9,0x22,0x83, +0x13,0xA6,0xC5,0x20,0x43,0x01,0x3B,0x84,0xF1,0xE6,0x85,0x49,0x9F,0x27,0xEA,0xF6, +0x84,0x1B,0x4E,0xA0,0xB4,0xDB,0x70,0x98,0xC7,0x32,0x01,0xB1,0x05,0x3E,0x07,0x4E, +0xEE,0xF4,0xFA,0x4F,0x2F,0x59,0x30,0x22,0xE7,0xAB,0x19,0x56,0x6B,0xE2,0x80,0x07, +0xFC,0xF3,0x16,0x75,0x80,0x39,0x51,0x7B,0xE5,0xF9,0x35,0xB6,0x74,0x4E,0xA9,0x8D, +0x82,0x13,0xE4,0xB6,0x3F,0xA9,0x03,0x83,0xFA,0xA2,0xBE,0x8A,0x15,0x6A,0x7F,0xDE, +0x0B,0xC3,0xB6,0x19,0x14,0x05,0xCA,0xEA,0xC3,0xA8,0x04,0x94,0x3B,0x46,0x7C,0x32, +0x0D,0xF3,0x00,0x66,0x22,0xC8,0x8D,0x69,0x6D,0x36,0x8C,0x11,0x18,0xB7,0xD3,0xB2, +0x1C,0x60,0xB4,0x38,0xFA,0x02,0x8C,0xCE,0xD3,0xDD,0x46,0x07,0xDE,0x0A,0x3E,0xEB, +0x5D,0x7C,0xC8,0x7C,0xFB,0xB0,0x2B,0x53,0xA4,0x92,0x62,0x69,0x51,0x25,0x05,0x61, +0x1A,0x44,0x81,0x8C,0x2C,0xA9,0x43,0x96,0x23,0xDF,0xAC,0x3A,0x81,0x9A,0x0E,0x29, +0xC5,0x1C,0xA9,0xE9,0x5D,0x1E,0xB6,0x9E,0x9E,0x30,0x0A,0x39,0xCE,0xF1,0x88,0x80, +0xFB,0x4B,0x5D,0xCC,0x32,0xEC,0x85,0x62,0x43,0x25,0x34,0x02,0x56,0x27,0x01,0x91, +0xB4,0x3B,0x70,0x2A,0x3F,0x6E,0xB1,0xE8,0x9C,0x88,0x01,0x7D,0x9F,0xD4,0xF9,0xDB, +0x53,0x6D,0x60,0x9D,0xBF,0x2C,0xE7,0x58,0xAB,0xB8,0x5F,0x46,0xFC,0xCE,0xC4,0x1B, +0x03,0x3C,0x09,0xEB,0x49,0x31,0x5C,0x69,0x46,0xB3,0xE0,0x47,0x02,0x03,0x01,0x00, +0x01,0xA3,0x42,0x30,0x40,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04, +0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04, +0x14,0x3A,0x9A,0x85,0x07,0x10,0x67,0x28,0xB6,0xEF,0xF6,0xBD,0x05,0x41,0x6E,0x20, +0xC1,0x94,0xDA,0x0F,0xDE,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, +0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x99,0xDB,0x5D,0x79,0xD5,0xF9,0x97, +0x59,0x67,0x03,0x61,0xF1,0x7E,0x3B,0x06,0x31,0x75,0x2D,0xA1,0x20,0x8E,0x4F,0x65, +0x87,0xB4,0xF7,0xA6,0x9C,0xBC,0xD8,0xE9,0x2F,0xD0,0xDB,0x5A,0xEE,0xCF,0x74,0x8C, +0x73,0xB4,0x38,0x42,0xDA,0x05,0x7B,0xF8,0x02,0x75,0xB8,0xFD,0xA5,0xB1,0xD7,0xAE, +0xF6,0xD7,0xDE,0x13,0xCB,0x53,0x10,0x7E,0x8A,0x46,0xD1,0x97,0xFA,0xB7,0x2E,0x2B, +0x11,0xAB,0x90,0xB0,0x27,0x80,0xF9,0xE8,0x9F,0x5A,0xE9,0x37,0x9F,0xAB,0xE4,0xDF, +0x6C,0xB3,0x85,0x17,0x9D,0x3D,0xD9,0x24,0x4F,0x79,0x91,0x35,0xD6,0x5F,0x04,0xEB, +0x80,0x83,0xAB,0x9A,0x02,0x2D,0xB5,0x10,0xF4,0xD8,0x90,0xC7,0x04,0x73,0x40,0xED, +0x72,0x25,0xA0,0xA9,0x9F,0xEC,0x9E,0xAB,0x68,0x12,0x99,0x57,0xC6,0x8F,0x12,0x3A, +0x09,0xA4,0xBD,0x44,0xFD,0x06,0x15,0x37,0xC1,0x9B,0xE4,0x32,0xA3,0xED,0x38,0xE8, +0xD8,0x64,0xF3,0x2C,0x7E,0x14,0xFC,0x02,0xEA,0x9F,0xCD,0xFF,0x07,0x68,0x17,0xDB, +0x22,0x90,0x38,0x2D,0x7A,0x8D,0xD1,0x54,0xF1,0x69,0xE3,0x5F,0x33,0xCA,0x7A,0x3D, +0x7B,0x0A,0xE3,0xCA,0x7F,0x5F,0x39,0xE5,0xE2,0x75,0xBA,0xC5,0x76,0x18,0x33,0xCE, +0x2C,0xF0,0x2F,0x4C,0xAD,0xF7,0xB1,0xE7,0xCE,0x4F,0xA8,0xC4,0x9B,0x4A,0x54,0x06, +0xC5,0x7F,0x7D,0xD5,0x08,0x0F,0xE2,0x1C,0xFE,0x7E,0x17,0xB8,0xAC,0x5E,0xF6,0xD4, +0x16,0xB2,0x43,0x09,0x0C,0x4D,0xF6,0xA7,0x6B,0xB4,0x99,0x84,0x65,0xCA,0x7A,0x88, +0xE2,0xE2,0x44,0xBE,0x5C,0xF7,0xEA,0x1C,0xF5, +}; + +/* subject: Common Name: Starfield Root Certificate Authority - G2; Organization: Starfield Technologies, Inc.; Locality: Scottsdale; State/Province: Arizona; Country: US */ +/* issuer: Common Name: Starfield Root Certificate Authority - G2; Organization: Starfield Technologies, Inc.; Locality: Scottsdale; State/Province: Arizona; Country: US */ +/* link: https://crt.sh/?q=2ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f5 */ +const unsigned char kCertificateWithFingerprint_2ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f5_certificate[993]={ +0x30,0x82,0x03,0xDD,0x30,0x82,0x02,0xC5,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x00, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30, +0x81,0x8F,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31, +0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x08,0x13,0x07,0x41,0x72,0x69,0x7A,0x6F,0x6E, +0x61,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x07,0x13,0x0A,0x53,0x63,0x6F,0x74, +0x74,0x73,0x64,0x61,0x6C,0x65,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0A,0x13, +0x1C,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x54,0x65,0x63,0x68,0x6E, +0x6F,0x6C,0x6F,0x67,0x69,0x65,0x73,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x32,0x30, +0x30,0x06,0x03,0x55,0x04,0x03,0x13,0x29,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C, +0x64,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61, +0x74,0x65,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x2D,0x20,0x47, +0x32,0x30,0x1E,0x17,0x0D,0x30,0x39,0x30,0x39,0x30,0x31,0x30,0x30,0x30,0x30,0x30, +0x30,0x5A,0x17,0x0D,0x33,0x37,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39, +0x5A,0x30,0x81,0x8F,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55, +0x53,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x08,0x13,0x07,0x41,0x72,0x69,0x7A, +0x6F,0x6E,0x61,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x07,0x13,0x0A,0x53,0x63, +0x6F,0x74,0x74,0x73,0x64,0x61,0x6C,0x65,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04, +0x0A,0x13,0x1C,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x54,0x65,0x63, +0x68,0x6E,0x6F,0x6C,0x6F,0x67,0x69,0x65,0x73,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31, +0x32,0x30,0x30,0x06,0x03,0x55,0x04,0x03,0x13,0x29,0x53,0x74,0x61,0x72,0x66,0x69, +0x65,0x6C,0x64,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69, +0x63,0x61,0x74,0x65,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x20,0x2D, +0x20,0x47,0x32,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02, +0x82,0x01,0x01,0x00,0xBD,0xED,0xC1,0x03,0xFC,0xF6,0x8F,0xFC,0x02,0xB1,0x6F,0x5B, +0x9F,0x48,0xD9,0x9D,0x79,0xE2,0xA2,0xB7,0x03,0x61,0x56,0x18,0xC3,0x47,0xB6,0xD7, +0xCA,0x3D,0x35,0x2E,0x89,0x43,0xF7,0xA1,0x69,0x9B,0xDE,0x8A,0x1A,0xFD,0x13,0x20, +0x9C,0xB4,0x49,0x77,0x32,0x29,0x56,0xFD,0xB9,0xEC,0x8C,0xDD,0x22,0xFA,0x72,0xDC, +0x27,0x61,0x97,0xEE,0xF6,0x5A,0x84,0xEC,0x6E,0x19,0xB9,0x89,0x2C,0xDC,0x84,0x5B, +0xD5,0x74,0xFB,0x6B,0x5F,0xC5,0x89,0xA5,0x10,0x52,0x89,0x46,0x55,0xF4,0xB8,0x75, +0x1C,0xE6,0x7F,0xE4,0x54,0xAE,0x4B,0xF8,0x55,0x72,0x57,0x02,0x19,0xF8,0x17,0x71, +0x59,0xEB,0x1E,0x28,0x07,0x74,0xC5,0x9D,0x48,0xBE,0x6C,0xB4,0xF4,0xA4,0xB0,0xF3, +0x64,0x37,0x79,0x92,0xC0,0xEC,0x46,0x5E,0x7F,0xE1,0x6D,0x53,0x4C,0x62,0xAF,0xCD, +0x1F,0x0B,0x63,0xBB,0x3A,0x9D,0xFB,0xFC,0x79,0x00,0x98,0x61,0x74,0xCF,0x26,0x82, +0x40,0x63,0xF3,0xB2,0x72,0x6A,0x19,0x0D,0x99,0xCA,0xD4,0x0E,0x75,0xCC,0x37,0xFB, +0x8B,0x89,0xC1,0x59,0xF1,0x62,0x7F,0x5F,0xB3,0x5F,0x65,0x30,0xF8,0xA7,0xB7,0x4D, +0x76,0x5A,0x1E,0x76,0x5E,0x34,0xC0,0xE8,0x96,0x56,0x99,0x8A,0xB3,0xF0,0x7F,0xA4, +0xCD,0xBD,0xDC,0x32,0x31,0x7C,0x91,0xCF,0xE0,0x5F,0x11,0xF8,0x6B,0xAA,0x49,0x5C, +0xD1,0x99,0x94,0xD1,0xA2,0xE3,0x63,0x5B,0x09,0x76,0xB5,0x56,0x62,0xE1,0x4B,0x74, +0x1D,0x96,0xD4,0x26,0xD4,0x08,0x04,0x59,0xD0,0x98,0x0E,0x0E,0xE6,0xDE,0xFC,0xC3, +0xEC,0x1F,0x90,0xF1,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x0F,0x06, +0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0E, +0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x1D, +0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x7C,0x0C,0x32,0x1F,0xA7,0xD9,0x30, +0x7F,0xC4,0x7D,0x68,0xA3,0x62,0xA8,0xA1,0xCE,0xAB,0x07,0x5B,0x27,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01, +0x00,0x11,0x59,0xFA,0x25,0x4F,0x03,0x6F,0x94,0x99,0x3B,0x9A,0x1F,0x82,0x85,0x39, +0xD4,0x76,0x05,0x94,0x5E,0xE1,0x28,0x93,0x6D,0x62,0x5D,0x09,0xC2,0xA0,0xA8,0xD4, +0xB0,0x75,0x38,0xF1,0x34,0x6A,0x9D,0xE4,0x9F,0x8A,0x86,0x26,0x51,0xE6,0x2C,0xD1, +0xC6,0x2D,0x6E,0x95,0x20,0x4A,0x92,0x01,0xEC,0xB8,0x8A,0x67,0x7B,0x31,0xE2,0x67, +0x2E,0x8C,0x95,0x03,0x26,0x2E,0x43,0x9D,0x4A,0x31,0xF6,0x0E,0xB5,0x0C,0xBB,0xB7, +0xE2,0x37,0x7F,0x22,0xBA,0x00,0xA3,0x0E,0x7B,0x52,0xFB,0x6B,0xBB,0x3B,0xC4,0xD3, +0x79,0x51,0x4E,0xCD,0x90,0xF4,0x67,0x07,0x19,0xC8,0x3C,0x46,0x7A,0x0D,0x01,0x7D, +0xC5,0x58,0xE7,0x6D,0xE6,0x85,0x30,0x17,0x9A,0x24,0xC4,0x10,0xE0,0x04,0xF7,0xE0, +0xF2,0x7F,0xD4,0xAA,0x0A,0xFF,0x42,0x1D,0x37,0xED,0x94,0xE5,0x64,0x59,0x12,0x20, +0x77,0x38,0xD3,0x32,0x3E,0x38,0x81,0x75,0x96,0x73,0xFA,0x68,0x8F,0xB1,0xCB,0xCE, +0x1F,0xC5,0xEC,0xFA,0x9C,0x7E,0xCF,0x7E,0xB1,0xF1,0x07,0x2D,0xB6,0xFC,0xBF,0xCA, +0xA4,0xBF,0xD0,0x97,0x05,0x4A,0xBC,0xEA,0x18,0x28,0x02,0x90,0xBD,0x54,0x78,0x09, +0x21,0x71,0xD3,0xD1,0x7D,0x1D,0xD9,0x16,0xB0,0xA9,0x61,0x3D,0xD0,0x0A,0x00,0x22, +0xFC,0xC7,0x7B,0xCB,0x09,0x64,0x45,0x0B,0x3B,0x40,0x81,0xF7,0x7D,0x7C,0x32,0xF5, +0x98,0xCA,0x58,0x8E,0x7D,0x2A,0xEE,0x90,0x59,0x73,0x64,0xF9,0x36,0x74,0x5E,0x25, +0xA1,0xF5,0x66,0x05,0x2E,0x7F,0x39,0x15,0xA9,0x2A,0xFB,0x50,0x8B,0x8E,0x85,0x69, +0xF4, +}; + +/* subject: Organizational Unit: Starfield Class 2 Certification Authority; Organization: Starfield Technologies, Inc.; Country: US */ +/* issuer: Organizational Unit: Starfield Class 2 Certification Authority; Organization: Starfield Technologies, Inc.; Country: US */ +/* link: https://crt.sh/?q=1465fa205397b876faa6f0a9958e5590e40fcc7faa4fb7c2c8677521fb5fb658 */ +const unsigned char kCertificateWithFingerprint_1465fa205397b876faa6f0a9958e5590e40fcc7faa4fb7c2c8677521fb5fb658_certificate[1043]={ +0x30,0x82,0x04,0x0F,0x30,0x82,0x02,0xF7,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x00, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x68,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x25, +0x30,0x23,0x06,0x03,0x55,0x04,0x0A,0x13,0x1C,0x53,0x74,0x61,0x72,0x66,0x69,0x65, +0x6C,0x64,0x20,0x54,0x65,0x63,0x68,0x6E,0x6F,0x6C,0x6F,0x67,0x69,0x65,0x73,0x2C, +0x20,0x49,0x6E,0x63,0x2E,0x31,0x32,0x30,0x30,0x06,0x03,0x55,0x04,0x0B,0x13,0x29, +0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x43,0x6C,0x61,0x73,0x73,0x20, +0x32,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20, +0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x34,0x30, +0x36,0x32,0x39,0x31,0x37,0x33,0x39,0x31,0x36,0x5A,0x17,0x0D,0x33,0x34,0x30,0x36, +0x32,0x39,0x31,0x37,0x33,0x39,0x31,0x36,0x5A,0x30,0x68,0x31,0x0B,0x30,0x09,0x06, +0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04, +0x0A,0x13,0x1C,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x54,0x65,0x63, +0x68,0x6E,0x6F,0x6C,0x6F,0x67,0x69,0x65,0x73,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31, +0x32,0x30,0x30,0x06,0x03,0x55,0x04,0x0B,0x13,0x29,0x53,0x74,0x61,0x72,0x66,0x69, +0x65,0x6C,0x64,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x43,0x65,0x72,0x74, +0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72, +0x69,0x74,0x79,0x30,0x82,0x01,0x20,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0D,0x00,0x30,0x82,0x01,0x08,0x02, +0x82,0x01,0x01,0x00,0xB7,0x32,0xC8,0xFE,0xE9,0x71,0xA6,0x04,0x85,0xAD,0x0C,0x11, +0x64,0xDF,0xCE,0x4D,0xEF,0xC8,0x03,0x18,0x87,0x3F,0xA1,0xAB,0xFB,0x3C,0xA6,0x9F, +0xF0,0xC3,0xA1,0xDA,0xD4,0xD8,0x6E,0x2B,0x53,0x90,0xFB,0x24,0xA4,0x3E,0x84,0xF0, +0x9E,0xE8,0x5F,0xEC,0xE5,0x27,0x44,0xF5,0x28,0xA6,0x3F,0x7B,0xDE,0xE0,0x2A,0xF0, +0xC8,0xAF,0x53,0x2F,0x9E,0xCA,0x05,0x01,0x93,0x1E,0x8F,0x66,0x1C,0x39,0xA7,0x4D, +0xFA,0x5A,0xB6,0x73,0x04,0x25,0x66,0xEB,0x77,0x7F,0xE7,0x59,0xC6,0x4A,0x99,0x25, +0x14,0x54,0xEB,0x26,0xC7,0xF3,0x7F,0x19,0xD5,0x30,0x70,0x8F,0xAF,0xB0,0x46,0x2A, +0xFF,0xAD,0xEB,0x29,0xED,0xD7,0x9F,0xAA,0x04,0x87,0xA3,0xD4,0xF9,0x89,0xA5,0x34, +0x5F,0xDB,0x43,0x91,0x82,0x36,0xD9,0x66,0x3C,0xB1,0xB8,0xB9,0x82,0xFD,0x9C,0x3A, +0x3E,0x10,0xC8,0x3B,0xEF,0x06,0x65,0x66,0x7A,0x9B,0x19,0x18,0x3D,0xFF,0x71,0x51, +0x3C,0x30,0x2E,0x5F,0xBE,0x3D,0x77,0x73,0xB2,0x5D,0x06,0x6C,0xC3,0x23,0x56,0x9A, +0x2B,0x85,0x26,0x92,0x1C,0xA7,0x02,0xB3,0xE4,0x3F,0x0D,0xAF,0x08,0x79,0x82,0xB8, +0x36,0x3D,0xEA,0x9C,0xD3,0x35,0xB3,0xBC,0x69,0xCA,0xF5,0xCC,0x9D,0xE8,0xFD,0x64, +0x8D,0x17,0x80,0x33,0x6E,0x5E,0x4A,0x5D,0x99,0xC9,0x1E,0x87,0xB4,0x9D,0x1A,0xC0, +0xD5,0x6E,0x13,0x35,0x23,0x5E,0xDF,0x9B,0x5F,0x3D,0xEF,0xD6,0xF7,0x76,0xC2,0xEA, +0x3E,0xBB,0x78,0x0D,0x1C,0x42,0x67,0x6B,0x04,0xD8,0xF8,0xD6,0xDA,0x6F,0x8B,0xF2, +0x44,0xA0,0x01,0xAB,0x02,0x01,0x03,0xA3,0x81,0xC5,0x30,0x81,0xC2,0x30,0x1D,0x06, +0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xBF,0x5F,0xB7,0xD1,0xCE,0xDD,0x1F,0x86, +0xF4,0x5B,0x55,0xAC,0xDC,0xD7,0x10,0xC2,0x0E,0xA9,0x88,0xE7,0x30,0x81,0x92,0x06, +0x03,0x55,0x1D,0x23,0x04,0x81,0x8A,0x30,0x81,0x87,0x80,0x14,0xBF,0x5F,0xB7,0xD1, +0xCE,0xDD,0x1F,0x86,0xF4,0x5B,0x55,0xAC,0xDC,0xD7,0x10,0xC2,0x0E,0xA9,0x88,0xE7, +0xA1,0x6C,0xA4,0x6A,0x30,0x68,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x55,0x53,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0A,0x13,0x1C,0x53,0x74, +0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x54,0x65,0x63,0x68,0x6E,0x6F,0x6C,0x6F, +0x67,0x69,0x65,0x73,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x32,0x30,0x30,0x06,0x03, +0x55,0x04,0x0B,0x13,0x29,0x53,0x74,0x61,0x72,0x66,0x69,0x65,0x6C,0x64,0x20,0x43, +0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61, +0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x82,0x01, +0x00,0x30,0x0C,0x06,0x03,0x55,0x1D,0x13,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82, +0x01,0x01,0x00,0x05,0x9D,0x3F,0x88,0x9D,0xD1,0xC9,0x1A,0x55,0xA1,0xAC,0x69,0xF3, +0xF3,0x59,0xDA,0x9B,0x01,0x87,0x1A,0x4F,0x57,0xA9,0xA1,0x79,0x09,0x2A,0xDB,0xF7, +0x2F,0xB2,0x1E,0xCC,0xC7,0x5E,0x6A,0xD8,0x83,0x87,0xA1,0x97,0xEF,0x49,0x35,0x3E, +0x77,0x06,0x41,0x58,0x62,0xBF,0x8E,0x58,0xB8,0x0A,0x67,0x3F,0xEC,0xB3,0xDD,0x21, +0x66,0x1F,0xC9,0x54,0xFA,0x72,0xCC,0x3D,0x4C,0x40,0xD8,0x81,0xAF,0x77,0x9E,0x83, +0x7A,0xBB,0xA2,0xC7,0xF5,0x34,0x17,0x8E,0xD9,0x11,0x40,0xF4,0xFC,0x2C,0x2A,0x4D, +0x15,0x7F,0xA7,0x62,0x5D,0x2E,0x25,0xD3,0x00,0x0B,0x20,0x1A,0x1D,0x68,0xF9,0x17, +0xB8,0xF4,0xBD,0x8B,0xED,0x28,0x59,0xDD,0x4D,0x16,0x8B,0x17,0x83,0xC8,0xB2,0x65, +0xC7,0x2D,0x7A,0xA5,0xAA,0xBC,0x53,0x86,0x6D,0xDD,0x57,0xA4,0xCA,0xF8,0x20,0x41, +0x0B,0x68,0xF0,0xF4,0xFB,0x74,0xBE,0x56,0x5D,0x7A,0x79,0xF5,0xF9,0x1D,0x85,0xE3, +0x2D,0x95,0xBE,0xF5,0x71,0x90,0x43,0xCC,0x8D,0x1F,0x9A,0x00,0x0A,0x87,0x29,0xE9, +0x55,0x22,0x58,0x00,0x23,0xEA,0xE3,0x12,0x43,0x29,0x5B,0x47,0x08,0xDD,0x8C,0x41, +0x6A,0x65,0x06,0xA8,0xE5,0x21,0xAA,0x41,0xB4,0x95,0x21,0x95,0xB9,0x7D,0xD1,0x34, +0xAB,0x13,0xD6,0xAD,0xBC,0xDC,0xE2,0x3D,0x39,0xCD,0xBD,0x3E,0x75,0x70,0xA1,0x18, +0x59,0x03,0xC9,0x22,0xB4,0x8F,0x9C,0xD5,0x5E,0x2A,0xD7,0xA5,0xB6,0xD4,0x0A,0x6D, +0xF8,0xB7,0x40,0x11,0x46,0x9A,0x1F,0x79,0x0E,0x62,0xBF,0x0F,0x97,0xEC,0xE0,0x2F, +0x1F,0x17,0x94, +}; + +/* subject: Organizational Unit: Go Daddy Class 2 Certification Authority; Organization: The Go Daddy Group, Inc.; Country: US */ +/* issuer: Organizational Unit: Go Daddy Class 2 Certification Authority; Organization: The Go Daddy Group, Inc.; Country: US */ +/* link: https://crt.sh/?q=c3846bf24b9e93ca64274c0ec67c1ecc5e024ffcacd2d74019350e81fe546ae4 */ +const unsigned char kCertificateWithFingerprint_c3846bf24b9e93ca64274c0ec67c1ecc5e024ffcacd2d74019350e81fe546ae4_certificate[1028]={ +0x30,0x82,0x04,0x00,0x30,0x82,0x02,0xE8,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x00, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x63,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x21, +0x30,0x1F,0x06,0x03,0x55,0x04,0x0A,0x13,0x18,0x54,0x68,0x65,0x20,0x47,0x6F,0x20, +0x44,0x61,0x64,0x64,0x79,0x20,0x47,0x72,0x6F,0x75,0x70,0x2C,0x20,0x49,0x6E,0x63, +0x2E,0x31,0x31,0x30,0x2F,0x06,0x03,0x55,0x04,0x0B,0x13,0x28,0x47,0x6F,0x20,0x44, +0x61,0x64,0x64,0x79,0x20,0x43,0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x43,0x65,0x72, +0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F, +0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x34,0x30,0x36,0x32,0x39,0x31,0x37, +0x30,0x36,0x32,0x30,0x5A,0x17,0x0D,0x33,0x34,0x30,0x36,0x32,0x39,0x31,0x37,0x30, +0x36,0x32,0x30,0x5A,0x30,0x63,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x55,0x53,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x0A,0x13,0x18,0x54,0x68, +0x65,0x20,0x47,0x6F,0x20,0x44,0x61,0x64,0x64,0x79,0x20,0x47,0x72,0x6F,0x75,0x70, +0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x31,0x30,0x2F,0x06,0x03,0x55,0x04,0x0B,0x13, +0x28,0x47,0x6F,0x20,0x44,0x61,0x64,0x64,0x79,0x20,0x43,0x6C,0x61,0x73,0x73,0x20, +0x32,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20, +0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x82,0x01,0x20,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0D, +0x00,0x30,0x82,0x01,0x08,0x02,0x82,0x01,0x01,0x00,0xDE,0x9D,0xD7,0xEA,0x57,0x18, +0x49,0xA1,0x5B,0xEB,0xD7,0x5F,0x48,0x86,0xEA,0xBE,0xDD,0xFF,0xE4,0xEF,0x67,0x1C, +0xF4,0x65,0x68,0xB3,0x57,0x71,0xA0,0x5E,0x77,0xBB,0xED,0x9B,0x49,0xE9,0x70,0x80, +0x3D,0x56,0x18,0x63,0x08,0x6F,0xDA,0xF2,0xCC,0xD0,0x3F,0x7F,0x02,0x54,0x22,0x54, +0x10,0xD8,0xB2,0x81,0xD4,0xC0,0x75,0x3D,0x4B,0x7F,0xC7,0x77,0xC3,0x3E,0x78,0xAB, +0x1A,0x03,0xB5,0x20,0x6B,0x2F,0x6A,0x2B,0xB1,0xC5,0x88,0x7E,0xC4,0xBB,0x1E,0xB0, +0xC1,0xD8,0x45,0x27,0x6F,0xAA,0x37,0x58,0xF7,0x87,0x26,0xD7,0xD8,0x2D,0xF6,0xA9, +0x17,0xB7,0x1F,0x72,0x36,0x4E,0xA6,0x17,0x3F,0x65,0x98,0x92,0xDB,0x2A,0x6E,0x5D, +0xA2,0xFE,0x88,0xE0,0x0B,0xDE,0x7F,0xE5,0x8D,0x15,0xE1,0xEB,0xCB,0x3A,0xD5,0xE2, +0x12,0xA2,0x13,0x2D,0xD8,0x8E,0xAF,0x5F,0x12,0x3D,0xA0,0x08,0x05,0x08,0xB6,0x5C, +0xA5,0x65,0x38,0x04,0x45,0x99,0x1E,0xA3,0x60,0x60,0x74,0xC5,0x41,0xA5,0x72,0x62, +0x1B,0x62,0xC5,0x1F,0x6F,0x5F,0x1A,0x42,0xBE,0x02,0x51,0x65,0xA8,0xAE,0x23,0x18, +0x6A,0xFC,0x78,0x03,0xA9,0x4D,0x7F,0x80,0xC3,0xFA,0xAB,0x5A,0xFC,0xA1,0x40,0xA4, +0xCA,0x19,0x16,0xFE,0xB2,0xC8,0xEF,0x5E,0x73,0x0D,0xEE,0x77,0xBD,0x9A,0xF6,0x79, +0x98,0xBC,0xB1,0x07,0x67,0xA2,0x15,0x0D,0xDD,0xA0,0x58,0xC6,0x44,0x7B,0x0A,0x3E, +0x62,0x28,0x5F,0xBA,0x41,0x07,0x53,0x58,0xCF,0x11,0x7E,0x38,0x74,0xC5,0xF8,0xFF, +0xB5,0x69,0x90,0x8F,0x84,0x74,0xEA,0x97,0x1B,0xAF,0x02,0x01,0x03,0xA3,0x81,0xC0, +0x30,0x81,0xBD,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xD2,0xC4, +0xB0,0xD2,0x91,0xD4,0x4C,0x11,0x71,0xB3,0x61,0xCB,0x3D,0xA1,0xFE,0xDD,0xA8,0x6A, +0xD4,0xE3,0x30,0x81,0x8D,0x06,0x03,0x55,0x1D,0x23,0x04,0x81,0x85,0x30,0x81,0x82, +0x80,0x14,0xD2,0xC4,0xB0,0xD2,0x91,0xD4,0x4C,0x11,0x71,0xB3,0x61,0xCB,0x3D,0xA1, +0xFE,0xDD,0xA8,0x6A,0xD4,0xE3,0xA1,0x67,0xA4,0x65,0x30,0x63,0x31,0x0B,0x30,0x09, +0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x21,0x30,0x1F,0x06,0x03,0x55, +0x04,0x0A,0x13,0x18,0x54,0x68,0x65,0x20,0x47,0x6F,0x20,0x44,0x61,0x64,0x64,0x79, +0x20,0x47,0x72,0x6F,0x75,0x70,0x2C,0x20,0x49,0x6E,0x63,0x2E,0x31,0x31,0x30,0x2F, +0x06,0x03,0x55,0x04,0x0B,0x13,0x28,0x47,0x6F,0x20,0x44,0x61,0x64,0x64,0x79,0x20, +0x43,0x6C,0x61,0x73,0x73,0x20,0x32,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63, +0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x82, +0x01,0x00,0x30,0x0C,0x06,0x03,0x55,0x1D,0x13,0x04,0x05,0x30,0x03,0x01,0x01,0xFF, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03, +0x82,0x01,0x01,0x00,0x32,0x4B,0xF3,0xB2,0xCA,0x3E,0x91,0xFC,0x12,0xC6,0xA1,0x07, +0x8C,0x8E,0x77,0xA0,0x33,0x06,0x14,0x5C,0x90,0x1E,0x18,0xF7,0x08,0xA6,0x3D,0x0A, +0x19,0xF9,0x87,0x80,0x11,0x6E,0x69,0xE4,0x96,0x17,0x30,0xFF,0x34,0x91,0x63,0x72, +0x38,0xEE,0xCC,0x1C,0x01,0xA3,0x1D,0x94,0x28,0xA4,0x31,0xF6,0x7A,0xC4,0x54,0xD7, +0xF6,0xE5,0x31,0x58,0x03,0xA2,0xCC,0xCE,0x62,0xDB,0x94,0x45,0x73,0xB5,0xBF,0x45, +0xC9,0x24,0xB5,0xD5,0x82,0x02,0xAD,0x23,0x79,0x69,0x8D,0xB8,0xB6,0x4D,0xCE,0xCF, +0x4C,0xCA,0x33,0x23,0xE8,0x1C,0x88,0xAA,0x9D,0x8B,0x41,0x6E,0x16,0xC9,0x20,0xE5, +0x89,0x9E,0xCD,0x3B,0xDA,0x70,0xF7,0x7E,0x99,0x26,0x20,0x14,0x54,0x25,0xAB,0x6E, +0x73,0x85,0xE6,0x9B,0x21,0x9D,0x0A,0x6C,0x82,0x0E,0xA8,0xF8,0xC2,0x0C,0xFA,0x10, +0x1E,0x6C,0x96,0xEF,0x87,0x0D,0xC4,0x0F,0x61,0x8B,0xAD,0xEE,0x83,0x2B,0x95,0xF8, +0x8E,0x92,0x84,0x72,0x39,0xEB,0x20,0xEA,0x83,0xED,0x83,0xCD,0x97,0x6E,0x08,0xBC, +0xEB,0x4E,0x26,0xB6,0x73,0x2B,0xE4,0xD3,0xF6,0x4C,0xFE,0x26,0x71,0xE2,0x61,0x11, +0x74,0x4A,0xFF,0x57,0x1A,0x87,0x0F,0x75,0x48,0x2E,0xCF,0x51,0x69,0x17,0xA0,0x02, +0x12,0x61,0x95,0xD5,0xD1,0x40,0xB2,0x10,0x4C,0xEE,0xC4,0xAC,0x10,0x43,0xA6,0xA5, +0x9E,0x0A,0xD5,0x95,0x62,0x9A,0x0D,0xCF,0x88,0x82,0xC5,0x32,0x0C,0xE4,0x2B,0x9F, +0x45,0xE6,0x0D,0x9F,0x28,0x9C,0xB1,0xB9,0x2A,0x5A,0x57,0xAD,0x37,0x0F,0xAF,0x1D, +0x7F,0xDB,0xBD,0x9F, +}; + +/* subject: Common Name: AAA Certificate Services, Organization: Comodo CA Limited, Locality: Salford, State/Province: Greater Manchester, Country: GB */ +/* issuer: Common Name: AAA Certificate Services, Organization: Comodo CA Limited, Locality: Salford, State/Province: Greater Manchester, Country: GB */ +/* link: https://crt.sh/?q=d7a7a0fb5d7e2731d771e9484ebcdef71d5f0c3e0a2948782bc83ee0ea699ef4 */ +const unsigned char kCertificateWithFingerprint_d7a7a0fb5d7e2731d771e9484ebcdef71d5f0c3e0a2948782bc83ee0ea699ef4_certificate[1078]={ +0x30,0x82,0x04,0x32,0x30,0x82,0x03,0x1A,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30, +0x7B,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B, +0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x0C,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72, +0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06, +0x03,0x55,0x04,0x07,0x0C,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30, +0x18,0x06,0x03,0x55,0x04,0x0A,0x0C,0x11,0x43,0x6F,0x6D,0x6F,0x64,0x6F,0x20,0x43, +0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x21,0x30,0x1F,0x06,0x03,0x55, +0x04,0x03,0x0C,0x18,0x41,0x41,0x41,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63, +0x61,0x74,0x65,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x30,0x1E,0x17,0x0D, +0x30,0x34,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32, +0x38,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x7B,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B,0x30,0x19,0x06, +0x03,0x55,0x04,0x08,0x0C,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72,0x20,0x4D,0x61, +0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04, +0x07,0x0C,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30,0x18,0x06,0x03, +0x55,0x04,0x0A,0x0C,0x11,0x43,0x6F,0x6D,0x6F,0x64,0x6F,0x20,0x43,0x41,0x20,0x4C, +0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x21,0x30,0x1F,0x06,0x03,0x55,0x04,0x03,0x0C, +0x18,0x41,0x41,0x41,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65, +0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x30,0x82,0x01,0x22,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F, +0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xBE,0x40,0x9D,0xF4,0x6E,0xE1, +0xEA,0x76,0x87,0x1C,0x4D,0x45,0x44,0x8E,0xBE,0x46,0xC8,0x83,0x06,0x9D,0xC1,0x2A, +0xFE,0x18,0x1F,0x8E,0xE4,0x02,0xFA,0xF3,0xAB,0x5D,0x50,0x8A,0x16,0x31,0x0B,0x9A, +0x06,0xD0,0xC5,0x70,0x22,0xCD,0x49,0x2D,0x54,0x63,0xCC,0xB6,0x6E,0x68,0x46,0x0B, +0x53,0xEA,0xCB,0x4C,0x24,0xC0,0xBC,0x72,0x4E,0xEA,0xF1,0x15,0xAE,0xF4,0x54,0x9A, +0x12,0x0A,0xC3,0x7A,0xB2,0x33,0x60,0xE2,0xDA,0x89,0x55,0xF3,0x22,0x58,0xF3,0xDE, +0xDC,0xCF,0xEF,0x83,0x86,0xA2,0x8C,0x94,0x4F,0x9F,0x68,0xF2,0x98,0x90,0x46,0x84, +0x27,0xC7,0x76,0xBF,0xE3,0xCC,0x35,0x2C,0x8B,0x5E,0x07,0x64,0x65,0x82,0xC0,0x48, +0xB0,0xA8,0x91,0xF9,0x61,0x9F,0x76,0x20,0x50,0xA8,0x91,0xC7,0x66,0xB5,0xEB,0x78, +0x62,0x03,0x56,0xF0,0x8A,0x1A,0x13,0xEA,0x31,0xA3,0x1E,0xA0,0x99,0xFD,0x38,0xF6, +0xF6,0x27,0x32,0x58,0x6F,0x07,0xF5,0x6B,0xB8,0xFB,0x14,0x2B,0xAF,0xB7,0xAA,0xCC, +0xD6,0x63,0x5F,0x73,0x8C,0xDA,0x05,0x99,0xA8,0x38,0xA8,0xCB,0x17,0x78,0x36,0x51, +0xAC,0xE9,0x9E,0xF4,0x78,0x3A,0x8D,0xCF,0x0F,0xD9,0x42,0xE2,0x98,0x0C,0xAB,0x2F, +0x9F,0x0E,0x01,0xDE,0xEF,0x9F,0x99,0x49,0xF1,0x2D,0xDF,0xAC,0x74,0x4D,0x1B,0x98, +0xB5,0x47,0xC5,0xE5,0x29,0xD1,0xF9,0x90,0x18,0xC7,0x62,0x9C,0xBE,0x83,0xC7,0x26, +0x7B,0x3E,0x8A,0x25,0xC7,0xC0,0xDD,0x9D,0xE6,0x35,0x68,0x10,0x20,0x9D,0x8F,0xD8, +0xDE,0xD2,0xC3,0x84,0x9C,0x0D,0x5E,0xE8,0x2F,0xC9,0x02,0x03,0x01,0x00,0x01,0xA3, +0x81,0xC0,0x30,0x81,0xBD,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14, +0xA0,0x11,0x0A,0x23,0x3E,0x96,0xF1,0x07,0xEC,0xE2,0xAF,0x29,0xEF,0x82,0xA5,0x7F, +0xD0,0x30,0xA4,0xB4,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04, +0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05, +0x30,0x03,0x01,0x01,0xFF,0x30,0x7B,0x06,0x03,0x55,0x1D,0x1F,0x04,0x74,0x30,0x72, +0x30,0x38,0xA0,0x36,0xA0,0x34,0x86,0x32,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63, +0x72,0x6C,0x2E,0x63,0x6F,0x6D,0x6F,0x64,0x6F,0x63,0x61,0x2E,0x63,0x6F,0x6D,0x2F, +0x41,0x41,0x41,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x65,0x53,0x65, +0x72,0x76,0x69,0x63,0x65,0x73,0x2E,0x63,0x72,0x6C,0x30,0x36,0xA0,0x34,0xA0,0x32, +0x86,0x30,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x2E,0x63,0x6F,0x6D, +0x6F,0x64,0x6F,0x2E,0x6E,0x65,0x74,0x2F,0x41,0x41,0x41,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x65,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x2E,0x63, +0x72,0x6C,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05, +0x00,0x03,0x82,0x01,0x01,0x00,0x08,0x56,0xFC,0x02,0xF0,0x9B,0xE8,0xFF,0xA4,0xFA, +0xD6,0x7B,0xC6,0x44,0x80,0xCE,0x4F,0xC4,0xC5,0xF6,0x00,0x58,0xCC,0xA6,0xB6,0xBC, +0x14,0x49,0x68,0x04,0x76,0xE8,0xE6,0xEE,0x5D,0xEC,0x02,0x0F,0x60,0xD6,0x8D,0x50, +0x18,0x4F,0x26,0x4E,0x01,0xE3,0xE6,0xB0,0xA5,0xEE,0xBF,0xBC,0x74,0x54,0x41,0xBF, +0xFD,0xFC,0x12,0xB8,0xC7,0x4F,0x5A,0xF4,0x89,0x60,0x05,0x7F,0x60,0xB7,0x05,0x4A, +0xF3,0xF6,0xF1,0xC2,0xBF,0xC4,0xB9,0x74,0x86,0xB6,0x2D,0x7D,0x6B,0xCC,0xD2,0xF3, +0x46,0xDD,0x2F,0xC6,0xE0,0x6A,0xC3,0xC3,0x34,0x03,0x2C,0x7D,0x96,0xDD,0x5A,0xC2, +0x0E,0xA7,0x0A,0x99,0xC1,0x05,0x8B,0xAB,0x0C,0x2F,0xF3,0x5C,0x3A,0xCF,0x6C,0x37, +0x55,0x09,0x87,0xDE,0x53,0x40,0x6C,0x58,0xEF,0xFC,0xB6,0xAB,0x65,0x6E,0x04,0xF6, +0x1B,0xDC,0x3C,0xE0,0x5A,0x15,0xC6,0x9E,0xD9,0xF1,0x59,0x48,0x30,0x21,0x65,0x03, +0x6C,0xEC,0xE9,0x21,0x73,0xEC,0x9B,0x03,0xA1,0xE0,0x37,0xAD,0xA0,0x15,0x18,0x8F, +0xFA,0xBA,0x02,0xCE,0xA7,0x2C,0xA9,0x10,0x13,0x2C,0xD4,0xE5,0x08,0x26,0xAB,0x22, +0x97,0x60,0xF8,0x90,0x5E,0x74,0xD4,0xA2,0x9A,0x53,0xBD,0xF2,0xA9,0x68,0xE0,0xA2, +0x6E,0xC2,0xD7,0x6C,0xB1,0xA3,0x0F,0x9E,0xBF,0xEB,0x68,0xE7,0x56,0xF2,0xAE,0xF2, +0xE3,0x2B,0x38,0x3A,0x09,0x81,0xB5,0x6B,0x85,0xD7,0xBE,0x2D,0xED,0x3F,0x1A,0xB7, +0xB2,0x63,0xE2,0xF5,0x62,0x2C,0x82,0xD4,0x6A,0x00,0x41,0x50,0xF1,0x39,0x83,0x9F, +0x95,0xE9,0x36,0x96,0x98,0x6E, +}; + +/* subject: Common Name: COMODO Certification Authority, Organization: COMODO CA Limited, Locality: Salford, State/Province: Greater Manchester, Country: GB */ +/* issuer: Common Name: COMODO Certification Authority, Organization: COMODO CA Limited, Locality: Salford, State/Province: Greater Manchester, Country: GB */ +/* link: https://crt.sh/?q=0c2cd63df7806fa399ede809116b575bf87989f06518f9808c860503178baf66 */ +const unsigned char kCertificateWithFingerprint_0c2cd63df7806fa399ede809116b575bf87989f06518f9808c860503178baf66_certificate[1057]={ +0x30,0x82,0x04,0x1D,0x30,0x82,0x03,0x05,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x4E, +0x81,0x2D,0x8A,0x82,0x65,0xE0,0x0B,0x02,0xEE,0x3E,0x35,0x02,0x46,0xE5,0x3D,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x30,0x81, +0x81,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B, +0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x13,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72, +0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06, +0x03,0x55,0x04,0x07,0x13,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30, +0x18,0x06,0x03,0x55,0x04,0x0A,0x13,0x11,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x43, +0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x27,0x30,0x25,0x06,0x03,0x55, +0x04,0x03,0x13,0x1E,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x43,0x65,0x72,0x74,0x69, +0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69, +0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x36,0x31,0x32,0x30,0x31,0x30,0x30,0x30,0x30, +0x30,0x30,0x5A,0x17,0x0D,0x32,0x39,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35, +0x39,0x5A,0x30,0x81,0x81,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02, +0x47,0x42,0x31,0x1B,0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x13,0x12,0x47,0x72,0x65, +0x61,0x74,0x65,0x72,0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31, +0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x07,0x13,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72, +0x64,0x31,0x1A,0x30,0x18,0x06,0x03,0x55,0x04,0x0A,0x13,0x11,0x43,0x4F,0x4D,0x4F, +0x44,0x4F,0x20,0x43,0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x27,0x30, +0x25,0x06,0x03,0x55,0x04,0x03,0x13,0x1E,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x43, +0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74, +0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82, +0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xD0,0x40,0x8B,0x8B,0x72,0xE3,0x91,0x1B,0xF7, +0x51,0xC1,0x1B,0x54,0x04,0x98,0xD3,0xA9,0xBF,0xC1,0xE6,0x8A,0x5D,0x3B,0x87,0xFB, +0xBB,0x88,0xCE,0x0D,0xE3,0x2F,0x3F,0x06,0x96,0xF0,0xA2,0x29,0x50,0x99,0xAE,0xDB, +0x3B,0xA1,0x57,0xB0,0x74,0x51,0x71,0xCD,0xED,0x42,0x91,0x4D,0x41,0xFE,0xA9,0xC8, +0xD8,0x6A,0x86,0x77,0x44,0xBB,0x59,0x66,0x97,0x50,0x5E,0xB4,0xD4,0x2C,0x70,0x44, +0xCF,0xDA,0x37,0x95,0x42,0x69,0x3C,0x30,0xC4,0x71,0xB3,0x52,0xF0,0x21,0x4D,0xA1, +0xD8,0xBA,0x39,0x7C,0x1C,0x9E,0xA3,0x24,0x9D,0xF2,0x83,0x16,0x98,0xAA,0x16,0x7C, +0x43,0x9B,0x15,0x5B,0xB7,0xAE,0x34,0x91,0xFE,0xD4,0x62,0x26,0x18,0x46,0x9A,0x3F, +0xEB,0xC1,0xF9,0xF1,0x90,0x57,0xEB,0xAC,0x7A,0x0D,0x8B,0xDB,0x72,0x30,0x6A,0x66, +0xD5,0xE0,0x46,0xA3,0x70,0xDC,0x68,0xD9,0xFF,0x04,0x48,0x89,0x77,0xDE,0xB5,0xE9, +0xFB,0x67,0x6D,0x41,0xE9,0xBC,0x39,0xBD,0x32,0xD9,0x62,0x02,0xF1,0xB1,0xA8,0x3D, +0x6E,0x37,0x9C,0xE2,0x2F,0xE2,0xD3,0xA2,0x26,0x8B,0xC6,0xB8,0x55,0x43,0x88,0xE1, +0x23,0x3E,0xA5,0xD2,0x24,0x39,0x6A,0x47,0xAB,0x00,0xD4,0xA1,0xB3,0xA9,0x25,0xFE, +0x0D,0x3F,0xA7,0x1D,0xBA,0xD3,0x51,0xC1,0x0B,0xA4,0xDA,0xAC,0x38,0xEF,0x55,0x50, +0x24,0x05,0x65,0x46,0x93,0x34,0x4F,0x2D,0x8D,0xAD,0xC6,0xD4,0x21,0x19,0xD2,0x8E, +0xCA,0x05,0x61,0x71,0x07,0x73,0x47,0xE5,0x8A,0x19,0x12,0xBD,0x04,0x4D,0xCE,0x4E, +0x9C,0xA5,0x48,0xAC,0xBB,0x26,0xF7,0x02,0x03,0x01,0x00,0x01,0xA3,0x81,0x8E,0x30, +0x81,0x8B,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x0B,0x58,0xE5, +0x8B,0xC6,0x4C,0x15,0x37,0xA4,0x40,0xA9,0x30,0xA9,0x21,0xBE,0x47,0x36,0x5A,0x56, +0xFF,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01, +0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01, +0x01,0xFF,0x30,0x49,0x06,0x03,0x55,0x1D,0x1F,0x04,0x42,0x30,0x40,0x30,0x3E,0xA0, +0x3C,0xA0,0x3A,0x86,0x38,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x2E, +0x63,0x6F,0x6D,0x6F,0x64,0x6F,0x63,0x61,0x2E,0x63,0x6F,0x6D,0x2F,0x43,0x4F,0x4D, +0x4F,0x44,0x4F,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E, +0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x2E,0x63,0x72,0x6C,0x30,0x0D,0x06, +0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01, +0x00,0x3E,0x98,0x9E,0x9B,0xF6,0x1B,0xE9,0xD7,0x39,0xB7,0x78,0xAE,0x1D,0x72,0x18, +0x49,0xD3,0x87,0xE4,0x43,0x82,0xEB,0x3F,0xC9,0xAA,0xF5,0xA8,0xB5,0xEF,0x55,0x7C, +0x21,0x52,0x65,0xF9,0xD5,0x0D,0xE1,0x6C,0xF4,0x3E,0x8C,0x93,0x73,0x91,0x2E,0x02, +0xC4,0x4E,0x07,0x71,0x6F,0xC0,0x8F,0x38,0x61,0x08,0xA8,0x1E,0x81,0x0A,0xC0,0x2F, +0x20,0x2F,0x41,0x8B,0x91,0xDC,0x48,0x45,0xBC,0xF1,0xC6,0xDE,0xBA,0x76,0x6B,0x33, +0xC8,0x00,0x2D,0x31,0x46,0x4C,0xED,0xE7,0x9D,0xCF,0x88,0x94,0xFF,0x33,0xC0,0x56, +0xE8,0x24,0x86,0x26,0xB8,0xD8,0x38,0x38,0xDF,0x2A,0x6B,0xDD,0x12,0xCC,0xC7,0x3F, +0x47,0x17,0x4C,0xA2,0xC2,0x06,0x96,0x09,0xD6,0xDB,0xFE,0x3F,0x3C,0x46,0x41,0xDF, +0x58,0xE2,0x56,0x0F,0x3C,0x3B,0xC1,0x1C,0x93,0x35,0xD9,0x38,0x52,0xAC,0xEE,0xC8, +0xEC,0x2E,0x30,0x4E,0x94,0x35,0xB4,0x24,0x1F,0x4B,0x78,0x69,0xDA,0xF2,0x02,0x38, +0xCC,0x95,0x52,0x93,0xF0,0x70,0x25,0x59,0x9C,0x20,0x67,0xC4,0xEE,0xF9,0x8B,0x57, +0x61,0xF4,0x92,0x76,0x7D,0x3F,0x84,0x8D,0x55,0xB7,0xE8,0xE5,0xAC,0xD5,0xF1,0xF5, +0x19,0x56,0xA6,0x5A,0xFB,0x90,0x1C,0xAF,0x93,0xEB,0xE5,0x1C,0xD4,0x67,0x97,0x5D, +0x04,0x0E,0xBE,0x0B,0x83,0xA6,0x17,0x83,0xB9,0x30,0x12,0xA0,0xC5,0x33,0x15,0x05, +0xB9,0x0D,0xFB,0xC7,0x05,0x76,0xE3,0xD8,0x4A,0x8D,0xFC,0x34,0x17,0xA3,0xC6,0x21, +0x28,0xBE,0x30,0x45,0x31,0x1E,0xC7,0x78,0xBE,0x58,0x61,0x38,0xAC,0x3B,0xE2,0x01, +0x65, +}; + +/* subject: Common Name: COMODO ECC Certification Authority, Organization: COMODO CA Limited, Locality: Salford, State/Province: Greater Manchester, Country: GB */ +/* issuer: Common Name: COMODO ECC Certification Authority, Organization: COMODO CA Limited, Locality: Salford, State/Province: Greater Manchester, Country: GB */ +/* link: https://crt.sh/?q=1793927a0614549789adce2f8f34f7f0b66d0f3ae3a3b84d21ec15dbba4fadc7 */ +const unsigned char kCertificateWithFingerprint_1793927a0614549789adce2f8f34f7f0b66d0f3ae3a3b84d21ec15dbba4fadc7_certificate[653]={ +0x30,0x82,0x02,0x89,0x30,0x82,0x02,0x0F,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x1F, +0x47,0xAF,0xAA,0x62,0x00,0x70,0x50,0x54,0x4C,0x01,0x9E,0x9B,0x63,0x99,0x2A,0x30, +0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x81,0x85,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B,0x30,0x19,0x06, +0x03,0x55,0x04,0x08,0x13,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72,0x20,0x4D,0x61, +0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04, +0x07,0x13,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30,0x18,0x06,0x03, +0x55,0x04,0x0A,0x13,0x11,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x43,0x41,0x20,0x4C, +0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x2B,0x30,0x29,0x06,0x03,0x55,0x04,0x03,0x13, +0x22,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x45,0x43,0x43,0x20,0x43,0x65,0x72,0x74, +0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72, +0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x30,0x38,0x30,0x33,0x30,0x36,0x30,0x30,0x30, +0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31,0x31,0x38,0x32,0x33,0x35,0x39, +0x35,0x39,0x5A,0x30,0x81,0x85,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x47,0x42,0x31,0x1B,0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x13,0x12,0x47,0x72, +0x65,0x61,0x74,0x65,0x72,0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72, +0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x07,0x13,0x07,0x53,0x61,0x6C,0x66,0x6F, +0x72,0x64,0x31,0x1A,0x30,0x18,0x06,0x03,0x55,0x04,0x0A,0x13,0x11,0x43,0x4F,0x4D, +0x4F,0x44,0x4F,0x20,0x43,0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x2B, +0x30,0x29,0x06,0x03,0x55,0x04,0x03,0x13,0x22,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20, +0x45,0x43,0x43,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F, +0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x76,0x30,0x10,0x06, +0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05,0x2B,0x81,0x04,0x00,0x22,0x03, +0x62,0x00,0x04,0x03,0x47,0x7B,0x2F,0x75,0xC9,0x82,0x15,0x85,0xFB,0x75,0xE4,0x91, +0x16,0xD4,0xAB,0x62,0x99,0xF5,0x3E,0x52,0x0B,0x06,0xCE,0x41,0x00,0x7F,0x97,0xE1, +0x0A,0x24,0x3C,0x1D,0x01,0x04,0xEE,0x3D,0xD2,0x8D,0x09,0x97,0x0C,0xE0,0x75,0xE4, +0xFA,0xFB,0x77,0x8A,0x2A,0xF5,0x03,0x60,0x4B,0x36,0x8B,0x16,0x23,0x16,0xAD,0x09, +0x71,0xF4,0x4A,0xF4,0x28,0x50,0xB4,0xFE,0x88,0x1C,0x6E,0x3F,0x6C,0x2F,0x2F,0x09, +0x59,0x5B,0xA5,0x5B,0x0B,0x33,0x99,0xE2,0xC3,0x3D,0x89,0xF9,0x6A,0x2C,0xEF,0xB2, +0xD3,0x06,0xE9,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16, +0x04,0x14,0x75,0x71,0xA7,0x19,0x48,0x19,0xBC,0x9D,0x9D,0xEA,0x41,0x47,0xDF,0x94, +0xC4,0x48,0x77,0x99,0xD3,0x79,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF, +0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D, +0x04,0x03,0x03,0x03,0x68,0x00,0x30,0x65,0x02,0x31,0x00,0xEF,0x03,0x5B,0x7A,0xAC, +0xB7,0x78,0x0A,0x72,0xB7,0x88,0xDF,0xFF,0xB5,0x46,0x14,0x09,0x0A,0xFA,0xA0,0xE6, +0x7D,0x08,0xC6,0x1A,0x87,0xBD,0x18,0xA8,0x73,0xBD,0x26,0xCA,0x60,0x0C,0x9D,0xCE, +0x99,0x9F,0xCF,0x5C,0x0F,0x30,0xE1,0xBE,0x14,0x31,0xEA,0x02,0x30,0x14,0xF4,0x93, +0x3C,0x49,0xA7,0x33,0x7A,0x90,0x46,0x47,0xB3,0x63,0x7D,0x13,0x9B,0x4E,0xB7,0x6F, +0x18,0x37,0x80,0x53,0xFE,0xDD,0x20,0xE0,0x35,0x9A,0x36,0xD1,0xC7,0x01,0xB9,0xE6, +0xDC,0xDD,0xF3,0xFF,0x1D,0x2C,0x3A,0x16,0x57,0xD9,0x92,0x39,0xD6, +}; + +/* subject: Common Name: COMODO RSA Certification Authority, Organization: COMODO CA Limited, Locality: Salford, State/Province: Greater Manchester, Country: GB */ +/* issuer: Common Name: COMODO RSA Certification Authority, Organization: COMODO CA Limited, Locality: Salford, State/Province: Greater Manchester, Country: GB */ +/* link: https://crt.sh/?q=52f0e1c4e58ec629291b60317f074671b85d7ea80d5b07273463534b32b40234 */ +const unsigned char kCertificateWithFingerprint_52f0e1c4e58ec629291b60317f074671b85d7ea80d5b07273463534b32b40234_certificate[1500]={ +0x30,0x82,0x05,0xD8,0x30,0x82,0x03,0xC0,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x4C, +0xAA,0xF9,0xCA,0xDB,0x63,0x6F,0xE0,0x1F,0xF7,0x4E,0xD8,0x5B,0x03,0x86,0x9D,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C,0x05,0x00,0x30,0x81, +0x85,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B, +0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x13,0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72, +0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73,0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06, +0x03,0x55,0x04,0x07,0x13,0x07,0x53,0x61,0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30, +0x18,0x06,0x03,0x55,0x04,0x0A,0x13,0x11,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x43, +0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65,0x64,0x31,0x2B,0x30,0x29,0x06,0x03,0x55, +0x04,0x03,0x13,0x22,0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x52,0x53,0x41,0x20,0x43, +0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74, +0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x31,0x30,0x30,0x31,0x31,0x39, +0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31,0x31,0x38,0x32, +0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0x85,0x31,0x0B,0x30,0x09,0x06,0x03,0x55, +0x04,0x06,0x13,0x02,0x47,0x42,0x31,0x1B,0x30,0x19,0x06,0x03,0x55,0x04,0x08,0x13, +0x12,0x47,0x72,0x65,0x61,0x74,0x65,0x72,0x20,0x4D,0x61,0x6E,0x63,0x68,0x65,0x73, +0x74,0x65,0x72,0x31,0x10,0x30,0x0E,0x06,0x03,0x55,0x04,0x07,0x13,0x07,0x53,0x61, +0x6C,0x66,0x6F,0x72,0x64,0x31,0x1A,0x30,0x18,0x06,0x03,0x55,0x04,0x0A,0x13,0x11, +0x43,0x4F,0x4D,0x4F,0x44,0x4F,0x20,0x43,0x41,0x20,0x4C,0x69,0x6D,0x69,0x74,0x65, +0x64,0x31,0x2B,0x30,0x29,0x06,0x03,0x55,0x04,0x03,0x13,0x22,0x43,0x4F,0x4D,0x4F, +0x44,0x4F,0x20,0x52,0x53,0x41,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61, +0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x82, +0x02,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05, +0x00,0x03,0x82,0x02,0x0F,0x00,0x30,0x82,0x02,0x0A,0x02,0x82,0x02,0x01,0x00,0x91, +0xE8,0x54,0x92,0xD2,0x0A,0x56,0xB1,0xAC,0x0D,0x24,0xDD,0xC5,0xCF,0x44,0x67,0x74, +0x99,0x2B,0x37,0xA3,0x7D,0x23,0x70,0x00,0x71,0xBC,0x53,0xDF,0xC4,0xFA,0x2A,0x12, +0x8F,0x4B,0x7F,0x10,0x56,0xBD,0x9F,0x70,0x72,0xB7,0x61,0x7F,0xC9,0x4B,0x0F,0x17, +0xA7,0x3D,0xE3,0xB0,0x04,0x61,0xEE,0xFF,0x11,0x97,0xC7,0xF4,0x86,0x3E,0x0A,0xFA, +0x3E,0x5C,0xF9,0x93,0xE6,0x34,0x7A,0xD9,0x14,0x6B,0xE7,0x9C,0xB3,0x85,0xA0,0x82, +0x7A,0x76,0xAF,0x71,0x90,0xD7,0xEC,0xFD,0x0D,0xFA,0x9C,0x6C,0xFA,0xDF,0xB0,0x82, +0xF4,0x14,0x7E,0xF9,0xBE,0xC4,0xA6,0x2F,0x4F,0x7F,0x99,0x7F,0xB5,0xFC,0x67,0x43, +0x72,0xBD,0x0C,0x00,0xD6,0x89,0xEB,0x6B,0x2C,0xD3,0xED,0x8F,0x98,0x1C,0x14,0xAB, +0x7E,0xE5,0xE3,0x6E,0xFC,0xD8,0xA8,0xE4,0x92,0x24,0xDA,0x43,0x6B,0x62,0xB8,0x55, +0xFD,0xEA,0xC1,0xBC,0x6C,0xB6,0x8B,0xF3,0x0E,0x8D,0x9A,0xE4,0x9B,0x6C,0x69,0x99, +0xF8,0x78,0x48,0x30,0x45,0xD5,0xAD,0xE1,0x0D,0x3C,0x45,0x60,0xFC,0x32,0x96,0x51, +0x27,0xBC,0x67,0xC3,0xCA,0x2E,0xB6,0x6B,0xEA,0x46,0xC7,0xC7,0x20,0xA0,0xB1,0x1F, +0x65,0xDE,0x48,0x08,0xBA,0xA4,0x4E,0xA9,0xF2,0x83,0x46,0x37,0x84,0xEB,0xE8,0xCC, +0x81,0x48,0x43,0x67,0x4E,0x72,0x2A,0x9B,0x5C,0xBD,0x4C,0x1B,0x28,0x8A,0x5C,0x22, +0x7B,0xB4,0xAB,0x98,0xD9,0xEE,0xE0,0x51,0x83,0xC3,0x09,0x46,0x4E,0x6D,0x3E,0x99, +0xFA,0x95,0x17,0xDA,0x7C,0x33,0x57,0x41,0x3C,0x8D,0x51,0xED,0x0B,0xB6,0x5C,0xAF, +0x2C,0x63,0x1A,0xDF,0x57,0xC8,0x3F,0xBC,0xE9,0x5D,0xC4,0x9B,0xAF,0x45,0x99,0xE2, +0xA3,0x5A,0x24,0xB4,0xBA,0xA9,0x56,0x3D,0xCF,0x6F,0xAA,0xFF,0x49,0x58,0xBE,0xF0, +0xA8,0xFF,0xF4,0xB8,0xAD,0xE9,0x37,0xFB,0xBA,0xB8,0xF4,0x0B,0x3A,0xF9,0xE8,0x43, +0x42,0x1E,0x89,0xD8,0x84,0xCB,0x13,0xF1,0xD9,0xBB,0xE1,0x89,0x60,0xB8,0x8C,0x28, +0x56,0xAC,0x14,0x1D,0x9C,0x0A,0xE7,0x71,0xEB,0xCF,0x0E,0xDD,0x3D,0xA9,0x96,0xA1, +0x48,0xBD,0x3C,0xF7,0xAF,0xB5,0x0D,0x22,0x4C,0xC0,0x11,0x81,0xEC,0x56,0x3B,0xF6, +0xD3,0xA2,0xE2,0x5B,0xB7,0xB2,0x04,0x22,0x52,0x95,0x80,0x93,0x69,0xE8,0x8E,0x4C, +0x65,0xF1,0x91,0x03,0x2D,0x70,0x74,0x02,0xEA,0x8B,0x67,0x15,0x29,0x69,0x52,0x02, +0xBB,0xD7,0xDF,0x50,0x6A,0x55,0x46,0xBF,0xA0,0xA3,0x28,0x61,0x7F,0x70,0xD0,0xC3, +0xA2,0xAA,0x2C,0x21,0xAA,0x47,0xCE,0x28,0x9C,0x06,0x45,0x76,0xBF,0x82,0x18,0x27, +0xB4,0xD5,0xAE,0xB4,0xCB,0x50,0xE6,0x6B,0xF4,0x4C,0x86,0x71,0x30,0xE9,0xA6,0xDF, +0x16,0x86,0xE0,0xD8,0xFF,0x40,0xDD,0xFB,0xD0,0x42,0x88,0x7F,0xA3,0x33,0x3A,0x2E, +0x5C,0x1E,0x41,0x11,0x81,0x63,0xCE,0x18,0x71,0x6B,0x2B,0xEC,0xA6,0x8A,0xB7,0x31, +0x5C,0x3A,0x6A,0x47,0xE0,0xC3,0x79,0x59,0xD6,0x20,0x1A,0xAF,0xF2,0x6A,0x98,0xAA, +0x72,0xBC,0x57,0x4A,0xD2,0x4B,0x9D,0xBB,0x10,0xFC,0xB0,0x4C,0x41,0xE5,0xED,0x1D, +0x3D,0x5E,0x28,0x9D,0x9C,0xCC,0xBF,0xB3,0x51,0xDA,0xA7,0x47,0xE5,0x84,0x53,0x02, +0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04, +0x16,0x04,0x14,0xBB,0xAF,0x7E,0x02,0x3D,0xFA,0xA6,0xF1,0x3C,0x84,0x8E,0xAD,0xEE, +0x38,0x98,0xEC,0xD9,0x32,0x32,0xD4,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01, +0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01, +0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x0C,0x05,0x00,0x03,0x82,0x02,0x01,0x00,0x0A,0xF1,0xD5,0x46, +0x84,0xB7,0xAE,0x51,0xBB,0x6C,0xB2,0x4D,0x41,0x14,0x00,0x93,0x4C,0x9C,0xCB,0xE5, +0xC0,0x54,0xCF,0xA0,0x25,0x8E,0x02,0xF9,0xFD,0xB0,0xA2,0x0D,0xF5,0x20,0x98,0x3C, +0x13,0x2D,0xAC,0x56,0xA2,0xB0,0xD6,0x7E,0x11,0x92,0xE9,0x2E,0xBA,0x9E,0x2E,0x9A, +0x72,0xB1,0xBD,0x19,0x44,0x6C,0x61,0x35,0xA2,0x9A,0xB4,0x16,0x12,0x69,0x5A,0x8C, +0xE1,0xD7,0x3E,0xA4,0x1A,0xE8,0x2F,0x03,0xF4,0xAE,0x61,0x1D,0x10,0x1B,0x2A,0xA4, +0x8B,0x7A,0xC5,0xFE,0x05,0xA6,0xE1,0xC0,0xD6,0xC8,0xFE,0x9E,0xAE,0x8F,0x2B,0xBA, +0x3D,0x99,0xF8,0xD8,0x73,0x09,0x58,0x46,0x6E,0xA6,0x9C,0xF4,0xD7,0x27,0xD3,0x95, +0xDA,0x37,0x83,0x72,0x1C,0xD3,0x73,0xE0,0xA2,0x47,0x99,0x03,0x38,0x5D,0xD5,0x49, +0x79,0x00,0x29,0x1C,0xC7,0xEC,0x9B,0x20,0x1C,0x07,0x24,0x69,0x57,0x78,0xB2,0x39, +0xFC,0x3A,0x84,0xA0,0xB5,0x9C,0x7C,0x8D,0xBF,0x2E,0x93,0x62,0x27,0xB7,0x39,0xDA, +0x17,0x18,0xAE,0xBD,0x3C,0x09,0x68,0xFF,0x84,0x9B,0x3C,0xD5,0xD6,0x0B,0x03,0xE3, +0x57,0x9E,0x14,0xF7,0xD1,0xEB,0x4F,0xC8,0xBD,0x87,0x23,0xB7,0xB6,0x49,0x43,0x79, +0x85,0x5C,0xBA,0xEB,0x92,0x0B,0xA1,0xC6,0xE8,0x68,0xA8,0x4C,0x16,0xB1,0x1A,0x99, +0x0A,0xE8,0x53,0x2C,0x92,0xBB,0xA1,0x09,0x18,0x75,0x0C,0x65,0xA8,0x7B,0xCB,0x23, +0xB7,0x1A,0xC2,0x28,0x85,0xC3,0x1B,0xFF,0xD0,0x2B,0x62,0xEF,0xA4,0x7B,0x09,0x91, +0x98,0x67,0x8C,0x14,0x01,0xCD,0x68,0x06,0x6A,0x63,0x21,0x75,0x03,0x80,0x88,0x8A, +0x6E,0x81,0xC6,0x85,0xF2,0xA9,0xA4,0x2D,0xE7,0xF4,0xA5,0x24,0x10,0x47,0x83,0xCA, +0xCD,0xF4,0x8D,0x79,0x58,0xB1,0x06,0x9B,0xE7,0x1A,0x2A,0xD9,0x9D,0x01,0xD7,0x94, +0x7D,0xED,0x03,0x4A,0xCA,0xF0,0xDB,0xE8,0xA9,0x01,0x3E,0xF5,0x56,0x99,0xC9,0x1E, +0x8E,0x49,0x3D,0xBB,0xE5,0x09,0xB9,0xE0,0x4F,0x49,0x92,0x3D,0x16,0x82,0x40,0xCC, +0xCC,0x59,0xC6,0xE6,0x3A,0xED,0x12,0x2E,0x69,0x3C,0x6C,0x95,0xB1,0xFD,0xAA,0x1D, +0x7B,0x7F,0x86,0xBE,0x1E,0x0E,0x32,0x46,0xFB,0xFB,0x13,0x8F,0x75,0x7F,0x4C,0x8B, +0x4B,0x46,0x63,0xFE,0x00,0x34,0x40,0x70,0xC1,0xC3,0xB9,0xA1,0xDD,0xA6,0x70,0xE2, +0x04,0xB3,0x41,0xBC,0xE9,0x80,0x91,0xEA,0x64,0x9C,0x7A,0xE1,0x22,0x03,0xA9,0x9C, +0x6E,0x6F,0x0E,0x65,0x4F,0x6C,0x87,0x87,0x5E,0xF3,0x6E,0xA0,0xF9,0x75,0xA5,0x9B, +0x40,0xE8,0x53,0xB2,0x27,0x9D,0x4A,0xB9,0xC0,0x77,0x21,0x8D,0xFF,0x87,0xF2,0xDE, +0xBC,0x8C,0xEF,0x17,0xDF,0xB7,0x49,0x0B,0xD1,0xF2,0x6E,0x30,0x0B,0x1A,0x0E,0x4E, +0x76,0xED,0x11,0xFC,0xF5,0xE9,0x56,0xB2,0x7D,0xBF,0xC7,0x6D,0x0A,0x93,0x8C,0xA5, +0xD0,0xC0,0xB6,0x1D,0xBE,0x3A,0x4E,0x94,0xA2,0xD7,0x6E,0x6C,0x0B,0xC2,0x8A,0x7C, +0xFA,0x20,0xF3,0xC4,0xE4,0xE5,0xCD,0x0D,0xA8,0xCB,0x91,0x92,0xB1,0x7C,0x85,0xEC, +0xB5,0x14,0x69,0x66,0x0E,0x82,0xE7,0xCD,0xCE,0xC8,0x2D,0xA6,0x51,0x7F,0x21,0xC1, +0x35,0x53,0x85,0x06,0x4A,0x5D,0x9F,0xAD,0xBB,0x1B,0x5F,0x74, +}; + +/* subject: Common Name: USERTrust ECC Certification Authority, Organization: The USERTRUST Network, Locality: Jersey City, State/Province: New Jersey, Country: US */ +/* issuer: Common Name: USERTrust ECC Certification Authority, Organization: The USERTRUST Network, Locality: Jersey City, State/Province: New Jersey, Country: US */ +/* link: https://crt.sh/?q=4ff460d54b9c86dabfbcfc5712e0400d2bed3fbc4d4fbdaa86e06adcd2a9ad7a */ +const unsigned char kCertificateWithFingerprint_4ff460d54b9c86dabfbcfc5712e0400d2bed3fbc4d4fbdaa86e06adcd2a9ad7a_certificate[659]={ +0x30,0x82,0x02,0x8F,0x30,0x82,0x02,0x15,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x5C, +0x8B,0x99,0xC5,0x5A,0x94,0xC5,0xD2,0x71,0x56,0xDE,0xCD,0x89,0x80,0xCC,0x26,0x30, +0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x81,0x88,0x31,0x0B, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x13,0x30,0x11,0x06, +0x03,0x55,0x04,0x08,0x13,0x0A,0x4E,0x65,0x77,0x20,0x4A,0x65,0x72,0x73,0x65,0x79, +0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x07,0x13,0x0B,0x4A,0x65,0x72,0x73,0x65, +0x79,0x20,0x43,0x69,0x74,0x79,0x31,0x1E,0x30,0x1C,0x06,0x03,0x55,0x04,0x0A,0x13, +0x15,0x54,0x68,0x65,0x20,0x55,0x53,0x45,0x52,0x54,0x52,0x55,0x53,0x54,0x20,0x4E, +0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x2E,0x30,0x2C,0x06,0x03,0x55,0x04,0x03,0x13, +0x25,0x55,0x53,0x45,0x52,0x54,0x72,0x75,0x73,0x74,0x20,0x45,0x43,0x43,0x20,0x43, +0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74, +0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x31,0x30,0x30,0x32,0x30,0x31, +0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31,0x31,0x38,0x32, +0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0x88,0x31,0x0B,0x30,0x09,0x06,0x03,0x55, +0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x08,0x13, +0x0A,0x4E,0x65,0x77,0x20,0x4A,0x65,0x72,0x73,0x65,0x79,0x31,0x14,0x30,0x12,0x06, +0x03,0x55,0x04,0x07,0x13,0x0B,0x4A,0x65,0x72,0x73,0x65,0x79,0x20,0x43,0x69,0x74, +0x79,0x31,0x1E,0x30,0x1C,0x06,0x03,0x55,0x04,0x0A,0x13,0x15,0x54,0x68,0x65,0x20, +0x55,0x53,0x45,0x52,0x54,0x52,0x55,0x53,0x54,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72, +0x6B,0x31,0x2E,0x30,0x2C,0x06,0x03,0x55,0x04,0x03,0x13,0x25,0x55,0x53,0x45,0x52, +0x54,0x72,0x75,0x73,0x74,0x20,0x45,0x43,0x43,0x20,0x43,0x65,0x72,0x74,0x69,0x66, +0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74, +0x79,0x30,0x76,0x30,0x10,0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05, +0x2B,0x81,0x04,0x00,0x22,0x03,0x62,0x00,0x04,0x1A,0xAC,0x54,0x5A,0xA9,0xF9,0x68, +0x23,0xE7,0x7A,0xD5,0x24,0x6F,0x53,0xC6,0x5A,0xD8,0x4B,0xAB,0xC6,0xD5,0xB6,0xD1, +0xE6,0x73,0x71,0xAE,0xDD,0x9C,0xD6,0x0C,0x61,0xFD,0xDB,0xA0,0x89,0x03,0xB8,0x05, +0x14,0xEC,0x57,0xCE,0xEE,0x5D,0x3F,0xE2,0x21,0xB3,0xCE,0xF7,0xD4,0x8A,0x79,0xE0, +0xA3,0x83,0x7E,0x2D,0x97,0xD0,0x61,0xC4,0xF1,0x99,0xDC,0x25,0x91,0x63,0xAB,0x7F, +0x30,0xA3,0xB4,0x70,0xE2,0xC7,0xA1,0x33,0x9C,0xF3,0xBF,0x2E,0x5C,0x53,0xB1,0x5F, +0xB3,0x7D,0x32,0x7F,0x8A,0x34,0xE3,0x79,0x79,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06, +0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x3A,0xE1,0x09,0x86,0xD4,0xCF,0x19,0xC2, +0x96,0x76,0x74,0x49,0x76,0xDC,0xE0,0x35,0xC6,0x63,0x63,0x9A,0x30,0x0E,0x06,0x03, +0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06,0x03, +0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0A,0x06, +0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x03,0x68,0x00,0x30,0x65,0x02,0x30, +0x36,0x67,0xA1,0x16,0x08,0xDC,0xE4,0x97,0x00,0x41,0x1D,0x4E,0xBE,0xE1,0x63,0x01, +0xCF,0x3B,0xAA,0x42,0x11,0x64,0xA0,0x9D,0x94,0x39,0x02,0x11,0x79,0x5C,0x7B,0x1D, +0xFA,0x64,0xB9,0xEE,0x16,0x42,0xB3,0xBF,0x8A,0xC2,0x09,0xC4,0xEC,0xE4,0xB1,0x4D, +0x02,0x31,0x00,0xE9,0x2A,0x61,0x47,0x8C,0x52,0x4A,0x4B,0x4E,0x18,0x70,0xF6,0xD6, +0x44,0xD6,0x6E,0xF5,0x83,0xBA,0x6D,0x58,0xBD,0x24,0xD9,0x56,0x48,0xEA,0xEF,0xC4, +0xA2,0x46,0x81,0x88,0x6A,0x3A,0x46,0xD1,0xA9,0x9B,0x4D,0xC9,0x61,0xDA,0xD1,0x5D, +0x57,0x6A,0x18, +}; + +/* subject: Common Name: USERTrust RSA Certification Authority, Organization: The USERTRUST Network, Locality: Jersey City, State/Province: New Jersey, Country: US */ +/* issuer: Common Name: USERTrust RSA Certification Authority, Organization: The USERTRUST Network, Locality: Jersey City, State/Province: New Jersey, Country: US */ +/* link: https://crt.sh/?q=e793c9b02fd8aa13e21c31228accb08119643b749c898964b1746d46c3d4cbd2 */ +const unsigned char kCertificateWithFingerprint_e793c9b02fd8aa13e21c31228accb08119643b749c898964b1746d46c3d4cbd2_certificate[1506]={ +0x30,0x82,0x05,0xDE,0x30,0x82,0x03,0xC6,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x01, +0xFD,0x6D,0x30,0xFC,0xA3,0xCA,0x51,0xA8,0x1B,0xBC,0x64,0x0E,0x35,0x03,0x2D,0x30, +0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C,0x05,0x00,0x30,0x81, +0x88,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x13, +0x30,0x11,0x06,0x03,0x55,0x04,0x08,0x13,0x0A,0x4E,0x65,0x77,0x20,0x4A,0x65,0x72, +0x73,0x65,0x79,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x07,0x13,0x0B,0x4A,0x65, +0x72,0x73,0x65,0x79,0x20,0x43,0x69,0x74,0x79,0x31,0x1E,0x30,0x1C,0x06,0x03,0x55, +0x04,0x0A,0x13,0x15,0x54,0x68,0x65,0x20,0x55,0x53,0x45,0x52,0x54,0x52,0x55,0x53, +0x54,0x20,0x4E,0x65,0x74,0x77,0x6F,0x72,0x6B,0x31,0x2E,0x30,0x2C,0x06,0x03,0x55, +0x04,0x03,0x13,0x25,0x55,0x53,0x45,0x52,0x54,0x72,0x75,0x73,0x74,0x20,0x52,0x53, +0x41,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20, +0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79,0x30,0x1E,0x17,0x0D,0x31,0x30,0x30, +0x32,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30,0x31, +0x31,0x38,0x32,0x33,0x35,0x39,0x35,0x39,0x5A,0x30,0x81,0x88,0x31,0x0B,0x30,0x09, +0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x13,0x30,0x11,0x06,0x03,0x55, +0x04,0x08,0x13,0x0A,0x4E,0x65,0x77,0x20,0x4A,0x65,0x72,0x73,0x65,0x79,0x31,0x14, +0x30,0x12,0x06,0x03,0x55,0x04,0x07,0x13,0x0B,0x4A,0x65,0x72,0x73,0x65,0x79,0x20, +0x43,0x69,0x74,0x79,0x31,0x1E,0x30,0x1C,0x06,0x03,0x55,0x04,0x0A,0x13,0x15,0x54, +0x68,0x65,0x20,0x55,0x53,0x45,0x52,0x54,0x52,0x55,0x53,0x54,0x20,0x4E,0x65,0x74, +0x77,0x6F,0x72,0x6B,0x31,0x2E,0x30,0x2C,0x06,0x03,0x55,0x04,0x03,0x13,0x25,0x55, +0x53,0x45,0x52,0x54,0x72,0x75,0x73,0x74,0x20,0x52,0x53,0x41,0x20,0x43,0x65,0x72, +0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x41,0x75,0x74,0x68,0x6F, +0x72,0x69,0x74,0x79,0x30,0x82,0x02,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86, +0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x02,0x0F,0x00,0x30,0x82,0x02,0x0A, +0x02,0x82,0x02,0x01,0x00,0x80,0x12,0x65,0x17,0x36,0x0E,0xC3,0xDB,0x08,0xB3,0xD0, +0xAC,0x57,0x0D,0x76,0xED,0xCD,0x27,0xD3,0x4C,0xAD,0x50,0x83,0x61,0xE2,0xAA,0x20, +0x4D,0x09,0x2D,0x64,0x09,0xDC,0xCE,0x89,0x9F,0xCC,0x3D,0xA9,0xEC,0xF6,0xCF,0xC1, +0xDC,0xF1,0xD3,0xB1,0xD6,0x7B,0x37,0x28,0x11,0x2B,0x47,0xDA,0x39,0xC6,0xBC,0x3A, +0x19,0xB4,0x5F,0xA6,0xBD,0x7D,0x9D,0xA3,0x63,0x42,0xB6,0x76,0xF2,0xA9,0x3B,0x2B, +0x91,0xF8,0xE2,0x6F,0xD0,0xEC,0x16,0x20,0x90,0x09,0x3E,0xE2,0xE8,0x74,0xC9,0x18, +0xB4,0x91,0xD4,0x62,0x64,0xDB,0x7F,0xA3,0x06,0xF1,0x88,0x18,0x6A,0x90,0x22,0x3C, +0xBC,0xFE,0x13,0xF0,0x87,0x14,0x7B,0xF6,0xE4,0x1F,0x8E,0xD4,0xE4,0x51,0xC6,0x11, +0x67,0x46,0x08,0x51,0xCB,0x86,0x14,0x54,0x3F,0xBC,0x33,0xFE,0x7E,0x6C,0x9C,0xFF, +0x16,0x9D,0x18,0xBD,0x51,0x8E,0x35,0xA6,0xA7,0x66,0xC8,0x72,0x67,0xDB,0x21,0x66, +0xB1,0xD4,0x9B,0x78,0x03,0xC0,0x50,0x3A,0xE8,0xCC,0xF0,0xDC,0xBC,0x9E,0x4C,0xFE, +0xAF,0x05,0x96,0x35,0x1F,0x57,0x5A,0xB7,0xFF,0xCE,0xF9,0x3D,0xB7,0x2C,0xB6,0xF6, +0x54,0xDD,0xC8,0xE7,0x12,0x3A,0x4D,0xAE,0x4C,0x8A,0xB7,0x5C,0x9A,0xB4,0xB7,0x20, +0x3D,0xCA,0x7F,0x22,0x34,0xAE,0x7E,0x3B,0x68,0x66,0x01,0x44,0xE7,0x01,0x4E,0x46, +0x53,0x9B,0x33,0x60,0xF7,0x94,0xBE,0x53,0x37,0x90,0x73,0x43,0xF3,0x32,0xC3,0x53, +0xEF,0xDB,0xAA,0xFE,0x74,0x4E,0x69,0xC7,0x6B,0x8C,0x60,0x93,0xDE,0xC4,0xC7,0x0C, +0xDF,0xE1,0x32,0xAE,0xCC,0x93,0x3B,0x51,0x78,0x95,0x67,0x8B,0xEE,0x3D,0x56,0xFE, +0x0C,0xD0,0x69,0x0F,0x1B,0x0F,0xF3,0x25,0x26,0x6B,0x33,0x6D,0xF7,0x6E,0x47,0xFA, +0x73,0x43,0xE5,0x7E,0x0E,0xA5,0x66,0xB1,0x29,0x7C,0x32,0x84,0x63,0x55,0x89,0xC4, +0x0D,0xC1,0x93,0x54,0x30,0x19,0x13,0xAC,0xD3,0x7D,0x37,0xA7,0xEB,0x5D,0x3A,0x6C, +0x35,0x5C,0xDB,0x41,0xD7,0x12,0xDA,0xA9,0x49,0x0B,0xDF,0xD8,0x80,0x8A,0x09,0x93, +0x62,0x8E,0xB5,0x66,0xCF,0x25,0x88,0xCD,0x84,0xB8,0xB1,0x3F,0xA4,0x39,0x0F,0xD9, +0x02,0x9E,0xEB,0x12,0x4C,0x95,0x7C,0xF3,0x6B,0x05,0xA9,0x5E,0x16,0x83,0xCC,0xB8, +0x67,0xE2,0xE8,0x13,0x9D,0xCC,0x5B,0x82,0xD3,0x4C,0xB3,0xED,0x5B,0xFF,0xDE,0xE5, +0x73,0xAC,0x23,0x3B,0x2D,0x00,0xBF,0x35,0x55,0x74,0x09,0x49,0xD8,0x49,0x58,0x1A, +0x7F,0x92,0x36,0xE6,0x51,0x92,0x0E,0xF3,0x26,0x7D,0x1C,0x4D,0x17,0xBC,0xC9,0xEC, +0x43,0x26,0xD0,0xBF,0x41,0x5F,0x40,0xA9,0x44,0x44,0xF4,0x99,0xE7,0x57,0x87,0x9E, +0x50,0x1F,0x57,0x54,0xA8,0x3E,0xFD,0x74,0x63,0x2F,0xB1,0x50,0x65,0x09,0xE6,0x58, +0x42,0x2E,0x43,0x1A,0x4C,0xB4,0xF0,0x25,0x47,0x59,0xFA,0x04,0x1E,0x93,0xD4,0x26, +0x46,0x4A,0x50,0x81,0xB2,0xDE,0xBE,0x78,0xB7,0xFC,0x67,0x15,0xE1,0xC9,0x57,0x84, +0x1E,0x0F,0x63,0xD6,0xE9,0x62,0xBA,0xD6,0x5F,0x55,0x2E,0xEA,0x5C,0xC6,0x28,0x08, +0x04,0x25,0x39,0xB8,0x0E,0x2B,0xA9,0xF2,0x4C,0x97,0x1C,0x07,0x3F,0x0D,0x52,0xF5, +0xED,0xEF,0x2F,0x82,0x0F,0x02,0x03,0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x1D, +0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x53,0x79,0xBF,0x5A,0xAA,0x2B,0x4A, +0xCF,0x54,0x80,0xE1,0xD8,0x9B,0xC0,0x9D,0xF2,0xB2,0x03,0x66,0xCB,0x30,0x0E,0x06, +0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x0F,0x06, +0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0D, +0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C,0x05,0x00,0x03,0x82,0x02, +0x01,0x00,0x5C,0xD4,0x7C,0x0D,0xCF,0xF7,0x01,0x7D,0x41,0x99,0x65,0x0C,0x73,0xC5, +0x52,0x9F,0xCB,0xF8,0xCF,0x99,0x06,0x7F,0x1B,0xDA,0x43,0x15,0x9F,0x9E,0x02,0x55, +0x57,0x96,0x14,0xF1,0x52,0x3C,0x27,0x87,0x94,0x28,0xED,0x1F,0x3A,0x01,0x37,0xA2, +0x76,0xFC,0x53,0x50,0xC0,0x84,0x9B,0xC6,0x6B,0x4E,0xBA,0x8C,0x21,0x4F,0xA2,0x8E, +0x55,0x62,0x91,0xF3,0x69,0x15,0xD8,0xBC,0x88,0xE3,0xC4,0xAA,0x0B,0xFD,0xEF,0xA8, +0xE9,0x4B,0x55,0x2A,0x06,0x20,0x6D,0x55,0x78,0x29,0x19,0xEE,0x5F,0x30,0x5C,0x4B, +0x24,0x11,0x55,0xFF,0x24,0x9A,0x6E,0x5E,0x2A,0x2B,0xEE,0x0B,0x4D,0x9F,0x7F,0xF7, +0x01,0x38,0x94,0x14,0x95,0x43,0x07,0x09,0xFB,0x60,0xA9,0xEE,0x1C,0xAB,0x12,0x8C, +0xA0,0x9A,0x5E,0xA7,0x98,0x6A,0x59,0x6D,0x8B,0x3F,0x08,0xFB,0xC8,0xD1,0x45,0xAF, +0x18,0x15,0x64,0x90,0x12,0x0F,0x73,0x28,0x2E,0xC5,0xE2,0x24,0x4E,0xFC,0x58,0xEC, +0xF0,0xF4,0x45,0xFE,0x22,0xB3,0xEB,0x2F,0x8E,0xD2,0xD9,0x45,0x61,0x05,0xC1,0x97, +0x6F,0xA8,0x76,0x72,0x8F,0x8B,0x8C,0x36,0xAF,0xBF,0x0D,0x05,0xCE,0x71,0x8D,0xE6, +0xA6,0x6F,0x1F,0x6C,0xA6,0x71,0x62,0xC5,0xD8,0xD0,0x83,0x72,0x0C,0xF1,0x67,0x11, +0x89,0x0C,0x9C,0x13,0x4C,0x72,0x34,0xDF,0xBC,0xD5,0x71,0xDF,0xAA,0x71,0xDD,0xE1, +0xB9,0x6C,0x8C,0x3C,0x12,0x5D,0x65,0xDA,0xBD,0x57,0x12,0xB6,0x43,0x6B,0xFF,0xE5, +0xDE,0x4D,0x66,0x11,0x51,0xCF,0x99,0xAE,0xEC,0x17,0xB6,0xE8,0x71,0x91,0x8C,0xDE, +0x49,0xFE,0xDD,0x35,0x71,0xA2,0x15,0x27,0x94,0x1C,0xCF,0x61,0xE3,0x26,0xBB,0x6F, +0xA3,0x67,0x25,0x21,0x5D,0xE6,0xDD,0x1D,0x0B,0x2E,0x68,0x1B,0x3B,0x82,0xAF,0xEC, +0x83,0x67,0x85,0xD4,0x98,0x51,0x74,0xB1,0xB9,0x99,0x80,0x89,0xFF,0x7F,0x78,0x19, +0x5C,0x79,0x4A,0x60,0x2E,0x92,0x40,0xAE,0x4C,0x37,0x2A,0x2C,0xC9,0xC7,0x62,0xC8, +0x0E,0x5D,0xF7,0x36,0x5B,0xCA,0xE0,0x25,0x25,0x01,0xB4,0xDD,0x1A,0x07,0x9C,0x77, +0x00,0x3F,0xD0,0xDC,0xD5,0xEC,0x3D,0xD4,0xFA,0xBB,0x3F,0xCC,0x85,0xD6,0x6F,0x7F, +0xA9,0x2D,0xDF,0xB9,0x02,0xF7,0xF5,0x97,0x9A,0xB5,0x35,0xDA,0xC3,0x67,0xB0,0x87, +0x4A,0xA9,0x28,0x9E,0x23,0x8E,0xFF,0x5C,0x27,0x6B,0xE1,0xB0,0x4F,0xF3,0x07,0xEE, +0x00,0x2E,0xD4,0x59,0x87,0xCB,0x52,0x41,0x95,0xEA,0xF4,0x47,0xD7,0xEE,0x64,0x41, +0x55,0x7C,0x8D,0x59,0x02,0x95,0xDD,0x62,0x9D,0xC2,0xB9,0xEE,0x5A,0x28,0x74,0x84, +0xA5,0x9B,0xB7,0x90,0xC7,0x0C,0x07,0xDF,0xF5,0x89,0x36,0x74,0x32,0xD6,0x28,0xC1, +0xB0,0xB0,0x0B,0xE0,0x9C,0x4C,0xC3,0x1C,0xD6,0xFC,0xE3,0x69,0xB5,0x47,0x46,0x81, +0x2F,0xA2,0x82,0xAB,0xD3,0x63,0x44,0x70,0xC4,0x8D,0xFF,0x2D,0x33,0xBA,0xAD,0x8F, +0x7B,0xB5,0x70,0x88,0xAE,0x3E,0x19,0xCF,0x40,0x28,0xD8,0xFC,0xC8,0x90,0xBB,0x5D, +0x99,0x22,0xF5,0x52,0xE6,0x58,0xC5,0x1F,0x88,0x31,0x43,0xEE,0x88,0x1D,0xD7,0xC6, +0x8E,0x3C,0x43,0x6A,0x1D,0xA7,0x18,0xDE,0x7D,0x3D,0x16,0xF1,0x62,0xF9,0xCA,0x90, +0xA8,0xFD, +}; + +/* subject: Common Name: GTS Root R1, Organization: Google Trust Services LLC, Country: US */ +/* issuer: Common Name: GTS Root R1, Organization: Google Trust Services LLC, Country: US */ +/* link: https://crt.sh/?q=d947432abde7b7fa90fc2e6b59101b1280e0e1c7e4e40fa3c6887fff57a7f4cf */ +const unsigned char kCertificateWithFingerprint_d947432abde7b7fa90fc2e6b59101b1280e0e1c7e4e40fa3c6887fff57a7f4cf_certificate[1371]={ +0x30,0x82,0x05,0x57,0x30,0x82,0x03,0x3F,0xA0,0x03,0x02,0x01,0x02,0x02,0x0D,0x02, +0x03,0xE5,0x93,0x6F,0x31,0xB0,0x13,0x49,0x88,0x6B,0xA2,0x17,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C,0x05,0x00,0x30,0x47,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x22,0x30,0x20,0x06,0x03, +0x55,0x04,0x0A,0x13,0x19,0x47,0x6F,0x6F,0x67,0x6C,0x65,0x20,0x54,0x72,0x75,0x73, +0x74,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x20,0x4C,0x4C,0x43,0x31,0x14, +0x30,0x12,0x06,0x03,0x55,0x04,0x03,0x13,0x0B,0x47,0x54,0x53,0x20,0x52,0x6F,0x6F, +0x74,0x20,0x52,0x31,0x30,0x1E,0x17,0x0D,0x31,0x36,0x30,0x36,0x32,0x32,0x30,0x30, +0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x36,0x30,0x36,0x32,0x32,0x30,0x30,0x30, +0x30,0x30,0x30,0x5A,0x30,0x47,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x55,0x53,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0A,0x13,0x19,0x47,0x6F, +0x6F,0x67,0x6C,0x65,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x53,0x65,0x72,0x76,0x69, +0x63,0x65,0x73,0x20,0x4C,0x4C,0x43,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x03, +0x13,0x0B,0x47,0x54,0x53,0x20,0x52,0x6F,0x6F,0x74,0x20,0x52,0x31,0x30,0x82,0x02, +0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00, +0x03,0x82,0x02,0x0F,0x00,0x30,0x82,0x02,0x0A,0x02,0x82,0x02,0x01,0x00,0xB6,0x11, +0x02,0x8B,0x1E,0xE3,0xA1,0x77,0x9B,0x3B,0xDC,0xBF,0x94,0x3E,0xB7,0x95,0xA7,0x40, +0x3C,0xA1,0xFD,0x82,0xF9,0x7D,0x32,0x06,0x82,0x71,0xF6,0xF6,0x8C,0x7F,0xFB,0xE8, +0xDB,0xBC,0x6A,0x2E,0x97,0x97,0xA3,0x8C,0x4B,0xF9,0x2B,0xF6,0xB1,0xF9,0xCE,0x84, +0x1D,0xB1,0xF9,0xC5,0x97,0xDE,0xEF,0xB9,0xF2,0xA3,0xE9,0xBC,0x12,0x89,0x5E,0xA7, +0xAA,0x52,0xAB,0xF8,0x23,0x27,0xCB,0xA4,0xB1,0x9C,0x63,0xDB,0xD7,0x99,0x7E,0xF0, +0x0A,0x5E,0xEB,0x68,0xA6,0xF4,0xC6,0x5A,0x47,0x0D,0x4D,0x10,0x33,0xE3,0x4E,0xB1, +0x13,0xA3,0xC8,0x18,0x6C,0x4B,0xEC,0xFC,0x09,0x90,0xDF,0x9D,0x64,0x29,0x25,0x23, +0x07,0xA1,0xB4,0xD2,0x3D,0x2E,0x60,0xE0,0xCF,0xD2,0x09,0x87,0xBB,0xCD,0x48,0xF0, +0x4D,0xC2,0xC2,0x7A,0x88,0x8A,0xBB,0xBA,0xCF,0x59,0x19,0xD6,0xAF,0x8F,0xB0,0x07, +0xB0,0x9E,0x31,0xF1,0x82,0xC1,0xC0,0xDF,0x2E,0xA6,0x6D,0x6C,0x19,0x0E,0xB5,0xD8, +0x7E,0x26,0x1A,0x45,0x03,0x3D,0xB0,0x79,0xA4,0x94,0x28,0xAD,0x0F,0x7F,0x26,0xE5, +0xA8,0x08,0xFE,0x96,0xE8,0x3C,0x68,0x94,0x53,0xEE,0x83,0x3A,0x88,0x2B,0x15,0x96, +0x09,0xB2,0xE0,0x7A,0x8C,0x2E,0x75,0xD6,0x9C,0xEB,0xA7,0x56,0x64,0x8F,0x96,0x4F, +0x68,0xAE,0x3D,0x97,0xC2,0x84,0x8F,0xC0,0xBC,0x40,0xC0,0x0B,0x5C,0xBD,0xF6,0x87, +0xB3,0x35,0x6C,0xAC,0x18,0x50,0x7F,0x84,0xE0,0x4C,0xCD,0x92,0xD3,0x20,0xE9,0x33, +0xBC,0x52,0x99,0xAF,0x32,0xB5,0x29,0xB3,0x25,0x2A,0xB4,0x48,0xF9,0x72,0xE1,0xCA, +0x64,0xF7,0xE6,0x82,0x10,0x8D,0xE8,0x9D,0xC2,0x8A,0x88,0xFA,0x38,0x66,0x8A,0xFC, +0x63,0xF9,0x01,0xF9,0x78,0xFD,0x7B,0x5C,0x77,0xFA,0x76,0x87,0xFA,0xEC,0xDF,0xB1, +0x0E,0x79,0x95,0x57,0xB4,0xBD,0x26,0xEF,0xD6,0x01,0xD1,0xEB,0x16,0x0A,0xBB,0x8E, +0x0B,0xB5,0xC5,0xC5,0x8A,0x55,0xAB,0xD3,0xAC,0xEA,0x91,0x4B,0x29,0xCC,0x19,0xA4, +0x32,0x25,0x4E,0x2A,0xF1,0x65,0x44,0xD0,0x02,0xCE,0xAA,0xCE,0x49,0xB4,0xEA,0x9F, +0x7C,0x83,0xB0,0x40,0x7B,0xE7,0x43,0xAB,0xA7,0x6C,0xA3,0x8F,0x7D,0x89,0x81,0xFA, +0x4C,0xA5,0xFF,0xD5,0x8E,0xC3,0xCE,0x4B,0xE0,0xB5,0xD8,0xB3,0x8E,0x45,0xCF,0x76, +0xC0,0xED,0x40,0x2B,0xFD,0x53,0x0F,0xB0,0xA7,0xD5,0x3B,0x0D,0xB1,0x8A,0xA2,0x03, +0xDE,0x31,0xAD,0xCC,0x77,0xEA,0x6F,0x7B,0x3E,0xD6,0xDF,0x91,0x22,0x12,0xE6,0xBE, +0xFA,0xD8,0x32,0xFC,0x10,0x63,0x14,0x51,0x72,0xDE,0x5D,0xD6,0x16,0x93,0xBD,0x29, +0x68,0x33,0xEF,0x3A,0x66,0xEC,0x07,0x8A,0x26,0xDF,0x13,0xD7,0x57,0x65,0x78,0x27, +0xDE,0x5E,0x49,0x14,0x00,0xA2,0x00,0x7F,0x9A,0xA8,0x21,0xB6,0xA9,0xB1,0x95,0xB0, +0xA5,0xB9,0x0D,0x16,0x11,0xDA,0xC7,0x6C,0x48,0x3C,0x40,0xE0,0x7E,0x0D,0x5A,0xCD, +0x56,0x3C,0xD1,0x97,0x05,0xB9,0xCB,0x4B,0xED,0x39,0x4B,0x9C,0xC4,0x3F,0xD2,0x55, +0x13,0x6E,0x24,0xB0,0xD6,0x71,0xFA,0xF4,0xC1,0xBA,0xCC,0xED,0x1B,0xF5,0xFE,0x81, +0x41,0xD8,0x00,0x98,0x3D,0x3A,0xC8,0xAE,0x7A,0x98,0x37,0x18,0x05,0x95,0x02,0x03, +0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01, +0xFF,0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01, +0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04, +0x16,0x04,0x14,0xE4,0xAF,0x2B,0x26,0x71,0x1A,0x2B,0x48,0x27,0x85,0x2F,0x52,0x66, +0x2C,0xEF,0xF0,0x89,0x13,0x71,0x3E,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x0C,0x05,0x00,0x03,0x82,0x02,0x01,0x00,0x9F,0xAA,0x42,0x26,0xDB, +0x0B,0x9B,0xBE,0xFF,0x1E,0x96,0x92,0x2E,0x3E,0xA2,0x65,0x4A,0x6A,0x98,0xBA,0x22, +0xCB,0x7D,0xC1,0x3A,0xD8,0x82,0x0A,0x06,0xC6,0xF6,0xA5,0xDE,0xC0,0x4E,0x87,0x66, +0x79,0xA1,0xF9,0xA6,0x58,0x9C,0xAA,0xF9,0xB5,0xE6,0x60,0xE7,0xE0,0xE8,0xB1,0x1E, +0x42,0x41,0x33,0x0B,0x37,0x3D,0xCE,0x89,0x70,0x15,0xCA,0xB5,0x24,0xA8,0xCF,0x6B, +0xB5,0xD2,0x40,0x21,0x98,0xCF,0x22,0x34,0xCF,0x3B,0xC5,0x22,0x84,0xE0,0xC5,0x0E, +0x8A,0x7C,0x5D,0x88,0xE4,0x35,0x24,0xCE,0x9B,0x3E,0x1A,0x54,0x1E,0x6E,0xDB,0xB2, +0x87,0xA7,0xFC,0xF3,0xFA,0x81,0x55,0x14,0x62,0x0A,0x59,0xA9,0x22,0x05,0x31,0x3E, +0x82,0xD6,0xEE,0xDB,0x57,0x34,0xBC,0x33,0x95,0xD3,0x17,0x1B,0xE8,0x27,0xA2,0x8B, +0x7B,0x4E,0x26,0x1A,0x7A,0x5A,0x64,0xB6,0xD1,0xAC,0x37,0xF1,0xFD,0xA0,0xF3,0x38, +0xEC,0x72,0xF0,0x11,0x75,0x9D,0xCB,0x34,0x52,0x8D,0xE6,0x76,0x6B,0x17,0xC6,0xDF, +0x86,0xAB,0x27,0x8E,0x49,0x2B,0x75,0x66,0x81,0x10,0x21,0xA6,0xEA,0x3E,0xF4,0xAE, +0x25,0xFF,0x7C,0x15,0xDE,0xCE,0x8C,0x25,0x3F,0xCA,0x62,0x70,0x0A,0xF7,0x2F,0x09, +0x66,0x07,0xC8,0x3F,0x1C,0xFC,0xF0,0xDB,0x45,0x30,0xDF,0x62,0x88,0xC1,0xB5,0x0F, +0x9D,0xC3,0x9F,0x4A,0xDE,0x59,0x59,0x47,0xC5,0x87,0x22,0x36,0xE6,0x82,0xA7,0xED, +0x0A,0xB9,0xE2,0x07,0xA0,0x8D,0x7B,0x7A,0x4A,0x3C,0x71,0xD2,0xE2,0x03,0xA1,0x1F, +0x32,0x07,0xDD,0x1B,0xE4,0x42,0xCE,0x0C,0x00,0x45,0x61,0x80,0xB5,0x0B,0x20,0x59, +0x29,0x78,0xBD,0xF9,0x55,0xCB,0x63,0xC5,0x3C,0x4C,0xF4,0xB6,0xFF,0xDB,0x6A,0x5F, +0x31,0x6B,0x99,0x9E,0x2C,0xC1,0x6B,0x50,0xA4,0xD7,0xE6,0x18,0x14,0xBD,0x85,0x3F, +0x67,0xAB,0x46,0x9F,0xA0,0xFF,0x42,0xA7,0x3A,0x7F,0x5C,0xCB,0x5D,0xB0,0x70,0x1D, +0x2B,0x34,0xF5,0xD4,0x76,0x09,0x0C,0xEB,0x78,0x4C,0x59,0x05,0xF3,0x33,0x42,0xC3, +0x61,0x15,0x10,0x1B,0x77,0x4D,0xCE,0x22,0x8C,0xD4,0x85,0xF2,0x45,0x7D,0xB7,0x53, +0xEA,0xEF,0x40,0x5A,0x94,0x0A,0x5C,0x20,0x5F,0x4E,0x40,0x5D,0x62,0x22,0x76,0xDF, +0xFF,0xCE,0x61,0xBD,0x8C,0x23,0x78,0xD2,0x37,0x02,0xE0,0x8E,0xDE,0xD1,0x11,0x37, +0x89,0xF6,0xBF,0xED,0x49,0x07,0x62,0xAE,0x92,0xEC,0x40,0x1A,0xAF,0x14,0x09,0xD9, +0xD0,0x4E,0xB2,0xA2,0xF7,0xBE,0xEE,0xEE,0xD8,0xFF,0xDC,0x1A,0x2D,0xDE,0xB8,0x36, +0x71,0xE2,0xFC,0x79,0xB7,0x94,0x25,0xD1,0x48,0x73,0x5B,0xA1,0x35,0xE7,0xB3,0x99, +0x67,0x75,0xC1,0x19,0x3A,0x2B,0x47,0x4E,0xD3,0x42,0x8E,0xFD,0x31,0xC8,0x16,0x66, +0xDA,0xD2,0x0C,0x3C,0xDB,0xB3,0x8E,0xC9,0xA1,0x0D,0x80,0x0F,0x7B,0x16,0x77,0x14, +0xBF,0xFF,0xDB,0x09,0x94,0xB2,0x93,0xBC,0x20,0x58,0x15,0xE9,0xDB,0x71,0x43,0xF3, +0xDE,0x10,0xC3,0x00,0xDC,0xA8,0x2A,0x95,0xB6,0xC2,0xD6,0x3F,0x90,0x6B,0x76,0xDB, +0x6C,0xFE,0x8C,0xBC,0xF2,0x70,0x35,0x0C,0xDC,0x99,0x19,0x35,0xDC,0xD7,0xC8,0x46, +0x63,0xD5,0x36,0x71,0xAE,0x57,0xFB,0xB7,0x82,0x6D,0xDC, +}; + +/* subject: Common Name: GTS Root R2, Organization: Google Trust Services LLC, Country: US */ +/* issuer: Common Name: GTS Root R2, Organization: Google Trust Services LLC, Country: US */ +/* link: https://crt.sh/?q=8d25cd97229dbf70356bda4eb3cc734031e24cf00fafcfd32dc76eb5841c7ea8 */ +const unsigned char kCertificateWithFingerprint_8d25cd97229dbf70356bda4eb3cc734031e24cf00fafcfd32dc76eb5841c7ea8_certificate[1371]={ +0x30,0x82,0x05,0x57,0x30,0x82,0x03,0x3F,0xA0,0x03,0x02,0x01,0x02,0x02,0x0D,0x02, +0x03,0xE5,0xAE,0xC5,0x8D,0x04,0x25,0x1A,0xAB,0x11,0x25,0xAA,0x30,0x0D,0x06,0x09, +0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C,0x05,0x00,0x30,0x47,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x22,0x30,0x20,0x06,0x03, +0x55,0x04,0x0A,0x13,0x19,0x47,0x6F,0x6F,0x67,0x6C,0x65,0x20,0x54,0x72,0x75,0x73, +0x74,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x20,0x4C,0x4C,0x43,0x31,0x14, +0x30,0x12,0x06,0x03,0x55,0x04,0x03,0x13,0x0B,0x47,0x54,0x53,0x20,0x52,0x6F,0x6F, +0x74,0x20,0x52,0x32,0x30,0x1E,0x17,0x0D,0x31,0x36,0x30,0x36,0x32,0x32,0x30,0x30, +0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x36,0x30,0x36,0x32,0x32,0x30,0x30,0x30, +0x30,0x30,0x30,0x5A,0x30,0x47,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13, +0x02,0x55,0x53,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0A,0x13,0x19,0x47,0x6F, +0x6F,0x67,0x6C,0x65,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x53,0x65,0x72,0x76,0x69, +0x63,0x65,0x73,0x20,0x4C,0x4C,0x43,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x03, +0x13,0x0B,0x47,0x54,0x53,0x20,0x52,0x6F,0x6F,0x74,0x20,0x52,0x32,0x30,0x82,0x02, +0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00, +0x03,0x82,0x02,0x0F,0x00,0x30,0x82,0x02,0x0A,0x02,0x82,0x02,0x01,0x00,0xCE,0xDE, +0xFD,0xA6,0xFB,0xEC,0xEC,0x14,0x34,0x3C,0x07,0x06,0x5A,0x6C,0x59,0xF7,0x19,0x35, +0xDD,0xF7,0xC1,0x9D,0x55,0xAA,0xD3,0xCD,0x3B,0xA4,0x93,0x72,0xEF,0x0A,0xFA,0x6D, +0x9D,0xF6,0xF0,0x85,0x80,0x5B,0xA1,0x48,0x52,0x9F,0x39,0xC5,0xB7,0xEE,0x28,0xAC, +0xEF,0xCB,0x76,0x68,0x14,0xB9,0xDF,0xAD,0x01,0x6C,0x99,0x1F,0xC4,0x22,0x1D,0x9F, +0xFE,0x72,0x77,0xE0,0x2C,0x5B,0xAF,0xE4,0x04,0xBF,0x4F,0x72,0xA0,0x1A,0x34,0x98, +0xE8,0x39,0x68,0xEC,0x95,0x25,0x7B,0x76,0xA1,0xE6,0x69,0xB9,0x85,0x19,0xBD,0x89, +0x8C,0xFE,0xAD,0xED,0x36,0xEA,0x73,0xBC,0xFF,0x83,0xE2,0xCB,0x7D,0xC1,0xD2,0xCE, +0x4A,0xB3,0x8D,0x05,0x9E,0x8B,0x49,0x93,0xDF,0xC1,0x5B,0xD0,0x6E,0x5E,0xF0,0x2E, +0x30,0x2E,0x82,0xFC,0xFA,0xBC,0xB4,0x17,0x0A,0x48,0xE5,0x88,0x9B,0xC5,0x9B,0x6B, +0xDE,0xB0,0xCA,0xB4,0x03,0xF0,0xDA,0xF4,0x90,0xB8,0x65,0x64,0xF7,0x5C,0x4C,0xAD, +0xE8,0x7E,0x66,0x5E,0x99,0xD7,0xB8,0xC2,0x3E,0xC8,0xD0,0x13,0x9D,0xAD,0xEE,0xE4, +0x45,0x7B,0x89,0x55,0xF7,0x8A,0x1F,0x62,0x52,0x84,0x12,0xB3,0xC2,0x40,0x97,0xE3, +0x8A,0x1F,0x47,0x91,0xA6,0x74,0x5A,0xD2,0xF8,0xB1,0x63,0x28,0x10,0xB8,0xB3,0x09, +0xB8,0x56,0x77,0x40,0xA2,0x26,0x98,0x79,0xC6,0xFE,0xDF,0x25,0xEE,0x3E,0xE5,0xA0, +0x7F,0xD4,0x61,0x0F,0x51,0x4B,0x3C,0x3F,0x8C,0xDA,0xE1,0x70,0x74,0xD8,0xC2,0x68, +0xA1,0xF9,0xC1,0x0C,0xE9,0xA1,0xE2,0x7F,0xBB,0x55,0x3C,0x76,0x06,0xEE,0x6A,0x4E, +0xCC,0x92,0x88,0x30,0x4D,0x9A,0xBD,0x4F,0x0B,0x48,0x9A,0x84,0xB5,0x98,0xA3,0xD5, +0xFB,0x73,0xC1,0x57,0x61,0xDD,0x28,0x56,0x75,0x13,0xAE,0x87,0x8E,0xE7,0x0C,0x51, +0x09,0x10,0x75,0x88,0x4C,0xBC,0x8D,0xF9,0x7B,0x3C,0xD4,0x22,0x48,0x1F,0x2A,0xDC, +0xEB,0x6B,0xBB,0x44,0xB1,0xCB,0x33,0x71,0x32,0x46,0xAF,0xAD,0x4A,0xF1,0x8C,0xE8, +0x74,0x3A,0xAC,0xE7,0x1A,0x22,0x73,0x80,0xD2,0x30,0xF7,0x25,0x42,0xC7,0x22,0x3B, +0x3B,0x12,0xAD,0x96,0x2E,0xC6,0xC3,0x76,0x07,0xAA,0x20,0xB7,0x35,0x49,0x57,0xE9, +0x92,0x49,0xE8,0x76,0x16,0x72,0x31,0x67,0x2B,0x96,0x7E,0x8A,0xA3,0xC7,0x94,0x56, +0x22,0xBF,0x6A,0x4B,0x7E,0x01,0x21,0xB2,0x23,0x32,0xDF,0xE4,0x9A,0x44,0x6D,0x59, +0x5B,0x5D,0xF5,0x00,0xA0,0x1C,0x9B,0xC6,0x78,0x97,0x8D,0x90,0xFF,0x9B,0xC8,0xAA, +0xB4,0xAF,0x11,0x51,0x39,0x5E,0xD9,0xFB,0x67,0xAD,0xD5,0x5B,0x11,0x9D,0x32,0x9A, +0x1B,0xBD,0xD5,0xBA,0x5B,0xA5,0xC9,0xCB,0x25,0x69,0x53,0x55,0x27,0x5C,0xE0,0xCA, +0x36,0xCB,0x88,0x61,0xFB,0x1E,0xB7,0xD0,0xCB,0xEE,0x16,0xFB,0xD3,0xA6,0x4C,0xDE, +0x92,0xA5,0xD4,0xE2,0xDF,0xF5,0x06,0x54,0xDE,0x2E,0x9D,0x4B,0xB4,0x93,0x30,0xAA, +0x81,0xCE,0xDD,0x1A,0xDC,0x51,0x73,0x0D,0x4F,0x70,0xE9,0xE5,0xB6,0x16,0x21,0x19, +0x79,0xB2,0xE6,0x89,0x0B,0x75,0x64,0xCA,0xD5,0xAB,0xBC,0x09,0xC1,0x18,0xA1,0xFF, +0xD4,0x54,0xA1,0x85,0x3C,0xFD,0x14,0x24,0x03,0xB2,0x87,0xD3,0xA4,0xB7,0x02,0x03, +0x01,0x00,0x01,0xA3,0x42,0x30,0x40,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01, +0xFF,0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01, +0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04, +0x16,0x04,0x14,0xBB,0xFF,0xCA,0x8E,0x23,0x9F,0x4F,0x99,0xCA,0xDB,0xE2,0x68,0xA6, +0xA5,0x15,0x27,0x17,0x1E,0xD9,0x0E,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7, +0x0D,0x01,0x01,0x0C,0x05,0x00,0x03,0x82,0x02,0x01,0x00,0x1F,0xCA,0xCE,0xDD,0xC7, +0xBE,0xA1,0x9F,0xD9,0x27,0x4C,0x0B,0xDC,0x17,0x98,0x11,0x6A,0x88,0xDE,0x3D,0xE6, +0x71,0x56,0x72,0xB2,0x9E,0x1A,0x4E,0x9C,0xD5,0x2B,0x98,0x24,0x5D,0x9B,0x6B,0x7B, +0xB0,0x33,0x82,0x09,0xBD,0xDF,0x25,0x46,0xEA,0x98,0x9E,0xB6,0x1B,0xFE,0x83,0x3C, +0xD2,0x62,0x61,0xC1,0x04,0xED,0xCE,0xE0,0xC5,0xC9,0xC8,0x13,0x13,0x55,0xE7,0xA8, +0x63,0xAD,0x8C,0x7B,0x01,0xFE,0x77,0x30,0xE1,0xCE,0x68,0x9B,0x05,0xF8,0x12,0xEE, +0x79,0x31,0xA0,0x41,0x45,0x35,0x28,0x0A,0x71,0xA4,0x24,0x4F,0x8C,0xDC,0x3C,0x82, +0x07,0x5F,0x66,0xDC,0x7D,0x10,0xFE,0x0C,0x61,0xB3,0x05,0x95,0xEE,0xE1,0xAE,0x81, +0x0F,0xA8,0xF8,0xC7,0x8F,0x4D,0xA8,0x23,0x02,0x26,0x6B,0x1D,0x83,0x52,0x55,0xCE, +0xB5,0x2F,0x00,0xCA,0x80,0x40,0xE0,0xE1,0x74,0xAC,0x60,0xF5,0x87,0x80,0x9D,0xAE, +0x36,0x64,0x91,0x5D,0xB0,0x68,0x18,0xEA,0x8A,0x61,0xC9,0x77,0xA8,0x97,0xC4,0xC9, +0xC7,0xA5,0xFC,0x55,0x4B,0xF3,0xF0,0x7F,0xB9,0x65,0x3D,0x27,0x68,0xD0,0xCC,0x6B, +0xFA,0x53,0x9D,0xE1,0x91,0x1A,0xC9,0x5D,0x1A,0x96,0x6D,0x32,0x87,0xED,0x03,0x20, +0xC8,0x02,0xCE,0x5A,0xBE,0xD9,0xEA,0xFD,0xB2,0x4D,0xC4,0x2F,0x1B,0xDF,0x5F,0x7A, +0xF5,0xF8,0x8B,0xC6,0xEE,0x31,0x3A,0x25,0x51,0x55,0x67,0x8D,0x64,0x32,0x7B,0xE9, +0x9E,0xC3,0x82,0xBA,0x2A,0x2D,0xE9,0x1E,0xB4,0xE0,0x48,0x06,0xA2,0xFC,0x67,0xAF, +0x1F,0x22,0x02,0x73,0xFB,0x20,0x0A,0xAF,0x9D,0x54,0x4B,0xA1,0xCD,0xFF,0x60,0x47, +0xB0,0x3F,0x5D,0xEF,0x1B,0x56,0xBD,0x97,0x21,0x96,0x2D,0x0A,0xD1,0x5E,0x9D,0x38, +0x02,0x47,0x6C,0xB9,0xF4,0xF6,0x23,0x25,0xB8,0xA0,0x6A,0x9A,0x2B,0x77,0x08,0xFA, +0xC4,0xB1,0x28,0x90,0x26,0x58,0x08,0x3C,0xE2,0x7E,0xAA,0xD7,0x3D,0x6F,0xBA,0x31, +0x88,0x0A,0x05,0xEB,0x27,0xB5,0xA1,0x49,0xEE,0xA0,0x45,0x54,0x7B,0xE6,0x27,0x65, +0x99,0x20,0x21,0xA8,0xA3,0xBC,0xFB,0x18,0x96,0xBB,0x52,0x6F,0x0C,0xED,0x83,0x51, +0x4C,0xE9,0x59,0xE2,0x20,0x60,0xC5,0xC2,0x65,0x92,0x82,0x8C,0xF3,0x10,0x1F,0x0E, +0x8A,0x97,0xBE,0x77,0x82,0x6D,0x3F,0x8F,0x1D,0x5D,0xBC,0x49,0x27,0xBD,0xCC,0x4F, +0x0F,0xE1,0xCE,0x76,0x86,0x04,0x23,0xC5,0xC0,0x8C,0x12,0x5B,0xFD,0xDB,0x84,0xA0, +0x24,0xF1,0x48,0xFF,0x64,0x7C,0xD0,0xBE,0x5C,0x16,0xD1,0xEF,0x99,0xAD,0xC0,0x1F, +0xFB,0xCB,0xAE,0xBC,0x38,0x22,0x06,0x26,0x64,0xDA,0xDA,0x97,0x0E,0x3F,0x28,0x15, +0x44,0xA8,0x4F,0x00,0xCA,0xF0,0x9A,0xCC,0xCF,0x74,0x6A,0xB4,0x3E,0x3C,0xEB,0x95, +0xEC,0xB5,0xD3,0x5A,0xD8,0x81,0x99,0xE9,0x43,0x18,0x37,0xEB,0xB3,0xBB,0xD1,0x58, +0x62,0x41,0xF3,0x66,0xD2,0x8F,0xAA,0x78,0x95,0x54,0x20,0xC3,0x5A,0x2E,0x74,0x2B, +0xD5,0xD1,0xBE,0x18,0x69,0xC0,0xAC,0xD5,0xA4,0xCF,0x39,0xBA,0x51,0x84,0x03,0x65, +0xE9,0x62,0xC0,0x62,0xFE,0xD8,0x4D,0x55,0x96,0xE2,0xD0,0x11,0xFA,0x48,0x34,0x11, +0xEC,0x9E,0xED,0x05,0x1D,0xE4,0xC8,0xD6,0x1D,0x86,0xCB, +}; + +/* subject: Common Name: GTS Root R3, Organization: Google Trust Services LLC, Country: US */ +/* issuer: Common Name: GTS Root R3, Organization: Google Trust Services LLC, Country: US */ +/* link: https://crt.sh/?q=34d8a73ee208d9bcdb0d956520934b4e40e69482596e8b6f73c8426b010a6f48 */ +const unsigned char kCertificateWithFingerprint_34d8a73ee208d9bcdb0d956520934b4e40e69482596e8b6f73c8426b010a6f48_certificate[525]={ +0x30,0x82,0x02,0x09,0x30,0x82,0x01,0x8E,0xA0,0x03,0x02,0x01,0x02,0x02,0x0D,0x02, +0x03,0xE5,0xB8,0x82,0xEB,0x20,0xF8,0x25,0x27,0x6D,0x3D,0x66,0x30,0x0A,0x06,0x08, +0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x47,0x31,0x0B,0x30,0x09,0x06,0x03, +0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0A, +0x13,0x19,0x47,0x6F,0x6F,0x67,0x6C,0x65,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x53, +0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x20,0x4C,0x4C,0x43,0x31,0x14,0x30,0x12,0x06, +0x03,0x55,0x04,0x03,0x13,0x0B,0x47,0x54,0x53,0x20,0x52,0x6F,0x6F,0x74,0x20,0x52, +0x33,0x30,0x1E,0x17,0x0D,0x31,0x36,0x30,0x36,0x32,0x32,0x30,0x30,0x30,0x30,0x30, +0x30,0x5A,0x17,0x0D,0x33,0x36,0x30,0x36,0x32,0x32,0x30,0x30,0x30,0x30,0x30,0x30, +0x5A,0x30,0x47,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53, +0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0A,0x13,0x19,0x47,0x6F,0x6F,0x67,0x6C, +0x65,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73, +0x20,0x4C,0x4C,0x43,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x03,0x13,0x0B,0x47, +0x54,0x53,0x20,0x52,0x6F,0x6F,0x74,0x20,0x52,0x33,0x30,0x76,0x30,0x10,0x06,0x07, +0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05,0x2B,0x81,0x04,0x00,0x22,0x03,0x62, +0x00,0x04,0x1F,0x4F,0x33,0x87,0x33,0x29,0x8A,0xA1,0x84,0xDE,0xCB,0xC7,0x21,0x58, +0x41,0x89,0xEA,0x56,0x9D,0x2B,0x4B,0x85,0xC6,0x1D,0x4C,0x27,0xBC,0x7F,0x26,0x51, +0x72,0x6F,0xE2,0x9F,0xD6,0xA3,0xCA,0xCC,0x45,0x14,0x46,0x8B,0xAD,0xEF,0x7E,0x86, +0x8C,0xEC,0xB1,0x7E,0x2F,0xFF,0xA9,0x71,0x9D,0x18,0x84,0x45,0x04,0x41,0x55,0x6E, +0x2B,0xEA,0x26,0x7F,0xBB,0x90,0x01,0xE3,0x4B,0x19,0xBA,0xE4,0x54,0x96,0x45,0x09, +0xB1,0xD5,0x6C,0x91,0x44,0xAD,0x84,0x13,0x8E,0x9A,0x8C,0x0D,0x80,0x0C,0x32,0xF6, +0xE0,0x27,0xA3,0x42,0x30,0x40,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF, +0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16, +0x04,0x14,0xC1,0xF1,0x26,0xBA,0xA0,0x2D,0xAE,0x85,0x81,0xCF,0xD3,0xF1,0x2A,0x12, +0xBD,0xB8,0x0A,0x67,0xFD,0xBC,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04, +0x03,0x03,0x03,0x69,0x00,0x30,0x66,0x02,0x31,0x00,0xF6,0xE1,0x20,0x95,0x14,0x7B, +0x54,0xA3,0x90,0x16,0x11,0xBF,0x84,0xC8,0xEA,0x6F,0x6B,0x17,0x9E,0x1E,0x46,0x98, +0x20,0x9B,0x9F,0xD3,0x0D,0xD9,0xAC,0xD3,0x2F,0xCD,0x7C,0xF8,0x5B,0x2E,0x55,0xBB, +0xBF,0xDD,0x92,0xF7,0xA4,0x0C,0xDC,0x31,0xE1,0xA2,0x02,0x31,0x00,0xFC,0x97,0x66, +0x66,0xE5,0x43,0x16,0x13,0x83,0xDD,0xC7,0xDF,0x2F,0xBE,0x14,0x38,0xED,0x01,0xCE, +0xB1,0x17,0x1A,0x11,0x75,0xE9,0xBD,0x03,0x8F,0x26,0x7E,0x84,0xE5,0xC9,0x60,0xA6, +0x95,0xD7,0x54,0x59,0xB7,0xE7,0x11,0x2C,0x89,0xD4,0xB9,0xEE,0x17, +}; + +/* subject: Common Name: GTS Root R4, Organization: Google Trust Services LLC, Country: US */ +/* issuer: Common Name: GTS Root R4, Organization: Google Trust Services LLC, Country: US */ +/* link: https://crt.sh/?q=349dfa4058c5e263123b398ae795573c4e1313c83fe68f93556cd5e8031b3c7d */ +const unsigned char kCertificateWithFingerprint_349dfa4058c5e263123b398ae795573c4e1313c83fe68f93556cd5e8031b3c7d_certificate[525]={ +0x30,0x82,0x02,0x09,0x30,0x82,0x01,0x8E,0xA0,0x03,0x02,0x01,0x02,0x02,0x0D,0x02, +0x03,0xE5,0xC0,0x68,0xEF,0x63,0x1A,0x9C,0x72,0x90,0x50,0x52,0x30,0x0A,0x06,0x08, +0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,0x30,0x47,0x31,0x0B,0x30,0x09,0x06,0x03, +0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0A, +0x13,0x19,0x47,0x6F,0x6F,0x67,0x6C,0x65,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x53, +0x65,0x72,0x76,0x69,0x63,0x65,0x73,0x20,0x4C,0x4C,0x43,0x31,0x14,0x30,0x12,0x06, +0x03,0x55,0x04,0x03,0x13,0x0B,0x47,0x54,0x53,0x20,0x52,0x6F,0x6F,0x74,0x20,0x52, +0x34,0x30,0x1E,0x17,0x0D,0x31,0x36,0x30,0x36,0x32,0x32,0x30,0x30,0x30,0x30,0x30, +0x30,0x5A,0x17,0x0D,0x33,0x36,0x30,0x36,0x32,0x32,0x30,0x30,0x30,0x30,0x30,0x30, +0x5A,0x30,0x47,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53, +0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0A,0x13,0x19,0x47,0x6F,0x6F,0x67,0x6C, +0x65,0x20,0x54,0x72,0x75,0x73,0x74,0x20,0x53,0x65,0x72,0x76,0x69,0x63,0x65,0x73, +0x20,0x4C,0x4C,0x43,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x03,0x13,0x0B,0x47, +0x54,0x53,0x20,0x52,0x6F,0x6F,0x74,0x20,0x52,0x34,0x30,0x76,0x30,0x10,0x06,0x07, +0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x05,0x2B,0x81,0x04,0x00,0x22,0x03,0x62, +0x00,0x04,0xF3,0x74,0x73,0xA7,0x68,0x8B,0x60,0xAE,0x43,0xB8,0x35,0xC5,0x81,0x30, +0x7B,0x4B,0x49,0x9D,0xFB,0xC1,0x61,0xCE,0xE6,0xDE,0x46,0xBD,0x6B,0xD5,0x61,0x18, +0x35,0xAE,0x40,0xDD,0x73,0xF7,0x89,0x91,0x30,0x5A,0xEB,0x3C,0xEE,0x85,0x7C,0xA2, +0x40,0x76,0x3B,0xA9,0xC6,0xB8,0x47,0xD8,0x2A,0xE7,0x92,0x91,0x6A,0x73,0xE9,0xB1, +0x72,0x39,0x9F,0x29,0x9F,0xA2,0x98,0xD3,0x5F,0x5E,0x58,0x86,0x65,0x0F,0xA1,0x84, +0x65,0x06,0xD1,0xDC,0x8B,0xC9,0xC7,0x73,0xC8,0x8C,0x6A,0x2F,0xE5,0xC4,0xAB,0xD1, +0x1D,0x8A,0xA3,0x42,0x30,0x40,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF, +0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF, +0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16, +0x04,0x14,0x80,0x4C,0xD6,0xEB,0x74,0xFF,0x49,0x36,0xA3,0xD5,0xD8,0xFC,0xB5,0x3E, +0xC5,0x6A,0xF0,0x94,0x1D,0x8C,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04, +0x03,0x03,0x03,0x69,0x00,0x30,0x66,0x02,0x31,0x00,0xE8,0x40,0xFF,0x83,0xDE,0x03, +0xF4,0x9F,0xAE,0x1D,0x7A,0xA7,0x2E,0xB9,0xAF,0x4F,0xF6,0x83,0x1D,0x0E,0x2D,0x85, +0x01,0x1D,0xD1,0xD9,0x6A,0xEC,0x0F,0xC2,0xAF,0xC7,0x5E,0x56,0x5E,0x5C,0xD5,0x1C, +0x58,0x22,0x28,0x0B,0xF7,0x30,0xB6,0x2F,0xB1,0x7C,0x02,0x31,0x00,0xF0,0x61,0x3C, +0xA7,0xF4,0xA0,0x82,0xE3,0x21,0xD5,0x84,0x1D,0x73,0x86,0x9C,0x2D,0xAF,0xCA,0x34, +0x9B,0xF1,0x9F,0xB9,0x23,0x36,0xE2,0xBC,0x60,0x03,0x9D,0x80,0xB3,0x9A,0x56,0xC8, +0xE1,0xE2,0xBB,0x14,0x79,0xCA,0xCD,0x21,0xD4,0x94,0xB5,0x49,0x43, +}; + +/* subject: Common Name: GlobalSign, Organization: GlobalSign, Organizational Unit: GlobalSign ECC Root CA - R4 */ +/* issuer: Common Name: GlobalSign, Organization: GlobalSign, Organizational Unit: GlobalSign ECC Root CA - R4 */ +/* link: https://crt.sh/?q=b085d70b964f191a73e4af0d54ae7a0e07aafdaf9b71dd0862138ab7325a24a2 */ +const unsigned char kCertificateWithFingerprint_b085d70b964f191a73e4af0d54ae7a0e07aafdaf9b71dd0862138ab7325a24a2_certificate[480]={ +0x30,0x82,0x01,0xDC,0x30,0x82,0x01,0x83,0xA0,0x03,0x02,0x01,0x02,0x02,0x0D,0x02, +0x03,0xE5,0x7E,0xF5,0x3F,0x93,0xFD,0xA5,0x09,0x21,0xB2,0xA6,0x30,0x0A,0x06,0x08, +0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x02,0x30,0x50,0x31,0x24,0x30,0x22,0x06,0x03, +0x55,0x04,0x0B,0x13,0x1B,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x20, +0x45,0x43,0x43,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20,0x52,0x34, +0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61, +0x6C,0x53,0x69,0x67,0x6E,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0A, +0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x30,0x1E,0x17,0x0D,0x31,0x32, +0x31,0x31,0x31,0x33,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x33,0x38,0x30, +0x31,0x31,0x39,0x30,0x33,0x31,0x34,0x30,0x37,0x5A,0x30,0x50,0x31,0x24,0x30,0x22, +0x06,0x03,0x55,0x04,0x0B,0x13,0x1B,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67, +0x6E,0x20,0x45,0x43,0x43,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x20,0x2D,0x20, +0x52,0x34,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x47,0x6C,0x6F, +0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03, +0x13,0x0A,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x53,0x69,0x67,0x6E,0x30,0x59,0x30,0x13, +0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D, +0x03,0x01,0x07,0x03,0x42,0x00,0x04,0xB8,0xC6,0x79,0xD3,0x8F,0x6C,0x25,0x0E,0x9F, +0x2E,0x39,0x19,0x1C,0x03,0xA4,0xAE,0x9A,0xE5,0x39,0x07,0x09,0x16,0xCA,0x63,0xB1, +0xB9,0x86,0xF8,0x8A,0x57,0xC1,0x57,0xCE,0x42,0xFA,0x73,0xA1,0xF7,0x65,0x42,0xFF, +0x1E,0xC1,0x00,0xB2,0x6E,0x73,0x0E,0xFF,0xC7,0x21,0xE5,0x18,0xA4,0xAA,0xD9,0x71, +0x3F,0xA8,0xD4,0xB9,0xCE,0x8C,0x1D,0xA3,0x42,0x30,0x40,0x30,0x0E,0x06,0x03,0x55, +0x1D,0x0F,0x01,0x01,0xFF,0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x0F,0x06,0x03,0x55, +0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03, +0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x54,0xB0,0x7B,0xAD,0x45,0xB8,0xE2,0x40,0x7F, +0xFB,0x0A,0x6E,0xFB,0xBE,0x33,0xC9,0x3C,0xA3,0x84,0xD5,0x30,0x0A,0x06,0x08,0x2A, +0x86,0x48,0xCE,0x3D,0x04,0x03,0x02,0x03,0x47,0x00,0x30,0x44,0x02,0x20,0x22,0x4F, +0x74,0x72,0xB9,0x60,0xAF,0xF1,0xE6,0x9C,0xA0,0x16,0x05,0x50,0x5F,0xC3,0x5E,0x3B, +0x6E,0x61,0x74,0xEF,0xBE,0x01,0xC4,0xBE,0x18,0x48,0x59,0x61,0x82,0x32,0x02,0x20, +0x26,0x9D,0x54,0x63,0x40,0xDE,0x37,0x60,0x50,0xCF,0xC8,0xD8,0xED,0x9D,0x82,0xAE, +0x37,0x98,0xBC,0xA3,0x8F,0x4C,0x4C,0xA9,0x34,0x2B,0x6C,0xEF,0xFB,0x95,0x9B,0x26, +}; + +const unsigned char* const kSSLCertCertificateList[36]={ +kCertificateWithFingerprint_16af57a9f676b0ab126095aa5ebadef22ab31119d644ac95cd4b93dbf3f26aeb_certificate, +kCertificateWithFingerprint_3e9099b5015e8f486c00bcea9d111ee721faba355a89bcf1df69561e3dc6325c_certificate, +kCertificateWithFingerprint_7d05ebb682339f8c9451ee094eebfefa7953a114edb2f44949452fab7d2fc185_certificate, +kCertificateWithFingerprint_7e37cb8b4c47090cab36551ba6f45db840680fba166a952db100717f43053fc2_certificate, +kCertificateWithFingerprint_4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161_certificate, +kCertificateWithFingerprint_cb3ccbb76031e5e0138f8dd39a23f9de47ffc35e43c1144cea27d46a5ab1cb5f_certificate, +kCertificateWithFingerprint_31ad6648f8104138c738f39ea4320133393e3a18cc02296ef97c2ac9ef6731d0_certificate, +kCertificateWithFingerprint_7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf_certificate, +kCertificateWithFingerprint_552f7bdcf1a7af9e6ce672017f4f12abf77240c78e761ac203d1d9d20ac89988_certificate, +kCertificateWithFingerprint_73c176434f1bc6d5adf45b0e76e727287c8de57616c1e6e6141a2b2cbc7d8e4c_certificate, +kCertificateWithFingerprint_02ed0eb28c14da45165c566791700d6451d7fb56f0b2ab1d3b8eb070e56edff5_certificate, +kCertificateWithFingerprint_43df5774b03e7fef5fe40d931a7bedf1bb2e6b42738c4e6d3841103d3aa7f339_certificate, +kCertificateWithFingerprint_6dc47172e01cbcb0bf62580d895fe2b8ac9ad4f873801e0c10b9c837d21eb177_certificate, +kCertificateWithFingerprint_0376ab1d54c5f9803ce4b2e201a0ee7eef7b57b636e8a93c9b8d4860c96f5fa7_certificate, +kCertificateWithFingerprint_0a81ec5a929777f145904af38d5d509f66b5e2c58fcdb531058b0e17f3f0b41b_certificate, +kCertificateWithFingerprint_70a73f7f376b60074248904534b11482d5bf0e698ecc498df52577ebf2e93b9a_certificate, +kCertificateWithFingerprint_bd71fdf6da97e4cf62d1647add2581b07d79adf8397eb4ecba9c5e8488821423_certificate, +kCertificateWithFingerprint_ebd41040e4bb3ec742c9e381d31ef2a41a48b6685c96e7cef3c1df6cd4331c99_certificate, +kCertificateWithFingerprint_cbb522d7b7f127ad6a0113865bdf1cd4102e7d0759af635a7cf4720dc963c53b_certificate, +kCertificateWithFingerprint_179fbc148a3dd00fd24ea13458cc43bfa7f59c8182d783a513f6ebec100c8924_certificate, +kCertificateWithFingerprint_2cabeafe37d06ca22aba7391c0033d25982952c453647349763a3ab5ad6ccf69_certificate, +kCertificateWithFingerprint_45140b3247eb9cc8c5b4f0d7b53091f73292089e6e5a63e2749dd3aca9198eda_certificate, +kCertificateWithFingerprint_2ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f5_certificate, +kCertificateWithFingerprint_1465fa205397b876faa6f0a9958e5590e40fcc7faa4fb7c2c8677521fb5fb658_certificate, +kCertificateWithFingerprint_c3846bf24b9e93ca64274c0ec67c1ecc5e024ffcacd2d74019350e81fe546ae4_certificate, +kCertificateWithFingerprint_d7a7a0fb5d7e2731d771e9484ebcdef71d5f0c3e0a2948782bc83ee0ea699ef4_certificate, +kCertificateWithFingerprint_0c2cd63df7806fa399ede809116b575bf87989f06518f9808c860503178baf66_certificate, +kCertificateWithFingerprint_1793927a0614549789adce2f8f34f7f0b66d0f3ae3a3b84d21ec15dbba4fadc7_certificate, +kCertificateWithFingerprint_52f0e1c4e58ec629291b60317f074671b85d7ea80d5b07273463534b32b40234_certificate, +kCertificateWithFingerprint_4ff460d54b9c86dabfbcfc5712e0400d2bed3fbc4d4fbdaa86e06adcd2a9ad7a_certificate, +kCertificateWithFingerprint_e793c9b02fd8aa13e21c31228accb08119643b749c898964b1746d46c3d4cbd2_certificate, +kCertificateWithFingerprint_d947432abde7b7fa90fc2e6b59101b1280e0e1c7e4e40fa3c6887fff57a7f4cf_certificate, +kCertificateWithFingerprint_8d25cd97229dbf70356bda4eb3cc734031e24cf00fafcfd32dc76eb5841c7ea8_certificate, +kCertificateWithFingerprint_34d8a73ee208d9bcdb0d956520934b4e40e69482596e8b6f73c8426b010a6f48_certificate, +kCertificateWithFingerprint_349dfa4058c5e263123b398ae795573c4e1313c83fe68f93556cd5e8031b3c7d_certificate, +kCertificateWithFingerprint_b085d70b964f191a73e4af0d54ae7a0e07aafdaf9b71dd0862138ab7325a24a2_certificate, +}; + +const size_t kSSLCertCertificateSizeList[36]={ +891, +955, +922, +586, +947, +914, +579, +969, +1428, +1173, +765, +1090, +1070, +848, +848, +1354, +514, +889, +867, +546, +1415, +969, +993, +1043, +1028, +1078, +1057, +653, +1500, +659, +1506, +1371, +1371, +525, +525, +480, +}; + +// clang-format on + +#endif // RTC_BASE_SSL_ROOTS_H_ diff --git a/pkg/apm/webrtc/rtc_base/ssl_stream_adapter.h b/pkg/apm/webrtc/rtc_base/ssl_stream_adapter.h new file mode 100644 index 00000000..bc62f8ea --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/ssl_stream_adapter.h @@ -0,0 +1,332 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SSL_STREAM_ADAPTER_H_ +#define RTC_BASE_SSL_STREAM_ADAPTER_H_ + +#include +#include + +#include +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/field_trials_view.h" +#include "rtc_base/buffer.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/stream.h" + +namespace webrtc { + +// Constants for SSL profile. +constexpr int kTlsNullWithNullNull = 0; +constexpr int kSslCipherSuiteMaxValue = 0xFFFF; + +// Constants for SRTP profiles. +constexpr int kSrtpInvalidCryptoSuite = 0; +constexpr int kSrtpAes128CmSha1_80 = 0x0001; +constexpr int kSrtpAes128CmSha1_32 = 0x0002; +constexpr int kSrtpAeadAes128Gcm = 0x0007; +constexpr int kSrtpAeadAes256Gcm = 0x0008; +constexpr int kSrtpCryptoSuiteMaxValue = 0xFFFF; + +// Constants for SSL signature algorithms. +constexpr int kSslSignatureAlgorithmUnknown = 0; +constexpr int kSslSignatureAlgorithmMaxValue = 0xFFFF; + +// Names of SRTP profiles listed above. +// 128-bit AES with 80-bit SHA-1 HMAC. +extern const char kCsAesCm128HmacSha1_80[]; +// 128-bit AES with 32-bit SHA-1 HMAC. +extern const char kCsAesCm128HmacSha1_32[]; +// 128-bit AES GCM with 16 byte AEAD auth tag. +extern const char kCsAeadAes128Gcm[]; +// 256-bit AES GCM with 16 byte AEAD auth tag. +extern const char kCsAeadAes256Gcm[]; + +// Given the DTLS-SRTP protection profile ID, as defined in +// https://tools.ietf.org/html/rfc4568#section-6.2 , return the SRTP profile +// name, as defined in https://tools.ietf.org/html/rfc5764#section-4.1.2. +std::string SrtpCryptoSuiteToName(int crypto_suite); + +// Get key length and salt length for given crypto suite. Returns true for +// valid suites, otherwise false. +bool GetSrtpKeyAndSaltLengths(int crypto_suite, + int* key_length, + int* salt_length); + +// Returns true if the given crypto suite id uses a GCM cipher. +bool IsGcmCryptoSuite(int crypto_suite); + +// SSLStreamAdapter : A StreamInterfaceAdapter that does SSL/TLS. +// After SSL has been started, the stream will only open on successful +// SSL verification of certificates, and the communication is +// encrypted of course. +// +// This class was written with SSLAdapter as a starting point. It +// offers a similar interface, with two differences: there is no +// support for a restartable SSL connection, and this class has a +// peer-to-peer mode. +// +// The SSL library requires initialization and cleanup. Static method +// for doing this are in SSLAdapter. They should possibly be moved out +// to a neutral class. + +enum SSLRole { SSL_CLIENT, SSL_SERVER }; +enum SSLMode { SSL_MODE_TLS, SSL_MODE_DTLS }; + +// TODO bugs.webrtc.org/40644300 remove unused legacy constants. +enum SSLProtocolVersion { + SSL_PROTOCOL_NOT_GIVEN = -1, + SSL_PROTOCOL_TLS_10 = 0, // Deprecated and no longer supported. + SSL_PROTOCOL_TLS_11 = 1, // Deprecated and no longer supported. + SSL_PROTOCOL_TLS_12 = 2, + SSL_PROTOCOL_TLS_13 = 3, + SSL_PROTOCOL_DTLS_10 = 1, // Deprecated and no longer supported. + SSL_PROTOCOL_DTLS_12 = SSL_PROTOCOL_TLS_12, + SSL_PROTOCOL_DTLS_13 = SSL_PROTOCOL_TLS_13, +}; + +// Versions returned from BoringSSL. +const uint16_t kDtls10VersionBytes = 0xfeff; +const uint16_t kDtls12VersionBytes = 0xfefd; +const uint16_t kDtls13VersionBytes = 0xfefc; + +enum class SSLPeerCertificateDigestError { + NONE, + UNKNOWN_ALGORITHM, + INVALID_LENGTH, + VERIFICATION_FAILED, +}; + +// Errors for Read -- in the high range so no conflict with OpenSSL. +enum { SSE_MSG_TRUNC = 0xff0001 }; + +// Used to send back UMA histogram value. Logged when Dtls handshake fails. +enum class SSLHandshakeError { UNKNOWN, INCOMPATIBLE_CIPHERSUITE, MAX_VALUE }; + +class SSLStreamAdapter : public StreamInterface { + public: + // Instantiate an SSLStreamAdapter wrapping the given stream, + // (using the selected implementation for the platform). + // Caller is responsible for freeing the returned object. + static std::unique_ptr Create( + std::unique_ptr stream, + absl::AnyInvocable handshake_error = + nullptr, + const FieldTrialsView* field_trials = nullptr); + + SSLStreamAdapter() = default; + ~SSLStreamAdapter() override = default; + + // Specify our SSL identity: key and certificate. SSLStream takes ownership + // of the SSLIdentity object and will free it when appropriate. Should be + // called no more than once on a given SSLStream instance. + virtual void SetIdentity(std::unique_ptr identity) = 0; + virtual SSLIdentity* GetIdentityForTesting() const = 0; + + // Call this to indicate that we are to play the server role (or client role, + // if the default argument is replaced by SSL_CLIENT). + // The default argument is for backward compatibility. + // TODO(ekr@rtfm.com): rename this SetRole to reflect its new function + virtual void SetServerRole(SSLRole role = SSL_SERVER) = 0; + + [[deprecated("Only DTLS is supported by the stream adapter")]] virtual void + SetMode(SSLMode mode) = 0; + + // Set maximum supported protocol version. The highest version supported by + // both ends will be used for the connection, i.e. if one party supports + // DTLS 1.0 and the other DTLS 1.2, DTLS 1.0 will be used. + // If requested version is not supported by underlying crypto library, the + // next lower will be used. + virtual void SetMaxProtocolVersion(SSLProtocolVersion version) = 0; + + // Set the initial retransmission timeout for DTLS messages. When the timeout + // expires, the message gets retransmitted and the timeout is exponentially + // increased. + // This should only be called before StartSSL(). + virtual void SetInitialRetransmissionTimeout(int timeout_ms) = 0; + + // Set MTU to be used for next handshake flight. + virtual void SetMTU(int mtu) = 0; + + // StartSSL starts negotiation with a peer, whose certificate is verified + // using the certificate digest. Generally, SetIdentity() and possibly + // SetServerRole() should have been called before this. + // SetPeerCertificateDigest() must also be called. It may be called after + // StartSSLWithPeer() but must be called before the underlying stream opens. + // + // Use of the stream prior to calling StartSSL will pass data in clear text. + // Calling StartSSL causes SSL negotiation to begin as soon as possible: right + // away if the underlying wrapped stream is already opened, or else as soon as + // it opens. + // + // StartSSL returns a negative error code on failure. Returning 0 means + // success so far, but negotiation is probably not complete and will continue + // asynchronously. In that case, the exposed stream will open after + // successful negotiation and verification, or an SE_CLOSE event will be + // raised if negotiation fails. + virtual int StartSSL() = 0; + + // Specify the digest of the certificate that our peer is expected to use. + // Only this certificate will be accepted during SSL verification. The + // certificate is assumed to have been obtained through some other secure + // channel (such as the signaling channel). This must specify the terminal + // certificate, not just a CA. SSLStream makes a copy of the digest value. + // + // Returns SSLPeerCertificateDigestError::NONE if successful. + virtual SSLPeerCertificateDigestError SetPeerCertificateDigest( + absl::string_view digest_alg, + ArrayView digest_val) = 0; + [[deprecated( + "Use SetPeerCertificateDigest with ArrayView instead")]] virtual bool + SetPeerCertificateDigest(absl::string_view digest_alg, + const unsigned char* digest_val, + size_t digest_len, + SSLPeerCertificateDigestError* error = nullptr); + + // Retrieves the peer's certificate chain including leaf certificate, if a + // connection has been established. + virtual std::unique_ptr GetPeerSSLCertChain() const = 0; + + // Retrieves the IANA registration id of the cipher suite used for the + // connection (e.g. 0x2F for "TLS_RSA_WITH_AES_128_CBC_SHA"). + virtual bool GetSslCipherSuite(int* cipher_suite) const = 0; + // Returns the name of the cipher suite used for the DTLS transport, + // as defined in the "Description" column of the IANA cipher suite registry. + virtual std::optional GetTlsCipherSuiteName() const = 0; + + // Retrieves the enum value for SSL version. + // Will return -1 until the version has been negotiated. + [[deprecated("Use GetSslVersionBytes")]] virtual SSLProtocolVersion + GetSslVersion() const = 0; + // Retrieves the 2-byte version from the TLS protocol. + // Will return false until the version has been negotiated. + virtual bool GetSslVersionBytes(int* version) const = 0; + + // Key Exporter interface from RFC 5705 + virtual bool ExportSrtpKeyingMaterial( + ZeroOnFreeBuffer& keying_material) = 0; + + // Returns the signature algorithm or 0 if not applicable. + virtual uint16_t GetPeerSignatureAlgorithm() const = 0; + + // DTLS-SRTP interface + virtual bool SetDtlsSrtpCryptoSuites( + const std::vector& crypto_suites) = 0; + virtual bool GetDtlsSrtpCryptoSuite(int* crypto_suite) const = 0; + + // Returns true if a TLS connection has been established. + // The only difference between this and "GetState() == SE_OPEN" is that if + // the peer certificate digest hasn't been verified, the state will still be + // SS_OPENING but IsTlsConnected should return true. + virtual bool IsTlsConnected() = 0; + + // Capabilities testing. + // Used to have "DTLS supported", "DTLS-SRTP supported" etc. methods, but now + // that's assumed. + static bool IsBoringSsl(); + + // Returns true iff the supplied cipher is deemed to be strong. + // TODO(torbjorng): Consider removing the KeyType argument. + static bool IsAcceptableCipher(int cipher, KeyType key_type); + static bool IsAcceptableCipher(absl::string_view cipher, KeyType key_type); + + //////////////////////////////////////////////////////////////////////////// + // Testing only member functions + //////////////////////////////////////////////////////////////////////////// + + // Use our timeutils.h source of timing in BoringSSL, allowing us to test + // using a fake clock. + static void EnableTimeCallbackForTesting(); + + // Return max DTLS SSLProtocolVersion supported by implementation. + static SSLProtocolVersion GetMaxSupportedDTLSProtocolVersion(); + + // Deprecated. Do not use this API outside of testing. + // Do not set this to false outside of testing. + void SetClientAuthEnabledForTesting(bool enabled) { + client_auth_enabled_ = enabled; + } + + // Deprecated. Do not use this API outside of testing. + // Returns true by default, else false if explicitly set to disable client + // authentication. + bool GetClientAuthEnabled() const { return client_auth_enabled_; } + + // Return number of times DTLS retransmission has been triggered. + // Used for testing (and maybe put into stats?). + virtual int GetRetransmissionCount() const = 0; + + // Return the the ID of the group used by the adapters most recently + // completed handshake, or 0 if not applicable (e.g. before the handshake). + virtual uint16_t GetSslGroupIdForTesting() const = 0; + + private: + // If true (default), the client is required to provide a certificate during + // handshake. If no certificate is given, handshake fails. This applies to + // server mode only. + bool client_auth_enabled_ = true; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::GetSrtpKeyAndSaltLengths; +using ::webrtc::IsGcmCryptoSuite; +using ::webrtc::kCsAeadAes128Gcm; +using ::webrtc::kCsAeadAes256Gcm; +using ::webrtc::kCsAesCm128HmacSha1_32; +using ::webrtc::kCsAesCm128HmacSha1_80; +using ::webrtc::kDtls10VersionBytes; +using ::webrtc::kDtls12VersionBytes; +using ::webrtc::kDtls13VersionBytes; +using ::webrtc::kSrtpAeadAes128Gcm; +using ::webrtc::kSrtpAeadAes256Gcm; +using ::webrtc::kSrtpAes128CmSha1_32; +using ::webrtc::kSrtpAes128CmSha1_80; +using ::webrtc::kSrtpCryptoSuiteMaxValue; +using ::webrtc::kSrtpInvalidCryptoSuite; +using ::webrtc::kSslCipherSuiteMaxValue; +using ::webrtc::kSslSignatureAlgorithmMaxValue; +using ::webrtc::kSslSignatureAlgorithmUnknown; +using ::webrtc::kTlsNullWithNullNull; +using ::webrtc::SrtpCryptoSuiteToName; +using ::webrtc::SSE_MSG_TRUNC; +using ::webrtc::SSL_CLIENT; +using ::webrtc::SSL_MODE_DTLS; +using ::webrtc::SSL_MODE_TLS; +using ::webrtc::SSL_PROTOCOL_DTLS_10; +using ::webrtc::SSL_PROTOCOL_DTLS_12; +using ::webrtc::SSL_PROTOCOL_DTLS_13; +using ::webrtc::SSL_PROTOCOL_NOT_GIVEN; +using ::webrtc::SSL_PROTOCOL_TLS_10; +using ::webrtc::SSL_PROTOCOL_TLS_11; +using ::webrtc::SSL_PROTOCOL_TLS_12; +using ::webrtc::SSL_PROTOCOL_TLS_13; +using ::webrtc::SSL_SERVER; +using ::webrtc::SSLHandshakeError; +using ::webrtc::SSLMode; +using ::webrtc::SSLPeerCertificateDigestError; +using ::webrtc::SSLProtocolVersion; +using ::webrtc::SSLRole; +using ::webrtc::SSLStreamAdapter; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SSL_STREAM_ADAPTER_H_ diff --git a/pkg/apm/webrtc/rtc_base/stream.h b/pkg/apm/webrtc/rtc_base/stream.h new file mode 100644 index 00000000..9bee2be0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/stream.h @@ -0,0 +1,174 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_STREAM_H_ +#define RTC_BASE_STREAM_H_ + +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "api/array_view.h" +#include "api/sequence_checker.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +/////////////////////////////////////////////////////////////////////////////// +// StreamInterface is a generic asynchronous stream interface, supporting read, +// write, and close operations, and asynchronous signalling of state changes. +// The interface is designed with file, memory, and socket implementations in +// mind. Some implementations offer extended operations, such as seeking. +/////////////////////////////////////////////////////////////////////////////// + +// The following enumerations are declared outside of the StreamInterface +// class for brevity in use. + +// The SS_OPENING state indicates that the stream will signal open or closed +// in the future. +enum StreamState { SS_CLOSED, SS_OPENING, SS_OPEN }; + +// Stream read/write methods return this value to indicate various success +// and failure conditions described below. +enum StreamResult { SR_ERROR, SR_SUCCESS, SR_BLOCK, SR_EOS }; + +// StreamEvents are used to asynchronously signal state transitionss. The flags +// may be combined. +// SE_OPEN: The stream has transitioned to the SS_OPEN state +// SE_CLOSE: The stream has transitioned to the SS_CLOSED state +// SE_READ: Data is available, so Read is likely to not return SR_BLOCK +// SE_WRITE: Data can be written, so Write is likely to not return SR_BLOCK +enum StreamEvent { SE_OPEN = 1, SE_READ = 2, SE_WRITE = 4, SE_CLOSE = 8 }; + +class RTC_EXPORT StreamInterface { + public: + virtual ~StreamInterface() {} + + StreamInterface(const StreamInterface&) = delete; + StreamInterface& operator=(const StreamInterface&) = delete; + + virtual StreamState GetState() const = 0; + + // Read attempts to fill buffer of size buffer_len. Write attempts to send + // data_len bytes stored in data. The variables read and write are set only + // on SR_SUCCESS (see below). Likewise, error is only set on SR_ERROR. + // Read and Write return a value indicating: + // SR_ERROR: an error occurred, which is returned in a non-null error + // argument. Interpretation of the error requires knowledge of the + // stream's concrete type, which limits its usefulness. + // SR_SUCCESS: some number of bytes were successfully written, which is + // returned in a non-null read/write argument. + // SR_BLOCK: the stream is in non-blocking mode, and the operation would + // block, or the stream is in SS_OPENING state. + // SR_EOS: the end-of-stream has been reached, or the stream is in the + // SS_CLOSED state. + + virtual StreamResult Read(ArrayView buffer, + size_t& read, + int& error) = 0; + virtual StreamResult Write(ArrayView data, + size_t& written, + int& error) = 0; + + // Attempt to transition to the SS_CLOSED state. SE_CLOSE will not be + // signalled as a result of this call. + virtual void Close() = 0; + + // Streams may issue one or more events to indicate state changes to a + // provided callback. + // The first argument is a bit-wise combination of `StreamEvent` flags. + // If SE_CLOSE is set, then the second argument is the associated error code. + // Otherwise, the value of the second parameter is undefined and should be + // set to 0. + // Note: Not all streams support callbacks. However, SS_OPENING and + // SR_BLOCK returned from member functions imply that certain callbacks will + // be made in the future. + void SetEventCallback(absl::AnyInvocable callback) { + RTC_DCHECK_RUN_ON(&callback_sequence_); + RTC_DCHECK(!callback_ || !callback); + callback_ = std::move(callback); + } + + // TODO(bugs.webrtc.org/11943): Remove after updating downstream code. + sigslot::signal3 SignalEvent + [[deprecated("Use SetEventCallback instead")]]; + + // Return true if flush is successful. + virtual bool Flush(); + + // + // CONVENIENCE METHODS + // + // These methods are implemented in terms of other methods, for convenience. + // + + // WriteAll is a helper function which repeatedly calls Write until all the + // data is written, or something other than SR_SUCCESS is returned. Note that + // unlike Write, the argument 'written' is always set, and may be non-zero + // on results other than SR_SUCCESS. The remaining arguments have the + // same semantics as Write. + StreamResult WriteAll(ArrayView data, + size_t& written, + int& error); + + protected: + StreamInterface(); + + // Utility function for derived classes. + void FireEvent(int stream_events, int err) RTC_RUN_ON(&callback_sequence_) { + if (callback_) { + callback_(stream_events, err); + } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(tommi): This is for backwards compatibility only while `SignalEvent` + // is being replaced by `SetEventCallback`. + SignalEvent(this, stream_events, err); +#pragma clang diagnostic pop + } + + RTC_NO_UNIQUE_ADDRESS SequenceChecker callback_sequence_{ + webrtc::SequenceChecker::kDetached}; + + private: + absl::AnyInvocable callback_ + RTC_GUARDED_BY(&callback_sequence_) = nullptr; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::SE_CLOSE; +using ::webrtc::SE_OPEN; +using ::webrtc::SE_READ; +using ::webrtc::SE_WRITE; +using ::webrtc::SR_BLOCK; +using ::webrtc::SR_EOS; +using ::webrtc::SR_ERROR; +using ::webrtc::SR_SUCCESS; +using ::webrtc::SS_CLOSED; +using ::webrtc::SS_OPEN; +using ::webrtc::SS_OPENING; +using ::webrtc::StreamEvent; +using ::webrtc::StreamInterface; +using ::webrtc::StreamResult; +using ::webrtc::StreamState; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_STREAM_H_ diff --git a/pkg/apm/webrtc/rtc_base/string_encode.cc b/pkg/apm/webrtc/rtc_base/string_encode.cc new file mode 100644 index 00000000..452b7d62 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/string_encode.cc @@ -0,0 +1,202 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/string_encode.h" + +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "rtc_base/arraysize.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +///////////////////////////////////////////////////////////////////////////// +// String Encoding Utilities +///////////////////////////////////////////////////////////////////////////// + +namespace { +const char HEX[] = "0123456789abcdef"; + +// Convert an unsigned value from 0 to 15 to the hex character equivalent... +char hex_encode(unsigned char val) { + RTC_DCHECK_LT(val, 16); + return (val < 16) ? HEX[val] : '!'; +} + +// ...and vice-versa. +bool hex_decode(char ch, unsigned char* val) { + if ((ch >= '0') && (ch <= '9')) { + *val = ch - '0'; + } else if ((ch >= 'A') && (ch <= 'F')) { + *val = (ch - 'A') + 10; + } else if ((ch >= 'a') && (ch <= 'f')) { + *val = (ch - 'a') + 10; + } else { + return false; + } + return true; +} + +size_t hex_encode_output_length(size_t srclen, char delimiter) { + return delimiter && srclen > 0 ? (srclen * 3 - 1) : (srclen * 2); +} + +// hex_encode shows the hex representation of binary data in ascii, with +// `delimiter` between bytes, or none if `delimiter` == 0. +void hex_encode_with_delimiter(char* buffer, + absl::string_view source, + char delimiter) { + RTC_DCHECK(buffer); + + // Init and check bounds. + const unsigned char* bsource = + reinterpret_cast(source.data()); + size_t srcpos = 0, bufpos = 0; + + size_t srclen = source.length(); + while (srcpos < srclen) { + unsigned char ch = bsource[srcpos++]; + buffer[bufpos] = hex_encode((ch >> 4) & 0xF); + buffer[bufpos + 1] = hex_encode((ch) & 0xF); + bufpos += 2; + + // Don't write a delimiter after the last byte. + if (delimiter && (srcpos < srclen)) { + buffer[bufpos] = delimiter; + ++bufpos; + } + } +} + +} // namespace + +std::string hex_encode(absl::string_view str) { + return hex_encode_with_delimiter(str, 0); +} + +std::string hex_encode_with_delimiter(absl::string_view source, + char delimiter) { + std::string s(hex_encode_output_length(source.length(), delimiter), 0); + hex_encode_with_delimiter(&s[0], source, delimiter); + return s; +} + +size_t hex_decode_with_delimiter(ArrayView cbuffer, + absl::string_view source, + char delimiter) { + if (cbuffer.empty()) + return 0; + + // Init and bounds check. + unsigned char* bbuffer = reinterpret_cast(cbuffer.data()); + size_t srcpos = 0, bufpos = 0; + size_t srclen = source.length(); + + size_t needed = (delimiter) ? (srclen + 1) / 3 : srclen / 2; + if (cbuffer.size() < needed) + return 0; + + while (srcpos < srclen) { + if ((srclen - srcpos) < 2) { + // This means we have an odd number of bytes. + return 0; + } + + unsigned char h1, h2; + if (!hex_decode(source[srcpos], &h1) || + !hex_decode(source[srcpos + 1], &h2)) + return 0; + + bbuffer[bufpos++] = (h1 << 4) | h2; + srcpos += 2; + + // Remove the delimiter if needed. + if (delimiter && (srclen - srcpos) > 1) { + if (source[srcpos] != delimiter) + return 0; + ++srcpos; + } + } + + return bufpos; +} + +size_t hex_decode(ArrayView buffer, absl::string_view source) { + return hex_decode_with_delimiter(buffer, source, 0); +} + +size_t tokenize(absl::string_view source, + char delimiter, + std::vector* fields) { + fields->clear(); + size_t last = 0; + for (size_t i = 0; i < source.length(); ++i) { + if (source[i] == delimiter) { + if (i != last) { + fields->emplace_back(source.substr(last, i - last)); + } + last = i + 1; + } + } + if (last != source.length()) { + fields->emplace_back(source.substr(last, source.length() - last)); + } + return fields->size(); +} + +bool tokenize_first(absl::string_view source, + const char delimiter, + std::string* token, + std::string* rest) { + // Find the first delimiter + size_t left_pos = source.find(delimiter); + if (left_pos == absl::string_view::npos) { + return false; + } + + // Look for additional occurrances of delimiter. + size_t right_pos = left_pos + 1; + while (right_pos < source.size() && source[right_pos] == delimiter) { + right_pos++; + } + + *token = std::string(source.substr(0, left_pos)); + *rest = std::string(source.substr(right_pos)); + return true; +} + +std::vector split(absl::string_view source, char delimiter) { + std::vector fields; + size_t last = 0; + for (size_t i = 0; i < source.length(); ++i) { + if (source[i] == delimiter) { + fields.push_back(source.substr(last, i - last)); + last = i + 1; + } + } + fields.push_back(source.substr(last)); + return fields; +} + +bool FromString(absl::string_view s, bool* b) { + if (s == "false") { + *b = false; + return true; + } + if (s == "true") { + *b = true; + return true; + } + return false; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/string_encode.h b/pkg/apm/webrtc/rtc_base/string_encode.h new file mode 100644 index 00000000..4d6fc0c0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/string_encode.h @@ -0,0 +1,151 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_STRING_ENCODE_H_ +#define RTC_BASE_STRING_ENCODE_H_ + +#include + +#include +#include +#include +#include + +#include "absl/base/macros.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/string_to_number.h" +#include "rtc_base/strings/string_format.h" + +namespace webrtc { + +inline std::string BoolToString(bool b) { + return b ? "true" : "false"; +} + +std::string hex_encode(absl::string_view str); +std::string hex_encode_with_delimiter(absl::string_view source, char delimiter); + +// hex_decode converts ascii hex to binary. +size_t hex_decode(ArrayView buffer, absl::string_view source); + +// hex_decode, assuming that there is a delimiter between every byte +// pair. +// `delimiter` == 0 means no delimiter +// If the buffer is too short or the data is invalid, we return 0. +size_t hex_decode_with_delimiter(ArrayView buffer, + absl::string_view source, + char delimiter); + +// Splits the source string into multiple fields separated by delimiter, +// with duplicates of delimiter creating empty fields. Empty input produces a +// single, empty, field. +std::vector split(absl::string_view source, char delimiter); + +// Splits the source string into multiple fields separated by delimiter, +// with duplicates of delimiter ignored. Trailing delimiter ignored. +size_t tokenize(absl::string_view source, + char delimiter, + std::vector* fields); + +// Extract the first token from source as separated by delimiter, with +// duplicates of delimiter ignored. Return false if the delimiter could not be +// found, otherwise return true. +bool tokenize_first(absl::string_view source, + char delimiter, + std::string* token, + std::string* rest); + +// Versions that behave differently from StrCat + +// Versions not supported by StrCat: + +template ::value && + !std::is_same::value, + int>::type = 0> +static bool FromString(absl::string_view s, T* t) { + RTC_DCHECK(t); + std::optional result = webrtc::StringToNumber(s); + + if (result) + *t = *result; + + return result.has_value(); +} + +bool FromString(absl::string_view s, bool* b); + +template +static inline T FromString(absl::string_view str) { + T val; + FromString(str, &val); + return val; +} + +////////////////////////////////////////////////////////////////////// + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::FromString; +using ::webrtc::hex_decode; +using ::webrtc::hex_decode_with_delimiter; +using ::webrtc::hex_encode; +using ::webrtc::hex_encode_with_delimiter; +using ::webrtc::split; +using ::webrtc::tokenize; +using ::webrtc::tokenize_first; + +namespace internal { +template +struct is_absl_strcat_callable : std::false_type {}; + +template +struct is_absl_strcat_callable< + T, + std::void_t()))>> : std::true_type {}; +} // namespace internal + +template +ABSL_DEPRECATE_AND_INLINE() +inline auto ToString(T value) -> + typename std::enable_if && + internal::is_absl_strcat_callable::value, + std::string>::type { + return absl::StrCat(value); +} + +template +ABSL_DEPRECATE_AND_INLINE() +inline auto ToString(T p) -> + typename std::enable_if::value && + std::is_pointer::value, + std::string>::type { + return webrtc::StringFormat("%p", p); +} + +template +ABSL_DEPRECATE_AND_INLINE() +inline auto ToString(T value) -> + typename std::enable_if && std::is_same_v, + std::string>::type { + return webrtc::BoolToString(value); +} + +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_STRING_ENCODE_H__ diff --git a/pkg/apm/webrtc/rtc_base/string_to_number.cc b/pkg/apm/webrtc/rtc_base/string_to_number.cc new file mode 100644 index 00000000..e88c97a7 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/string_to_number.cc @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#include "rtc_base/string_to_number.h" + +#include + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace string_to_number_internal { + +std::optional ParseSigned(absl::string_view str, int base) { + if (str.empty()) + return std::nullopt; + + if (isdigit(static_cast(str[0])) || str[0] == '-') { + std::string str_str(str); + char* end = nullptr; + errno = 0; + const signed_type value = std::strtoll(str_str.c_str(), &end, base); + // Check for errors and also make sure that there were no embedded nuls in + // the input string. + if (end == str_str.c_str() + str_str.size() && errno == 0) { + return value; + } + } + return std::nullopt; +} + +std::optional ParseUnsigned(absl::string_view str, int base) { + if (str.empty()) + return std::nullopt; + + if (isdigit(static_cast(str[0])) || str[0] == '-') { + std::string str_str(str); + // Explicitly discard negative values. std::strtoull parsing causes unsigned + // wraparound. We cannot just reject values that start with -, though, since + // -0 is perfectly fine, as is -0000000000000000000000000000000. + const bool is_negative = str[0] == '-'; + char* end = nullptr; + errno = 0; + const unsigned_type value = std::strtoull(str_str.c_str(), &end, base); + // Check for errors and also make sure that there were no embedded nuls in + // the input string. + if (end == str_str.c_str() + str_str.size() && errno == 0 && + (value == 0 || !is_negative)) { + return value; + } + } + return std::nullopt; +} + +template +T StrToT(const char* str, char** str_end); + +template <> +inline float StrToT(const char* str, char** str_end) { + return std::strtof(str, str_end); +} + +template <> +inline double StrToT(const char* str, char** str_end) { + return std::strtod(str, str_end); +} + +template <> +inline long double StrToT(const char* str, char** str_end) { + return std::strtold(str, str_end); +} + +template +std::optional ParseFloatingPoint(absl::string_view str) { + if (str.empty()) + return std::nullopt; + + if (str[0] == '\0') + return std::nullopt; + std::string str_str(str); + char* end = nullptr; + errno = 0; + const T value = StrToT(str_str.c_str(), &end); + if (end == str_str.c_str() + str_str.size() && errno == 0) { + return value; + } + return std::nullopt; +} + +template std::optional ParseFloatingPoint(absl::string_view str); +template std::optional ParseFloatingPoint(absl::string_view str); +template std::optional ParseFloatingPoint(absl::string_view str); + +} // namespace string_to_number_internal +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/string_to_number.h b/pkg/apm/webrtc/rtc_base/string_to_number.h new file mode 100644 index 00000000..a66473b8 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/string_to_number.h @@ -0,0 +1,113 @@ +/* + * 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 RTC_BASE_STRING_TO_NUMBER_H_ +#define RTC_BASE_STRING_TO_NUMBER_H_ + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" + +namespace webrtc { + +// This file declares a family of functions to parse integers from strings. +// The standard C library functions either fail to indicate errors (atoi, etc.) +// or are a hassle to work with (strtol, sscanf, etc.). The standard C++ library +// functions (std::stoi, etc.) indicate errors by throwing exceptions, which +// are disabled in WebRTC. +// +// Integers are parsed using: +// std::optional StringToNumber(absl::string_view str, +// int base = 10); +// +// These functions parse a value from the beginning of a string into one of the +// fundamental integer types, or returns an empty Optional if parsing +// failed. Values outside of the range supported by the type will be +// rejected. The strings must begin with a digit or a minus sign. No leading +// space nor trailing contents are allowed. +// By setting base to 0, one of octal, decimal or hexadecimal will be +// detected from the string's prefix (0, nothing or 0x, respectively). +// If non-zero, base can be set to a value between 2 and 36 inclusively. + +namespace string_to_number_internal { +// These must be (unsigned) long long, to match the signature of strto(u)ll. +using unsigned_type = unsigned long long; // NOLINT(runtime/int) +using signed_type = long long; // NOLINT(runtime/int) + +std::optional ParseSigned(absl::string_view str, int base); +std::optional ParseUnsigned(absl::string_view str, int base); + +template +std::optional ParseFloatingPoint(absl::string_view str); +} // namespace string_to_number_internal + +template +typename std::enable_if::value && std::is_signed::value, + std::optional>::type +StringToNumber(absl::string_view str, int base = 10) { + using string_to_number_internal::signed_type; + static_assert( + std::numeric_limits::max() <= + std::numeric_limits::max() && + std::numeric_limits::lowest() >= + std::numeric_limits::lowest(), + "StringToNumber only supports signed integers as large as long long int"); + std::optional value = + string_to_number_internal::ParseSigned(str, base); + if (value && *value >= std::numeric_limits::lowest() && + *value <= std::numeric_limits::max()) { + return static_cast(*value); + } + return std::nullopt; +} + +template +typename std::enable_if::value && + std::is_unsigned::value, + std::optional>::type +StringToNumber(absl::string_view str, int base = 10) { + using string_to_number_internal::unsigned_type; + static_assert(std::numeric_limits::max() <= + std::numeric_limits::max(), + "StringToNumber only supports unsigned integers as large as " + "unsigned long long int"); + std::optional value = + string_to_number_internal::ParseUnsigned(str, base); + if (value && *value <= std::numeric_limits::max()) { + return static_cast(*value); + } + return std::nullopt; +} + +template +typename std::enable_if::value, + std::optional>::type +StringToNumber(absl::string_view str, int /* base */ = 10) { + static_assert( + std::numeric_limits::max() <= std::numeric_limits::max(), + "StringToNumber only supports floating-point numbers as large " + "as long double"); + return string_to_number_internal::ParseFloatingPoint(str); +} + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::StringToNumber; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_STRING_TO_NUMBER_H_ diff --git a/pkg/apm/webrtc/rtc_base/string_utils.cc b/pkg/apm/webrtc/rtc_base/string_utils.cc new file mode 100644 index 00000000..9e6baffc --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/string_utils.cc @@ -0,0 +1,42 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/string_utils.h" + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" + +namespace webrtc { + +size_t strcpyn(char* buffer, size_t buflen, absl::string_view source) { + if (buflen <= 0) + return 0; + + size_t srclen = source.length(); + if (srclen >= buflen) { + srclen = buflen - 1; + } + memcpy(buffer, source.data(), srclen); + buffer[srclen] = 0; + return srclen; +} + +std::string ToHex(const int i) { + char buffer[50]; + snprintf(buffer, sizeof(buffer), "%x", i); + + return std::string(buffer); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/string_utils.h b/pkg/apm/webrtc/rtc_base/string_utils.h new file mode 100644 index 00000000..ca2607b1 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/string_utils.h @@ -0,0 +1,147 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_STRING_UTILS_H_ +#define RTC_BASE_STRING_UTILS_H_ + +#include +#include + +#include "absl/strings/string_view.h" + +#if defined(WEBRTC_WIN) +#include +#include +#include + +#endif // WEBRTC_WIN + +#if defined(WEBRTC_POSIX) +#include +#endif // WEBRTC_POSIX + +#include + +namespace webrtc { + +const size_t SIZE_UNKNOWN = static_cast(-1); + +// An absl::string_view comparator functor for use with container types such as +// std::map that support heterogenous lookup. +// +// Example usage: +// std::map my_map; +struct AbslStringViewCmp { + using is_transparent = void; + bool operator()(absl::string_view a, absl::string_view b) const { + return a < b; + } +}; + +// Safe version of strncpy that always nul-terminate. +size_t strcpyn(char* buffer, size_t buflen, absl::string_view source); + +/////////////////////////////////////////////////////////////////////////////// +// UTF helpers (Windows only) +/////////////////////////////////////////////////////////////////////////////// + +#if defined(WEBRTC_WIN) + +inline std::wstring ToUtf16(const char* utf8, size_t len) { + if (len == 0) + return std::wstring(); + int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), + nullptr, 0); + std::wstring ws(len16, 0); + ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), &*ws.begin(), + len16); + return ws; +} + +inline std::wstring ToUtf16(absl::string_view str) { + return ToUtf16(str.data(), str.length()); +} + +inline std::string ToUtf8(const wchar_t* wide, size_t len) { + if (len == 0) + return std::string(); + int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), + nullptr, 0, nullptr, nullptr); + std::string ns(len8, 0); + ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), &*ns.begin(), + len8, nullptr, nullptr); + return ns; +} + +inline std::string ToUtf8(const wchar_t* wide) { + return ToUtf8(wide, wcslen(wide)); +} + +inline std::string ToUtf8(const std::wstring& wstr) { + return ToUtf8(wstr.data(), wstr.length()); +} + +#endif // WEBRTC_WIN + +// TODO(jonasolsson): replace with absl::Hex when that becomes available. +std::string ToHex(int i); + +// CompileTimeString comprises of a string-like object which can be used as a +// regular const char* in compile time and supports concatenation. Useful for +// concatenating constexpr strings in for example macro declarations. +namespace rtc_base_string_utils_internal { +template +struct CompileTimeString { + char string[NPlus1] = {0}; + constexpr CompileTimeString() = default; + template + explicit constexpr CompileTimeString(const char (&chars)[MPlus1]) { + char* chars_pointer = string; + for (auto c : chars) + *chars_pointer++ = c; + } + template + constexpr auto Concat(CompileTimeString b) { + CompileTimeString result; + char* chars_pointer = result.string; + for (auto c : string) + *chars_pointer++ = c; + chars_pointer = result.string + NPlus1 - 1; + for (auto c : b.string) + *chars_pointer++ = c; + result.string[NPlus1 + MPlus1 - 2] = 0; + return result; + } + constexpr operator const char*() { return string; } +}; +} // namespace rtc_base_string_utils_internal + +// Makes a constexpr CompileTimeString without having to specify X +// explicitly. +template +constexpr auto MakeCompileTimeString(const char (&a)[N]) { + return rtc_base_string_utils_internal::CompileTimeString(a); +} + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::AbslStringViewCmp; +using ::webrtc::MakeCompileTimeString; +using ::webrtc::SIZE_UNKNOWN; +using ::webrtc::strcpyn; +using ::webrtc::ToHex; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_STRING_UTILS_H_ diff --git a/pkg/apm/webrtc/rtc_base/strings/json.h b/pkg/apm/webrtc/rtc_base/strings/json.h new file mode 100644 index 00000000..90e9080d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/strings/json.h @@ -0,0 +1,123 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_STRINGS_JSON_H_ +#define RTC_BASE_STRINGS_JSON_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "json/json.h" // IWYU pragma: export +#include "json/reader.h" // IWYU pragma: export +#include "json/value.h" // IWYU pragma: export + +namespace webrtc { + +/////////////////////////////////////////////////////////////////////////////// +// JSON Helpers +/////////////////////////////////////////////////////////////////////////////// + +// Robust conversion operators, better than the ones in JsonCpp. +bool GetIntFromJson(const Json::Value& in, int* out); +bool GetUIntFromJson(const Json::Value& in, unsigned int* out); +bool GetStringFromJson(const Json::Value& in, std::string* out); +bool GetBoolFromJson(const Json::Value& in, bool* out); +bool GetDoubleFromJson(const Json::Value& in, double* out); + +// Pull values out of a JSON array. +bool GetValueFromJsonArray(const Json::Value& in, size_t n, Json::Value* out); +bool GetIntFromJsonArray(const Json::Value& in, size_t n, int* out); +bool GetUIntFromJsonArray(const Json::Value& in, size_t n, unsigned int* out); +bool GetStringFromJsonArray(const Json::Value& in, size_t n, std::string* out); +bool GetBoolFromJsonArray(const Json::Value& in, size_t n, bool* out); +bool GetDoubleFromJsonArray(const Json::Value& in, size_t n, double* out); + +// Convert json arrays to std::vector +bool JsonArrayToValueVector(const Json::Value& in, + std::vector* out); +bool JsonArrayToIntVector(const Json::Value& in, std::vector* out); +bool JsonArrayToUIntVector(const Json::Value& in, + std::vector* out); +bool JsonArrayToStringVector(const Json::Value& in, + std::vector* out); +bool JsonArrayToBoolVector(const Json::Value& in, std::vector* out); +bool JsonArrayToDoubleVector(const Json::Value& in, std::vector* out); + +// Convert std::vector to json array +Json::Value ValueVectorToJsonArray(const std::vector& in); +Json::Value IntVectorToJsonArray(const std::vector& in); +Json::Value UIntVectorToJsonArray(const std::vector& in); +Json::Value StringVectorToJsonArray(const std::vector& in); +Json::Value BoolVectorToJsonArray(const std::vector& in); +Json::Value DoubleVectorToJsonArray(const std::vector& in); + +// Pull values out of a JSON object. +bool GetValueFromJsonObject(const Json::Value& in, + absl::string_view k, + Json::Value* out); +bool GetIntFromJsonObject(const Json::Value& in, absl::string_view k, int* out); +bool GetUIntFromJsonObject(const Json::Value& in, + absl::string_view k, + unsigned int* out); +bool GetStringFromJsonObject(const Json::Value& in, + absl::string_view k, + std::string* out); +bool GetBoolFromJsonObject(const Json::Value& in, + absl::string_view k, + bool* out); +bool GetDoubleFromJsonObject(const Json::Value& in, + absl::string_view k, + double* out); + +// Writes out a Json value as a string. +std::string JsonValueToString(const Json::Value& json); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::BoolVectorToJsonArray; +using ::webrtc::DoubleVectorToJsonArray; +using ::webrtc::GetBoolFromJson; +using ::webrtc::GetBoolFromJsonArray; +using ::webrtc::GetBoolFromJsonObject; +using ::webrtc::GetDoubleFromJson; +using ::webrtc::GetDoubleFromJsonArray; +using ::webrtc::GetDoubleFromJsonObject; +using ::webrtc::GetIntFromJson; +using ::webrtc::GetIntFromJsonArray; +using ::webrtc::GetIntFromJsonObject; +using ::webrtc::GetStringFromJson; +using ::webrtc::GetStringFromJsonArray; +using ::webrtc::GetStringFromJsonObject; +using ::webrtc::GetUIntFromJson; +using ::webrtc::GetUIntFromJsonArray; +using ::webrtc::GetUIntFromJsonObject; +using ::webrtc::GetValueFromJsonArray; +using ::webrtc::GetValueFromJsonObject; +using ::webrtc::IntVectorToJsonArray; +using ::webrtc::JsonArrayToBoolVector; +using ::webrtc::JsonArrayToDoubleVector; +using ::webrtc::JsonArrayToIntVector; +using ::webrtc::JsonArrayToStringVector; +using ::webrtc::JsonArrayToUIntVector; +using ::webrtc::JsonArrayToValueVector; +using ::webrtc::JsonValueToString; +using ::webrtc::StringVectorToJsonArray; +using ::webrtc::UIntVectorToJsonArray; +using ::webrtc::ValueVectorToJsonArray; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_STRINGS_JSON_H_ diff --git a/pkg/apm/webrtc/rtc_base/strings/str_join.h b/pkg/apm/webrtc/rtc_base/strings/str_join.h new file mode 100644 index 00000000..779cded4 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/strings/str_join.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_STRINGS_STR_JOIN_H_ +#define RTC_BASE_STRINGS_STR_JOIN_H_ + +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +template +std::string StrJoin(const Range& seq, absl::string_view delimiter) { + StringBuilder sb; + int idx = 0; + + for (const typename Range::value_type& elem : seq) { + if (idx > 0) { + sb << delimiter; + } + sb << elem; + + ++idx; + } + return sb.Release(); +} + +template +std::string StrJoin(const Range& seq, + absl::string_view delimiter, + const Functor& fn) { + StringBuilder sb; + int idx = 0; + + for (const typename Range::value_type& elem : seq) { + if (idx > 0) { + sb << delimiter; + } + fn(sb, elem); + + ++idx; + } + return sb.Release(); +} + +} // namespace webrtc + +#endif // RTC_BASE_STRINGS_STR_JOIN_H_ diff --git a/pkg/apm/webrtc/rtc_base/strings/string_builder.cc b/pkg/apm/webrtc/rtc_base/strings/string_builder.cc new file mode 100644 index 00000000..fb568fbd --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/strings/string_builder.cc @@ -0,0 +1,133 @@ +/* + * Copyright 2018 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/strings/string_builder.h" + +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +SimpleStringBuilder::SimpleStringBuilder(ArrayView buffer) + : buffer_(buffer) { + buffer_[0] = '\0'; + RTC_DCHECK(IsConsistent()); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(char ch) { + return operator<<(absl::string_view(&ch, 1)); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(absl::string_view str) { + RTC_DCHECK_LT(size_ + str.length(), buffer_.size()) + << "Buffer size was insufficient"; + const size_t chars_added = SafeMin(str.length(), buffer_.size() - size_ - 1); + memcpy(&buffer_[size_], str.data(), chars_added); + size_ += chars_added; + buffer_[size_] = '\0'; + RTC_DCHECK(IsConsistent()); + return *this; +} + +// Numeric conversion routines. +// +// We use std::[v]snprintf instead of std::to_string because: +// * std::to_string relies on the current locale for formatting purposes, +// and therefore concurrent calls to std::to_string from multiple threads +// may result in partial serialization of calls +// * snprintf allows us to print the number directly into our buffer. +// * avoid allocating a std::string (potential heap alloc). +// TODO(tommi): Switch to std::to_chars in C++17. + +SimpleStringBuilder& SimpleStringBuilder::operator<<(int i) { + return AppendFormat("%d", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(unsigned i) { + return AppendFormat("%u", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(long i) { // NOLINT + return AppendFormat("%ld", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(long long i) { // NOLINT + return AppendFormat("%lld", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<( + unsigned long i) { // NOLINT + return AppendFormat("%lu", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<( + unsigned long long i) { // NOLINT + return AppendFormat("%llu", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(float f) { + return AppendFormat("%g", f); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(double f) { + return AppendFormat("%g", f); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(long double f) { + return AppendFormat("%Lg", f); +} + +SimpleStringBuilder& SimpleStringBuilder::AppendFormat(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + const int len = + std::vsnprintf(&buffer_[size_], buffer_.size() - size_, fmt, args); + if (len >= 0) { + const size_t chars_added = SafeMin(len, buffer_.size() - 1 - size_); + size_ += chars_added; + RTC_DCHECK_EQ(len, chars_added) << "Buffer size was insufficient"; + } else { + // This should never happen, but we're paranoid, so re-write the + // terminator in case vsnprintf() overwrote it. + RTC_DCHECK_NOTREACHED(); + buffer_[size_] = '\0'; + } + va_end(args); + RTC_DCHECK(IsConsistent()); + return *this; +} + +StringBuilder& StringBuilder::AppendFormat(const char* fmt, ...) { + va_list args, copy; + va_start(args, fmt); + va_copy(copy, args); + const int predicted_length = std::vsnprintf(nullptr, 0, fmt, copy); + va_end(copy); + + RTC_DCHECK_GE(predicted_length, 0); + if (predicted_length > 0) { + const size_t size = str_.size(); + str_.resize(size + predicted_length); + // Pass "+ 1" to vsnprintf to include space for the '\0'. + const int actual_length = + std::vsnprintf(&str_[size], predicted_length + 1, fmt, args); + RTC_DCHECK_GE(actual_length, 0); + } + va_end(args); + return *this; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/strings/string_builder.h b/pkg/apm/webrtc/rtc_base/strings/string_builder.h new file mode 100644 index 00000000..55d0b061 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/strings/string_builder.h @@ -0,0 +1,175 @@ +/* + * 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 RTC_BASE_STRINGS_STRING_BUILDER_H_ +#define RTC_BASE_STRINGS_STRING_BUILDER_H_ + +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "rtc_base/string_encode.h" + +namespace webrtc { + +// This is a minimalistic string builder class meant to cover the most cases of +// when you might otherwise be tempted to use a stringstream (discouraged for +// anything except logging). It uses a fixed-size buffer provided by the caller +// and concatenates strings and numbers into it, allowing the results to be +// read via `str()`. +class SimpleStringBuilder { + public: + explicit SimpleStringBuilder(ArrayView buffer); + SimpleStringBuilder(const SimpleStringBuilder&) = delete; + SimpleStringBuilder& operator=(const SimpleStringBuilder&) = delete; + + SimpleStringBuilder& operator<<(char ch); + SimpleStringBuilder& operator<<(absl::string_view str); + SimpleStringBuilder& operator<<(int i); + SimpleStringBuilder& operator<<(unsigned i); + SimpleStringBuilder& operator<<(long i); // NOLINT + SimpleStringBuilder& operator<<(long long i); // NOLINT + SimpleStringBuilder& operator<<(unsigned long i); // NOLINT + SimpleStringBuilder& operator<<(unsigned long long i); // NOLINT + SimpleStringBuilder& operator<<(float f); + SimpleStringBuilder& operator<<(double f); + SimpleStringBuilder& operator<<(long double f); + + // Returns a pointer to the built string. The name `str()` is borrowed for + // compatibility reasons as we replace usage of stringstream throughout the + // code base. + const char* str() const { return buffer_.data(); } + + // Returns the length of the string. The name `size()` is picked for STL + // compatibility reasons. + size_t size() const { return size_; } + +// Allows appending a printf style formatted string. +#if defined(__GNUC__) + __attribute__((__format__(__printf__, 2, 3))) +#endif + SimpleStringBuilder& + AppendFormat(const char* fmt, ...); + + private: + bool IsConsistent() const { + return size_ <= buffer_.size() - 1 && buffer_[size_] == '\0'; + } + + // An always-zero-terminated fixed-size buffer that we write to. The fixed + // size allows the buffer to be stack allocated, which helps performance. + // Having a fixed size is furthermore useful to avoid unnecessary resizing + // while building it. + const ArrayView buffer_; + + // Represents the number of characters written to the buffer. + // This does not include the terminating '\0'. + size_t size_ = 0; +}; + +// A string builder that supports dynamic resizing while building a string. +// The class is based around an instance of std::string and allows moving +// ownership out of the class once the string has been built. +// Note that this class uses the heap for allocations, so SimpleStringBuilder +// might be more efficient for some use cases. +class StringBuilder { + public: + StringBuilder() {} + explicit StringBuilder(absl::string_view s) : str_(s) {} + + // TODO(tommi): Support construction from StringBuilder? + StringBuilder(const StringBuilder&) = delete; + StringBuilder& operator=(const StringBuilder&) = delete; + + StringBuilder& operator<<(const absl::string_view str) { + str_.append(str.data(), str.length()); + return *this; + } + + StringBuilder& operator<<(char c) = delete; + + StringBuilder& operator<<(int i) { + str_ += absl::StrCat(i); + return *this; + } + + StringBuilder& operator<<(unsigned i) { + str_ += absl::StrCat(i); + return *this; + } + + StringBuilder& operator<<(long i) { // NOLINT + str_ += absl::StrCat(i); + return *this; + } + + StringBuilder& operator<<(long long i) { // NOLINT + str_ += absl::StrCat(i); + return *this; + } + + StringBuilder& operator<<(unsigned long i) { // NOLINT + str_ += absl::StrCat(i); + return *this; + } + + StringBuilder& operator<<(unsigned long long i) { // NOLINT + str_ += absl::StrCat(i); + return *this; + } + + StringBuilder& operator<<(float f) { + str_ += absl::StrCat(f); + return *this; + } + + StringBuilder& operator<<(double f) { + str_ += absl::StrCat(f); + return *this; + } + + const std::string& str() const { return str_; } + + void Clear() { str_.clear(); } + + size_t size() const { return str_.size(); } + + std::string Release() { + std::string ret = std::move(str_); + str_.clear(); + return ret; + } + + // Allows appending a printf style formatted string. + StringBuilder& AppendFormat(const char* fmt, ...) +#if defined(__GNUC__) + __attribute__((__format__(__printf__, 2, 3))) +#endif + ; + + private: + std::string str_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::SimpleStringBuilder; +using ::webrtc::StringBuilder; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_STRINGS_STRING_BUILDER_H_ diff --git a/pkg/apm/webrtc/rtc_base/strings/string_format.cc b/pkg/apm/webrtc/rtc_base/strings/string_format.cc new file mode 100644 index 00000000..35661531 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/strings/string_format.cc @@ -0,0 +1,41 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/strings/string_format.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// This is an arbitrary limitation that can be changed if necessary, or removed +// if someone has the time and inclination to replicate the fancy logic from +// Chromium's base::StringPrinf(). +constexpr int kMaxSize = 512; + +} // namespace + +std::string StringFormat(const char* fmt, ...) { + char buffer[kMaxSize]; + va_list args; + va_start(args, fmt); + int result = vsnprintf(buffer, kMaxSize, fmt, args); + va_end(args); + RTC_DCHECK_GE(result, 0) << "ERROR: vsnprintf() failed with error " << result; + RTC_DCHECK_LT(result, kMaxSize) + << "WARNING: string was truncated from " << result << " to " + << (kMaxSize - 1) << " characters"; + return std::string(buffer); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/strings/string_format.h b/pkg/apm/webrtc/rtc_base/strings/string_format.h new file mode 100644 index 00000000..707cab0e --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/strings/string_format.h @@ -0,0 +1,39 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_STRINGS_STRING_FORMAT_H_ +#define RTC_BASE_STRINGS_STRING_FORMAT_H_ + +#include + +namespace webrtc { + +#if defined(__GNUC__) +#define RTC_PRINTF_FORMAT(format_param, dots_param) \ + __attribute__((format(printf, format_param, dots_param))) +#else +#define RTC_PRINTF_FORMAT(format_param, dots_param) +#endif + +// Return a C++ string given printf-like input. +// Based on base::StringPrintf() in Chrome but without its fancy dynamic memory +// allocation for any size of the input buffer. +std::string StringFormat(const char* fmt, ...) RTC_PRINTF_FORMAT(1, 2); +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::StringFormat; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_STRINGS_STRING_FORMAT_H_ diff --git a/pkg/apm/webrtc/rtc_base/strings/strings.go b/pkg/apm/webrtc/rtc_base/strings/strings.go new file mode 100644 index 00000000..a0b85bc0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/strings/strings.go @@ -0,0 +1,10 @@ +//go:build console + +package strings + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/rtc_base/strong_alias.h b/pkg/apm/webrtc/rtc_base/strong_alias.h new file mode 100644 index 00000000..3f45113f --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/strong_alias.h @@ -0,0 +1,76 @@ +/* + * Copyright 2019 The Chromium Authors. All rights reserved. + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_STRONG_ALIAS_H_ +#define RTC_BASE_STRONG_ALIAS_H_ + +#include +#include + +namespace webrtc { + +// This is a copy of +// https://source.chromium.org/chromium/chromium/src/+/main:base/types/strong_alias.h +// as the API (and internals) are using type-safe integral identifiers, but this +// library can't depend on that file. The ostream operator has been removed +// per WebRTC library conventions, and the underlying type is exposed. + +template +class StrongAlias { + public: + using UnderlyingType = TheUnderlyingType; + constexpr StrongAlias() = default; + constexpr explicit StrongAlias(const UnderlyingType& v) : value_(v) {} + constexpr explicit StrongAlias(UnderlyingType&& v) noexcept + : value_(std::move(v)) {} + + constexpr UnderlyingType* operator->() { return &value_; } + constexpr const UnderlyingType* operator->() const { return &value_; } + + constexpr UnderlyingType& operator*() & { return value_; } + constexpr const UnderlyingType& operator*() const& { return value_; } + constexpr UnderlyingType&& operator*() && { return std::move(value_); } + constexpr const UnderlyingType&& operator*() const&& { + return std::move(value_); + } + + constexpr UnderlyingType& value() & { return value_; } + constexpr const UnderlyingType& value() const& { return value_; } + constexpr UnderlyingType&& value() && { return std::move(value_); } + constexpr const UnderlyingType&& value() const&& { return std::move(value_); } + + constexpr explicit operator const UnderlyingType&() const& { return value_; } + + constexpr bool operator==(const StrongAlias& other) const { + return value_ == other.value_; + } + constexpr bool operator!=(const StrongAlias& other) const { + return value_ != other.value_; + } + constexpr bool operator<(const StrongAlias& other) const { + return value_ < other.value_; + } + constexpr bool operator<=(const StrongAlias& other) const { + return value_ <= other.value_; + } + constexpr bool operator>(const StrongAlias& other) const { + return value_ > other.value_; + } + constexpr bool operator>=(const StrongAlias& other) const { + return value_ >= other.value_; + } + + protected: + UnderlyingType value_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_STRONG_ALIAS_H_ diff --git a/pkg/apm/webrtc/rtc_base/swap_queue.h b/pkg/apm/webrtc/rtc_base/swap_queue.h new file mode 100644 index 00000000..3c8149c1 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/swap_queue.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SWAP_QUEUE_H_ +#define RTC_BASE_SWAP_QUEUE_H_ + +#include + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace internal { + +// (Internal; please don't use outside this file.) +template +bool NoopSwapQueueItemVerifierFunction(const T&) { + return true; +} + +} // namespace internal + +// Functor to use when supplying a verifier function for the queue. +template +class SwapQueueItemVerifier { + public: + bool operator()(const T& t) const { return QueueItemVerifierFunction(t); } +}; + +// This class is a fixed-size queue. A single producer calls Insert() to insert +// an element of type T at the back of the queue, and a single consumer calls +// Remove() to remove an element from the front of the queue. It's safe for the +// producer and the consumer to access the queue concurrently, from different +// threads. +// +// To avoid the construction, copying, and destruction of Ts that a naive +// queue implementation would require, for each "full" T passed from +// producer to consumer, SwapQueue passes an "empty" T in the other +// direction (an "empty" T is one that contains nothing of value for the +// consumer). This bidirectional movement is implemented with swap(). +// +// // Create queue: +// Bottle proto(568); // Prepare an empty Bottle. Heap allocates space for +// // 568 ml. +// SwapQueue q(N, proto); // Init queue with N copies of proto. +// // Each copy allocates on the heap. +// // Producer pseudo-code: +// Bottle b(568); // Prepare an empty Bottle. Heap allocates space for 568 ml. +// loop { +// b.Fill(amount); // Where amount <= 568 ml. +// q.Insert(&b); // Swap our full Bottle for an empty one from q. +// } +// +// // Consumer pseudo-code: +// Bottle b(568); // Prepare an empty Bottle. Heap allocates space for 568 ml. +// loop { +// q.Remove(&b); // Swap our empty Bottle for the next-in-line full Bottle. +// Drink(&b); +// } +// +// For a well-behaved Bottle class, there are no allocations in the +// producer, since it just fills an empty Bottle that's already large +// enough; no deallocations in the consumer, since it returns each empty +// Bottle to the queue after having drunk it; and no copies along the +// way, since the queue uses swap() everywhere to move full Bottles in +// one direction and empty ones in the other. +template > +class SwapQueue { + public: + // Creates a queue of size size and fills it with default constructed Ts. + explicit SwapQueue(size_t size) : queue_(size) { + RTC_DCHECK(VerifyQueueSlots()); + } + + // Same as above and accepts an item verification functor. + SwapQueue(size_t size, const QueueItemVerifier& queue_item_verifier) + : queue_item_verifier_(queue_item_verifier), queue_(size) { + RTC_DCHECK(VerifyQueueSlots()); + } + + // Creates a queue of size size and fills it with copies of prototype. + SwapQueue(size_t size, const T& prototype) : queue_(size, prototype) { + RTC_DCHECK(VerifyQueueSlots()); + } + + // Same as above and accepts an item verification functor. + SwapQueue(size_t size, + const T& prototype, + const QueueItemVerifier& queue_item_verifier) + : queue_item_verifier_(queue_item_verifier), queue_(size, prototype) { + RTC_DCHECK(VerifyQueueSlots()); + } + + // Resets the queue to have zero content while maintaining the queue size. + // Just like Remove(), this can only be called (safely) from the + // consumer. + void Clear() { + // Drop all non-empty elements by resetting num_elements_ and incrementing + // next_read_index_ by the previous value of num_elements_. Relaxed memory + // ordering is sufficient since the dropped elements are not accessed. + next_read_index_ += std::atomic_exchange_explicit( + &num_elements_, size_t{0}, std::memory_order_relaxed); + if (next_read_index_ >= queue_.size()) { + next_read_index_ -= queue_.size(); + } + + RTC_DCHECK_LT(next_read_index_, queue_.size()); + } + + // Inserts a "full" T at the back of the queue by swapping *input with an + // "empty" T from the queue. + // Returns true if the item was inserted or false if not (the queue was full). + // When specified, the T given in *input must pass the ItemVerifier() test. + // The contents of *input after the call are then also guaranteed to pass the + // ItemVerifier() test. + ABSL_MUST_USE_RESULT bool Insert(T* input) { + RTC_DCHECK(input); + + RTC_DCHECK(queue_item_verifier_(*input)); + + // Load the value of num_elements_. Acquire memory ordering prevents reads + // and writes to queue_[next_write_index_] to be reordered to before the + // load. (That element might be accessed by a concurrent call to Remove() + // until the load finishes.) + if (std::atomic_load_explicit(&num_elements_, std::memory_order_acquire) == + queue_.size()) { + return false; + } + + using std::swap; + swap(*input, queue_[next_write_index_]); + + // Increment the value of num_elements_ to account for the inserted element. + // Release memory ordering prevents the reads and writes to + // queue_[next_write_index_] to be reordered to after the increment. (Once + // the increment has finished, Remove() might start accessing that element.) + const size_t old_num_elements = std::atomic_fetch_add_explicit( + &num_elements_, size_t{1}, std::memory_order_release); + + ++next_write_index_; + if (next_write_index_ == queue_.size()) { + next_write_index_ = 0; + } + + RTC_DCHECK_LT(next_write_index_, queue_.size()); + RTC_DCHECK_LT(old_num_elements, queue_.size()); + + return true; + } + + // Removes the frontmost "full" T from the queue by swapping it with + // the "empty" T in *output. + // Returns true if an item could be removed or false if not (the queue was + // empty). When specified, The T given in *output must pass the ItemVerifier() + // test and the contents of *output after the call are then also guaranteed to + // pass the ItemVerifier() test. + ABSL_MUST_USE_RESULT bool Remove(T* output) { + RTC_DCHECK(output); + + RTC_DCHECK(queue_item_verifier_(*output)); + + // Load the value of num_elements_. Acquire memory ordering prevents reads + // and writes to queue_[next_read_index_] to be reordered to before the + // load. (That element might be accessed by a concurrent call to Insert() + // until the load finishes.) + if (std::atomic_load_explicit(&num_elements_, std::memory_order_acquire) == + 0) { + return false; + } + + using std::swap; + swap(*output, queue_[next_read_index_]); + + // Decrement the value of num_elements_ to account for the removed element. + // Release memory ordering prevents the reads and writes to + // queue_[next_write_index_] to be reordered to after the decrement. (Once + // the decrement has finished, Insert() might start accessing that element.) + std::atomic_fetch_sub_explicit(&num_elements_, size_t{1}, + std::memory_order_release); + + ++next_read_index_; + if (next_read_index_ == queue_.size()) { + next_read_index_ = 0; + } + + RTC_DCHECK_LT(next_read_index_, queue_.size()); + + return true; + } + + // Returns the current number of elements in the queue. Since elements may be + // concurrently added to the queue, the caller must treat this as a lower + // bound, not an exact count. + // May only be called by the consumer. + size_t SizeAtLeast() const { + // Acquire memory ordering ensures that we wait for the producer to finish + // inserting any element in progress. + return std::atomic_load_explicit(&num_elements_, std::memory_order_acquire); + } + + private: + // Verify that the queue slots complies with the ItemVerifier test. This + // function is not thread-safe and can only be used in the constructors. + bool VerifyQueueSlots() { + for (const auto& v : queue_) { + RTC_DCHECK(queue_item_verifier_(v)); + } + return true; + } + + // TODO(peah): Change this to use std::function() once we can use C++11 std + // lib. + QueueItemVerifier queue_item_verifier_; + + // Only accessed by the single producer. + size_t next_write_index_ = 0; + + // Only accessed by the single consumer. + size_t next_read_index_ = 0; + + // Accessed by both the producer and the consumer and used for synchronization + // between them. + std::atomic num_elements_{0}; + + // The elements of the queue are acced by both the producer and the consumer, + // mediated by num_elements_. queue_.size() is constant. + std::vector queue_; + + SwapQueue(const SwapQueue&) = delete; + SwapQueue& operator=(const SwapQueue&) = delete; +}; + +} // namespace webrtc + +#endif // RTC_BASE_SWAP_QUEUE_H_ diff --git a/pkg/apm/webrtc/rtc_base/synchronization/mutex.h b/pkg/apm/webrtc/rtc_base/synchronization/mutex.h new file mode 100644 index 00000000..1144b4b8 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/synchronization/mutex.h @@ -0,0 +1,72 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_H_ +#define RTC_BASE_SYNCHRONIZATION_MUTEX_H_ + +#include + +#include "absl/base/attributes.h" +#include "rtc_base/checks.h" +#include "rtc_base/thread_annotations.h" + +#if defined(WEBRTC_ABSL_MUTEX) +#include "rtc_base/synchronization/mutex_abseil.h" // nogncheck +#elif defined(WEBRTC_WIN) +#include "rtc_base/synchronization/mutex_critical_section.h" +#elif defined(WEBRTC_POSIX) +#include "rtc_base/synchronization/mutex_pthread.h" +#else +#error Unsupported platform. +#endif + +namespace webrtc { + +// The Mutex guarantees exclusive access and aims to follow Abseil semantics +// (i.e. non-reentrant etc). +class RTC_LOCKABLE Mutex final { + public: + Mutex() = default; + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; + + void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { impl_.Lock(); } + ABSL_MUST_USE_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) { + return impl_.TryLock(); + } + // Return immediately if this thread holds the mutex, or RTC_DCHECK_IS_ON==0. + // Otherwise, may report an error (typically by crashing with a diagnostic), + // or may return immediately. + void AssertHeld() const RTC_ASSERT_EXCLUSIVE_LOCK() { impl_.AssertHeld(); } + void Unlock() RTC_UNLOCK_FUNCTION() { impl_.Unlock(); } + + private: + MutexImpl impl_; +}; + +// MutexLock, for serializing execution through a scope. +class RTC_SCOPED_LOCKABLE MutexLock final { + public: + MutexLock(const MutexLock&) = delete; + MutexLock& operator=(const MutexLock&) = delete; + + explicit MutexLock(Mutex* mutex) RTC_EXCLUSIVE_LOCK_FUNCTION(mutex) + : mutex_(mutex) { + mutex->Lock(); + } + ~MutexLock() RTC_UNLOCK_FUNCTION() { mutex_->Unlock(); } + + private: + Mutex* mutex_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_SYNCHRONIZATION_MUTEX_H_ diff --git a/pkg/apm/webrtc/rtc_base/synchronization/mutex_abseil.h b/pkg/apm/webrtc/rtc_base/synchronization/mutex_abseil.h new file mode 100644 index 00000000..d42f974f --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/synchronization/mutex_abseil.h @@ -0,0 +1,43 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_ABSEIL_H_ +#define RTC_BASE_SYNCHRONIZATION_MUTEX_ABSEIL_H_ + +#include "absl/base/attributes.h" +#include "absl/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class RTC_LOCKABLE MutexImpl final { + public: + MutexImpl() = default; + MutexImpl(const MutexImpl&) = delete; + MutexImpl& operator=(const MutexImpl&) = delete; + + void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { mutex_.Lock(); } + ABSL_MUST_USE_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) { + return mutex_.TryLock(); + } + void AssertHeld() const RTC_ASSERT_EXCLUSIVE_LOCK() { +#if RTC_DCHECK_IS_ON + mutex_.AssertHeld(); +#endif + } + void Unlock() RTC_UNLOCK_FUNCTION() { mutex_.Unlock(); } + + private: + absl::Mutex mutex_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_SYNCHRONIZATION_MUTEX_ABSEIL_H_ diff --git a/pkg/apm/webrtc/rtc_base/synchronization/mutex_critical_section.h b/pkg/apm/webrtc/rtc_base/synchronization/mutex_critical_section.h new file mode 100644 index 00000000..b6a3c4a7 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/synchronization/mutex_critical_section.h @@ -0,0 +1,56 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_CRITICAL_SECTION_H_ +#define RTC_BASE_SYNCHRONIZATION_MUTEX_CRITICAL_SECTION_H_ + +#if defined(WEBRTC_WIN) +// clang-format off +// clang formating would change include order. + +// Include winsock2.h before including to maintain consistency with +// win32.h. To include win32.h directly, it must be broken out into its own +// build target. +#include +#include +#include // must come after windows headers. +// clang-format on + +#include "absl/base/attributes.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class RTC_LOCKABLE MutexImpl final { + public: + MutexImpl() { InitializeCriticalSection(&critical_section_); } + MutexImpl(const MutexImpl&) = delete; + MutexImpl& operator=(const MutexImpl&) = delete; + ~MutexImpl() { DeleteCriticalSection(&critical_section_); } + + void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { + EnterCriticalSection(&critical_section_); + } + ABSL_MUST_USE_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) { + return TryEnterCriticalSection(&critical_section_) != FALSE; + } + void AssertHeld() const RTC_ASSERT_EXCLUSIVE_LOCK() {} + void Unlock() RTC_UNLOCK_FUNCTION() { + LeaveCriticalSection(&critical_section_); + } + + private: + CRITICAL_SECTION critical_section_; +}; + +} // namespace webrtc + +#endif // #if defined(WEBRTC_WIN) +#endif // RTC_BASE_SYNCHRONIZATION_MUTEX_CRITICAL_SECTION_H_ diff --git a/pkg/apm/webrtc/rtc_base/synchronization/mutex_pthread.h b/pkg/apm/webrtc/rtc_base/synchronization/mutex_pthread.h new file mode 100644 index 00000000..a532bd22 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/synchronization/mutex_pthread.h @@ -0,0 +1,101 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_PTHREAD_H_ +#define RTC_BASE_SYNCHRONIZATION_MUTEX_PTHREAD_H_ + +#if defined(WEBRTC_POSIX) + +#include +#if defined(WEBRTC_MAC) +#include +#endif + +#include "absl/base/attributes.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class RTC_LOCKABLE MutexImpl final { + public: + MutexImpl() { + pthread_mutexattr_t mutex_attribute; + pthread_mutexattr_init(&mutex_attribute); +#if defined(WEBRTC_MAC) + pthread_mutexattr_setpolicy_np(&mutex_attribute, + _PTHREAD_MUTEX_POLICY_FIRSTFIT); +#endif + pthread_mutex_init(&mutex_, &mutex_attribute); + pthread_mutexattr_destroy(&mutex_attribute); + } + MutexImpl(const MutexImpl&) = delete; + MutexImpl& operator=(const MutexImpl&) = delete; + ~MutexImpl() { pthread_mutex_destroy(&mutex_); } + + void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { + pthread_mutex_lock(&mutex_); + owner_.SetOwner(); + } + ABSL_MUST_USE_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) { + if (pthread_mutex_trylock(&mutex_) != 0) { + return false; + } + owner_.SetOwner(); + return true; + } + void AssertHeld() const RTC_ASSERT_EXCLUSIVE_LOCK() { owner_.AssertOwned(); } + void Unlock() RTC_UNLOCK_FUNCTION() { + owner_.ClearOwner(); + pthread_mutex_unlock(&mutex_); + } + + private: + class OwnerRecord { + public: +#if !RTC_DCHECK_IS_ON + void SetOwner() {} + void ClearOwner() {} + void AssertOwned() const {} +#else + void SetOwner() { + latest_owner_ = pthread_self(); + is_owned_ = true; + } + void ClearOwner() { is_owned_ = false; } + void AssertOwned() const { + RTC_CHECK(is_owned_); + RTC_CHECK(pthread_equal(latest_owner_, pthread_self())); + } + + private: + // Use two separate primitive types, rather than std::optional, since the + // data race described below might invalidate std::optional invariants. + bool is_owned_ = false; + pthread_t latest_owner_ = pthread_self(); +#endif + }; + + pthread_mutex_t mutex_; + // This record is modified only with the mutex held, and hence, calls to + // AssertHeld where mutex is held are race-free and will always succeed. + // + // The failure case is more subtle: If AssertHeld is called from some thread + // not holding the mutex, and RTC_DCHECK_IS_ON==1, we have a data race. It is + // highly likely that the calling thread will see `is_owned_` false or + // `latest_owner_` different from itself, and crash. But it may fail to crash, + // and invoke some other undefined behavior (still, this race can happen only + // when RTC_DCHECK_IS_ON==1). + RTC_NO_UNIQUE_ADDRESS OwnerRecord owner_; +}; + +} // namespace webrtc +#endif // #if defined(WEBRTC_POSIX) +#endif // RTC_BASE_SYNCHRONIZATION_MUTEX_PTHREAD_H_ diff --git a/pkg/apm/webrtc/rtc_base/synchronization/sequence_checker_internal.cc b/pkg/apm/webrtc/rtc_base/synchronization/sequence_checker_internal.cc new file mode 100644 index 00000000..007acacd --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/synchronization/sequence_checker_internal.cc @@ -0,0 +1,89 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "rtc_base/synchronization/sequence_checker_internal.h" + +#include + +#include "api/task_queue/task_queue_base.h" +#include "rtc_base/checks.h" +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/synchronization/mutex.h" + +namespace webrtc { +namespace webrtc_sequence_checker_internal { + +SequenceCheckerImpl::SequenceCheckerImpl(bool attach_to_current_thread) + : attached_(attach_to_current_thread), + valid_thread_(CurrentThreadRef()), + valid_queue_(TaskQueueBase::Current()) {} + +SequenceCheckerImpl::SequenceCheckerImpl(TaskQueueBase* attached_queue) + : attached_(attached_queue != nullptr), + valid_thread_(PlatformThreadRef()), + valid_queue_(attached_queue) {} + +bool SequenceCheckerImpl::IsCurrent() const { + const TaskQueueBase* const current_queue = TaskQueueBase::Current(); + const PlatformThreadRef current_thread = CurrentThreadRef(); + MutexLock scoped_lock(&lock_); + if (!attached_) { // Previously detached. + attached_ = true; + valid_thread_ = current_thread; + valid_queue_ = current_queue; + return true; + } + if (valid_queue_) { + return valid_queue_ == current_queue; + } + return IsThreadRefEqual(valid_thread_, current_thread); +} + +void SequenceCheckerImpl::Detach() { + MutexLock scoped_lock(&lock_); + attached_ = false; + // We don't need to touch the other members here, they will be + // reset on the next call to IsCurrent(). +} + +#if RTC_DCHECK_IS_ON +std::string SequenceCheckerImpl::ExpectationToString() const { + const TaskQueueBase* const current_queue = TaskQueueBase::Current(); + const webrtc::PlatformThreadRef current_thread = webrtc::CurrentThreadRef(); + MutexLock scoped_lock(&lock_); + if (!attached_) + return "Checker currently not attached."; + + // The format of the string is meant to compliment the one we have inside of + // FatalLog() (checks.cc). Example: + // + // # Expected: TQ: 0x0 SysQ: 0x7fff69541330 Thread: 0x11dcf6dc0 + // # Actual: TQ: 0x7fa8f0604190 SysQ: 0x7fa8f0604a30 Thread: 0x700006f1a000 + // TaskQueue doesn't match + + webrtc::StringBuilder message; + message.AppendFormat( + "# Expected: TQ: %p Thread: %p\n" + "# Actual: TQ: %p Thread: %p\n", + valid_queue_, reinterpret_cast(valid_thread_), current_queue, + reinterpret_cast(current_thread)); + + if ((valid_queue_ || current_queue) && valid_queue_ != current_queue) { + message << "TaskQueue doesn't match\n"; + } else if (!webrtc::IsThreadRefEqual(valid_thread_, current_thread)) { + message << "Threads don't match\n"; + } + + return message.Release(); +} +#endif // RTC_DCHECK_IS_ON + +} // namespace webrtc_sequence_checker_internal +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/synchronization/sequence_checker_internal.h b/pkg/apm/webrtc/rtc_base/synchronization/sequence_checker_internal.h new file mode 100644 index 00000000..ea801497 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/synchronization/sequence_checker_internal.h @@ -0,0 +1,90 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_INTERNAL_H_ +#define RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_INTERNAL_H_ + +#include +#include + +#include "api/task_queue/task_queue_base.h" +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { +namespace webrtc_sequence_checker_internal { + +// Real implementation of SequenceChecker, for use in debug mode, or +// for temporary use in release mode (e.g. to RTC_CHECK on a threading issue +// seen only in the wild). +// +// Note: You should almost always use the SequenceChecker class to get the +// right version for your build configuration. +class RTC_EXPORT SequenceCheckerImpl { + public: + explicit SequenceCheckerImpl(bool attach_to_current_thread); + explicit SequenceCheckerImpl(TaskQueueBase* attached_queue); + ~SequenceCheckerImpl() = default; + + bool IsCurrent() const; + // Changes the task queue or thread that is checked for in IsCurrent. This can + // be useful when an object may be created on one task queue / thread and then + // used exclusively on another thread. + void Detach(); + + // Returns a string that is formatted to match with the error string printed + // by RTC_CHECK() when a condition is not met. + // This is used in conjunction with the RTC_DCHECK_RUN_ON() macro. + std::string ExpectationToString() const; + + private: + mutable Mutex lock_; + // These are mutable so that IsCurrent can set them. + mutable bool attached_ RTC_GUARDED_BY(lock_); + mutable PlatformThreadRef valid_thread_ RTC_GUARDED_BY(lock_); + mutable const TaskQueueBase* valid_queue_ RTC_GUARDED_BY(lock_); +}; + +// Do nothing implementation, for use in release mode. +// +// Note: You should almost always use the SequenceChecker class to get the +// right version for your build configuration. +class SequenceCheckerDoNothing { + public: + explicit SequenceCheckerDoNothing(bool /* attach_to_current_thread */) {} + explicit SequenceCheckerDoNothing(TaskQueueBase* /* attached_queue */) {} + bool IsCurrent() const { return true; } + void Detach() {} +}; + +template +std::enable_if_t, + std::string> +ExpectationToString([[maybe_unused]] const ThreadLikeObject* checker) { +#if RTC_DCHECK_IS_ON + return checker->ExpectationToString(); +#else + return std::string(); +#endif +} + +// Catch-all implementation for types other than explicitly supported above. +template +std::enable_if_t, + std::string> +ExpectationToString(const ThreadLikeObject*) { + return std::string(); +} + +} // namespace webrtc_sequence_checker_internal +} // namespace webrtc + +#endif // RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_INTERNAL_H_ diff --git a/pkg/apm/webrtc/rtc_base/synchronization/sync.go b/pkg/apm/webrtc/rtc_base/synchronization/sync.go new file mode 100644 index 00000000..6455eb49 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/synchronization/sync.go @@ -0,0 +1,10 @@ +//go:build console + +package synchronization + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/rtc_base/synchronization/yield.cc b/pkg/apm/webrtc/rtc_base/synchronization/yield.cc new file mode 100644 index 00000000..cbb58d12 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/synchronization/yield.cc @@ -0,0 +1,36 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/synchronization/yield.h" + +#if defined(WEBRTC_WIN) +#include +#else +#include +#include +#endif + +namespace webrtc { + +void YieldCurrentThread() { + // TODO(bugs.webrtc.org/11634): use dedicated OS functionality instead of + // sleep for yielding. +#if defined(WEBRTC_WIN) + ::Sleep(0); +#elif defined(WEBRTC_MAC) && defined(RTC_USE_NATIVE_MUTEX_ON_MAC) && \ + !RTC_USE_NATIVE_MUTEX_ON_MAC + sched_yield(); +#else + static const struct timespec ts_null = {0}; + nanosleep(&ts_null, nullptr); +#endif +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/synchronization/yield.h b/pkg/apm/webrtc/rtc_base/synchronization/yield.h new file mode 100644 index 00000000..d4f5f99f --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/synchronization/yield.h @@ -0,0 +1,20 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_SYNCHRONIZATION_YIELD_H_ +#define RTC_BASE_SYNCHRONIZATION_YIELD_H_ + +namespace webrtc { + +// Request rescheduling of threads. +void YieldCurrentThread(); + +} // namespace webrtc + +#endif // RTC_BASE_SYNCHRONIZATION_YIELD_H_ diff --git a/pkg/apm/webrtc/rtc_base/synchronization/yield_policy.cc b/pkg/apm/webrtc/rtc_base/synchronization/yield_policy.cc new file mode 100644 index 00000000..c8ba9f22 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/synchronization/yield_policy.cc @@ -0,0 +1,82 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "rtc_base/synchronization/yield_policy.h" + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "rtc_base/checks.h" +#if !defined(ABSL_HAVE_THREAD_LOCAL) && defined(WEBRTC_POSIX) +#include +#endif + +namespace webrtc { +namespace { + +#if defined(ABSL_HAVE_THREAD_LOCAL) + +ABSL_CONST_INIT thread_local YieldInterface* current_yield_policy = nullptr; + +YieldInterface* GetCurrentYieldPolicy() { + return current_yield_policy; +} + +void SetCurrentYieldPolicy(YieldInterface* ptr) { + current_yield_policy = ptr; +} + +#elif defined(WEBRTC_POSIX) + +// Emscripten does not support the C++11 thread_local keyword but does support +// the pthread thread-local storage API. +// https://github.com/emscripten-core/emscripten/issues/3502 + +ABSL_CONST_INIT pthread_key_t g_current_yield_policy_tls = 0; + +void InitializeTls() { + RTC_CHECK_EQ(pthread_key_create(&g_current_yield_policy_tls, nullptr), 0); +} + +pthread_key_t GetCurrentYieldPolicyTls() { + static pthread_once_t init_once = PTHREAD_ONCE_INIT; + RTC_CHECK_EQ(pthread_once(&init_once, &InitializeTls), 0); + return g_current_yield_policy_tls; +} + +YieldInterface* GetCurrentYieldPolicy() { + return static_cast( + pthread_getspecific(GetCurrentYieldPolicyTls())); +} + +void SetCurrentYieldPolicy(YieldInterface* ptr) { + pthread_setspecific(GetCurrentYieldPolicyTls(), ptr); +} + +#else +#error Unsupported platform +#endif + +} // namespace + +ScopedYieldPolicy::ScopedYieldPolicy(YieldInterface* policy) + : previous_(GetCurrentYieldPolicy()) { + SetCurrentYieldPolicy(policy); +} + +ScopedYieldPolicy::~ScopedYieldPolicy() { + SetCurrentYieldPolicy(previous_); +} + +void ScopedYieldPolicy::YieldExecution() { + YieldInterface* current = GetCurrentYieldPolicy(); + if (current) + current->YieldExecution(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/synchronization/yield_policy.h b/pkg/apm/webrtc/rtc_base/synchronization/yield_policy.h new file mode 100644 index 00000000..1756cf21 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/synchronization/yield_policy.h @@ -0,0 +1,47 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_SYNCHRONIZATION_YIELD_POLICY_H_ +#define RTC_BASE_SYNCHRONIZATION_YIELD_POLICY_H_ + +namespace webrtc { +class YieldInterface { + public: + virtual ~YieldInterface() = default; + virtual void YieldExecution() = 0; +}; + +// Sets the current thread-local yield policy while it's in scope and reverts +// to the previous policy when it leaves the scope. +class ScopedYieldPolicy final { + public: + explicit ScopedYieldPolicy(YieldInterface* policy); + ScopedYieldPolicy(const ScopedYieldPolicy&) = delete; + ScopedYieldPolicy& operator=(const ScopedYieldPolicy&) = delete; + ~ScopedYieldPolicy(); + // Will yield as specified by the currently active thread-local yield policy + // (which by default is a no-op). + static void YieldExecution(); + + private: + YieldInterface* const previous_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::ScopedYieldPolicy; +using ::webrtc::YieldInterface; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SYNCHRONIZATION_YIELD_POLICY_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/arch.h b/pkg/apm/webrtc/rtc_base/system/arch.h new file mode 100644 index 00000000..9d945ef7 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/arch.h @@ -0,0 +1,100 @@ +/* + * 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. + */ + +// This file contains platform-specific typedefs and defines. +// Much of it is derived from Chromium's build/build_config.h. + +#ifndef RTC_BASE_SYSTEM_ARCH_H_ +#define RTC_BASE_SYSTEM_ARCH_H_ + +// Processor architecture detection. For more info on what's defined, see: +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +// https://www.agner.org/optimize/calling_conventions.pdf +// https://sourceforge.net/p/predef/wiki/Architectures/ +// or with gcc, run: "echo | gcc -E -dM -" +#if defined(_M_X64) || defined(__x86_64__) +#define WEBRTC_ARCH_X86_FAMILY +#define WEBRTC_ARCH_X86_64 +#define WEBRTC_ARCH_64_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(_M_ARM64) || defined(__aarch64__) +#define WEBRTC_ARCH_ARM_FAMILY +#define WEBRTC_ARCH_64_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(_M_IX86) || defined(__i386__) +#define WEBRTC_ARCH_X86_FAMILY +#define WEBRTC_ARCH_X86 +#define WEBRTC_ARCH_32_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(_M_ARM) || defined(__ARMEL__) +#define WEBRTC_ARCH_ARM_FAMILY +#define WEBRTC_ARCH_32_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__MIPSEL__) || defined(__MIPSEB__) +#define WEBRTC_ARCH_MIPS_FAMILY +#if defined(__LP64__) +#define WEBRTC_ARCH_64_BITS +#else +#define WEBRTC_ARCH_32_BITS +#endif +#if defined(__MIPSEL__) +#define WEBRTC_ARCH_LITTLE_ENDIAN +#else +#define WEBRTC_ARCH_BIG_ENDIAN +#endif +#elif defined(__PPC__) +#if defined(__PPC64__) +#define WEBRTC_ARCH_64_BITS +#else +#define WEBRTC_ARCH_32_BITS +#endif +#if defined(__LITTLE_ENDIAN__) +#define WEBRTC_ARCH_LITTLE_ENDIAN +#else +#define WEBRTC_ARCH_BIG_ENDIAN +#endif +#elif defined(__sparc) || defined(__sparc__) +#if __SIZEOF_LONG__ == 8 +#define WEBRTC_ARCH_64_BITS +#else +#define WEBRTC_ARCH_32_BITS +#endif +#define WEBRTC_ARCH_BIG_ENDIAN +#elif defined(__riscv) && __riscv_xlen == 64 +#define WEBRTC_ARCH_64_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__riscv) && __riscv_xlen == 32 +#define WEBRTC_ARCH_32_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__loongarch32) +#define WEBRTC_ARCH_LOONG_FAMILY +#define WEBRTC_ARCH_LOONG32 +#define WEBRTC_ARCH_32_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__loongarch64) +#define WEBRTC_ARCH_LOONG_FAMILY +#define WEBRTC_ARCH_LOONG64 +#define WEBRTC_ARCH_64_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__pnacl__) +#define WEBRTC_ARCH_32_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__EMSCRIPTEN__) +#define WEBRTC_ARCH_32_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#else +#error Please add support for your architecture in rtc_base/system/arch.h +#endif + +#if !(defined(WEBRTC_ARCH_LITTLE_ENDIAN) ^ defined(WEBRTC_ARCH_BIG_ENDIAN)) +#error Define either WEBRTC_ARCH_LITTLE_ENDIAN or WEBRTC_ARCH_BIG_ENDIAN +#endif + +#endif // RTC_BASE_SYSTEM_ARCH_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/asm_defines.h b/pkg/apm/webrtc/rtc_base/system/asm_defines.h new file mode 100644 index 00000000..a7f6aad2 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/asm_defines.h @@ -0,0 +1,72 @@ +/* + * 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 RTC_BASE_SYSTEM_ASM_DEFINES_H_ +#define RTC_BASE_SYSTEM_ASM_DEFINES_H_ + +// clang-format off +// clang formatting breaks everything here, e.g. concatenating directives, +// due to absence of context via asm keyword. + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + +// Define the macros used in ARM assembly code, so that for Mac or iOS builds +// we add leading underscores for the function names. +#ifdef __APPLE__ +.macro GLOBAL_FUNCTION name +.global _\name +.private_extern _\name +.endm +.macro DEFINE_FUNCTION name +_\name: +.endm +.macro CALL_FUNCTION name +bl _\name +.endm +.macro GLOBAL_LABEL name +.global _\name +.private_extern _\name +.endm +#else +.macro GLOBAL_FUNCTION name +.global \name +.hidden \name +.endm +.macro DEFINE_FUNCTION name +#if defined(__linux__) && defined(__ELF__) +.type \name,%function +#endif +\name: +.endm +.macro CALL_FUNCTION name +bl \name +.endm +.macro GLOBAL_LABEL name +.global \name +.hidden \name +.endm +#endif + +// With Apple's clang compiler, for instructions ldrb, strh, etc., +// the condition code is after the width specifier. Here we define +// only the ones that are actually used in the assembly files. +#if (defined __llvm__) && (defined __APPLE__) +.macro streqh reg1, reg2, num +strheq \reg1, \reg2, \num +.endm +#endif + +.text + +// clang-format on + +#endif // RTC_BASE_SYSTEM_ASM_DEFINES_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/assume.h b/pkg/apm/webrtc/rtc_base/system/assume.h new file mode 100644 index 00000000..231c9e18 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/assume.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYSTEM_ASSUME_H_ +#define RTC_BASE_SYSTEM_ASSUME_H_ + +// Possibly evaluate `p`, promising the compiler that the result is true; the +// compiler is allowed (but not required) to use this information when +// optimizing the code. USE WITH CAUTION! If you promise the compiler things +// that aren't true, it will build a broken binary for you. +// +// As a simple example, the compiler is allowed to transform this +// +// RTC_ASSUME(x == 4); +// return x; +// +// into this +// +// return 4; +// +// It is even allowed to propagate the assumption "backwards in time", if it can +// prove that it must have held at some earlier time. For example, the compiler +// is allowed to transform this +// +// int Add(int x, int y) { +// if (x == 17) +// y += 1; +// RTC_ASSUME(x != 17); +// return x + y; +// } +// +// into this +// +// int Add(int x, int y) { +// return x + y; +// } +// +// since if `x` isn't 17 on the third line of the function body, the test of `x +// == 17` on the first line must fail since nothing can modify the local +// variable `x` in between. +// +// The intended use is to allow the compiler to optimize better. For example, +// here we allow the compiler to omit an instruction that ensures correct +// rounding of negative arguments: +// +// int DivBy2(int x) { +// RTC_ASSUME(x >= 0); +// return x / 2; +// } +// +// and here we allow the compiler to possibly omit a null check: +// +// void Delete(int* p) { +// RTC_ASSUME(p != nullptr); +// delete p; +// } +// +// clang-format off +#if defined(__GNUC__) +#define RTC_ASSUME(p) do { if (!(p)) __builtin_unreachable(); } while (0) +#else +#define RTC_ASSUME(p) do {} while (0) +#endif +// clang-format on + +#endif // RTC_BASE_SYSTEM_ASSUME_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/cocoa_threading.h b/pkg/apm/webrtc/rtc_base/system/cocoa_threading.h new file mode 100644 index 00000000..518cb717 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/cocoa_threading.h @@ -0,0 +1,24 @@ +/* + * 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 RTC_BASE_SYSTEM_COCOA_THREADING_H_ +#define RTC_BASE_SYSTEM_COCOA_THREADING_H_ + +// If Cocoa is to be used on more than one thread, it must know that the +// application is multithreaded. Since it's possible to enter Cocoa code +// from threads created by pthread_thread_create, Cocoa won't necessarily +// be aware that the application is multithreaded. Spawning an NSThread is +// enough to get Cocoa to set up for multithreaded operation, so this is done +// if necessary before pthread_thread_create spawns any threads. +// +// http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html +void InitCocoaMultiThreading(); + +#endif // RTC_BASE_SYSTEM_COCOA_THREADING_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/file_wrapper.cc b/pkg/apm/webrtc/rtc_base/system/file_wrapper.cc new file mode 100644 index 00000000..7f7a30bf --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/file_wrapper.cc @@ -0,0 +1,136 @@ +/* + * 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 "rtc_base/system/file_wrapper.h" + +#include + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" + +#ifdef _WIN32 +#include +#else +#endif + +#include + +namespace webrtc { +namespace { +FILE* FileOpen(absl::string_view file_name_utf8, bool read_only, int* error) { + RTC_CHECK_EQ(file_name_utf8.find_first_of('\0'), absl::string_view::npos) + << "Invalid filename, containing NUL character"; + std::string file_name(file_name_utf8); +#if defined(_WIN32) + int len = MultiByteToWideChar(CP_UTF8, 0, file_name.c_str(), -1, nullptr, 0); + std::wstring wstr(len, 0); + MultiByteToWideChar(CP_UTF8, 0, file_name.c_str(), -1, &wstr[0], len); + FILE* file = _wfopen(wstr.c_str(), read_only ? L"rb" : L"wb"); +#else + FILE* file = fopen(file_name.c_str(), read_only ? "rb" : "wb"); +#endif + if (!file && error) { + *error = errno; + } + return file; +} + +} // namespace + +// static +FileWrapper FileWrapper::OpenReadOnly(absl::string_view file_name_utf8) { + return FileWrapper(FileOpen(file_name_utf8, true, nullptr)); +} + +// static +FileWrapper FileWrapper::OpenWriteOnly(absl::string_view file_name_utf8, + int* error /*=nullptr*/) { + return FileWrapper(FileOpen(file_name_utf8, false, error)); +} + +FileWrapper::FileWrapper(FileWrapper&& other) { + operator=(std::move(other)); +} + +FileWrapper& FileWrapper::operator=(FileWrapper&& other) { + Close(); + file_ = other.file_; + other.file_ = nullptr; + return *this; +} + +bool FileWrapper::SeekRelative(int64_t offset) { + RTC_DCHECK(file_); + return fseek(file_, checked_cast(offset), SEEK_CUR) == 0; +} + +bool FileWrapper::SeekTo(int64_t position) { + RTC_DCHECK(file_); + return fseek(file_, checked_cast(position), SEEK_SET) == 0; +} + +std::optional FileWrapper::FileSize() { + if (file_ == nullptr) + return std::nullopt; + long original_position = ftell(file_); + if (original_position < 0) + return std::nullopt; + int seek_error = fseek(file_, 0, SEEK_END); + if (seek_error) + return std::nullopt; + long file_size = ftell(file_); + seek_error = fseek(file_, original_position, SEEK_SET); + if (seek_error) + return std::nullopt; + return checked_cast(file_size); +} + +bool FileWrapper::Flush() { + RTC_DCHECK(file_); + return fflush(file_) == 0; +} + +size_t FileWrapper::Read(void* buf, size_t length) { + RTC_DCHECK(file_); + return fread(buf, 1, length, file_); +} + +bool FileWrapper::ReadEof() const { + RTC_DCHECK(file_); + return feof(file_); +} + +bool FileWrapper::Write(const void* buf, size_t length) { + RTC_DCHECK(file_); + return fwrite(buf, 1, length, file_) == length; +} + +bool FileWrapper::Close() { + if (file_ == nullptr) + return true; + + bool success = fclose(file_) == 0; + file_ = nullptr; + return success; +} + +FILE* FileWrapper::Release() { + FILE* file = file_; + file_ = nullptr; + return file; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/system/file_wrapper.h b/pkg/apm/webrtc/rtc_base/system/file_wrapper.h new file mode 100644 index 00000000..eafc685e --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/file_wrapper.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYSTEM_FILE_WRAPPER_H_ +#define RTC_BASE_SYSTEM_FILE_WRAPPER_H_ + +#include +#include +#include + +#include +#include + +#include "absl/strings/string_view.h" + +// Implementation that can read (exclusive) or write from/to a file. + +namespace webrtc { + +// This class is a thin wrapper around FILE*. It's main features are that it +// owns the FILE*, calling fclose on destruction, and that on windows, file +// names passed to the open methods are always treated as utf-8, regardless of +// system code page. + +// Most of the methods return only a success/fail indication. When needed, an +// optional argument |int* error| should be added to all methods, in the same +// way as for the OpenWriteOnly methods. +class FileWrapper final { + public: + // Opens a file, in read or write mode. Use the is_open() method on the + // returned object to check if the open operation was successful. On failure, + // and if `error` is non-null, the system errno value is stored at |*error|. + // The file is closed by the destructor. + static FileWrapper OpenReadOnly(absl::string_view file_name_utf8); + static FileWrapper OpenWriteOnly(absl::string_view file_name_utf8, + int* error = nullptr); + + FileWrapper() = default; + + // Takes over ownership of `file`, closing it on destruction. Calling with + // null `file` is allowed, and results in a FileWrapper with is_open() false. + explicit FileWrapper(FILE* file) : file_(file) {} + ~FileWrapper() { Close(); } + + // Copying is not supported. + FileWrapper(const FileWrapper&) = delete; + FileWrapper& operator=(const FileWrapper&) = delete; + + // Support for move semantics. + FileWrapper(FileWrapper&&); + FileWrapper& operator=(FileWrapper&&); + + // Returns true if a file has been opened. If the file is not open, no methods + // but is_open and Close may be called. + bool is_open() const { return file_ != nullptr; } + + // Closes the file, and implies Flush. Returns true on success, false if + // writing buffered data fails. On failure, the file is nevertheless closed. + // Calling Close on an already closed file does nothing and returns success. + bool Close(); + + // Releases and returns the wrapped file without closing it. This call passes + // the ownership of the file to the caller, and the wrapper is no longer + // responsible for closing it. Similarly the previously wrapped file is no + // longer available for the wrapper to use in any aspect. + FILE* Release(); + + // Write any buffered data to the underlying file. Returns true on success, + // false on write error. Note: Flushing when closing, is not required. + bool Flush(); + + // Seeks to the beginning of file. Returns true on success, false on failure, + // e.g., if the underlying file isn't seekable. + bool Rewind() { return SeekTo(0); } + // TODO(nisse): The seek functions are used only by the WavReader. If that + // code is demoted to test code, seek functions can be deleted from this + // utility. + // Seek relative to current file position. + bool SeekRelative(int64_t offset); + // Seek to given position. + bool SeekTo(int64_t position); + + // Returns the file size or std::nullopt if the size could not be determined. + // (A file size might not exists for non-seekable files or file-like + // objects, for example /dev/tty on unix.) + std::optional FileSize(); + + // Returns number of bytes read. Short count indicates EOF or error. + size_t Read(void* buf, size_t length); + + // If the most recent Read() returned a short count, this methods returns true + // if the short count was due to EOF, and false it it was due to some i/o + // error. + bool ReadEof() const; + + // Returns true if all data was successfully written (or buffered), or false + // if there was an error. Writing buffered data can fail later, and is + // reported with return value from Flush or Close. + bool Write(const void* buf, size_t length); + + private: + FILE* file_ = nullptr; +}; + +} // namespace webrtc + +#endif // RTC_BASE_SYSTEM_FILE_WRAPPER_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/gcd_helpers.h b/pkg/apm/webrtc/rtc_base/system/gcd_helpers.h new file mode 100644 index 00000000..a8df0a9d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/gcd_helpers.h @@ -0,0 +1,29 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYSTEM_GCD_HELPERS_H_ +#define RTC_BASE_SYSTEM_GCD_HELPERS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_queue_t +RTCDispatchQueueCreateWithTarget(const char* label, + dispatch_queue_attr_t attr, + dispatch_queue_t target); + +#ifdef __cplusplus +} +#endif + +#endif // RTC_BASE_SYSTEM_GCD_HELPERS_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/ignore_warnings.h b/pkg/apm/webrtc/rtc_base/system/ignore_warnings.h new file mode 100644 index 00000000..e891c508 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/ignore_warnings.h @@ -0,0 +1,29 @@ +/* + * 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 RTC_BASE_SYSTEM_IGNORE_WARNINGS_H_ +#define RTC_BASE_SYSTEM_IGNORE_WARNINGS_H_ + +#ifdef __clang__ +#define RTC_PUSH_IGNORING_WFRAME_LARGER_THAN() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wframe-larger-than=\"") +#define RTC_POP_IGNORING_WFRAME_LARGER_THAN() _Pragma("clang diagnostic pop") +#elif __GNUC__ +#define RTC_PUSH_IGNORING_WFRAME_LARGER_THAN() \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wframe-larger-than=\"") +#define RTC_POP_IGNORING_WFRAME_LARGER_THAN() _Pragma("GCC diagnostic pop") +#else +#define RTC_PUSH_IGNORING_WFRAME_LARGER_THAN() +#define RTC_POP_IGNORING_WFRAME_LARGER_THAN() +#endif + +#endif // RTC_BASE_SYSTEM_IGNORE_WARNINGS_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/inline.h b/pkg/apm/webrtc/rtc_base/system/inline.h new file mode 100644 index 00000000..f585d34d --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/inline.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYSTEM_INLINE_H_ +#define RTC_BASE_SYSTEM_INLINE_H_ + +#if defined(_MSC_VER) + +#define RTC_FORCE_INLINE __forceinline +#define RTC_NO_INLINE __declspec(noinline) + +#elif defined(__GNUC__) + +#define RTC_FORCE_INLINE __attribute__((__always_inline__)) +#define RTC_NO_INLINE __attribute__((__noinline__)) + +#else + +#define RTC_FORCE_INLINE +#define RTC_NO_INLINE + +#endif + +#endif // RTC_BASE_SYSTEM_INLINE_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/no_cfi_icall.h b/pkg/apm/webrtc/rtc_base/system/no_cfi_icall.h new file mode 100644 index 00000000..42d6c9c2 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/no_cfi_icall.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYSTEM_NO_CFI_ICALL_H_ +#define RTC_BASE_SYSTEM_NO_CFI_ICALL_H_ + +#include "rtc_base/sanitizer.h" + +// DISABLE_CFI_ICALL -- Disable Control Flow Integrity indirect call checks. +// Note that the same macro is defined in "base/compiler_specific.h". +// Only use this when building standalone WebRTC. +#if !defined(WEBRTC_CHROMIUM_BUILD) +#if !defined(DISABLE_CFI_ICALL) +#if defined(WEBRTC_WIN) +// Windows also needs __declspec(guard(nocf)). +#define DISABLE_CFI_ICALL RTC_NO_SANITIZE("cfi-icall") __declspec(guard(nocf)) +#else +#define DISABLE_CFI_ICALL RTC_NO_SANITIZE("cfi-icall") +#endif // defined(WEBRTC_WIN) +#endif // !defined(DISABLE_CFI_ICALL) +#if !defined(DISABLE_CFI_ICALL) +#define DISABLE_CFI_ICALL +#endif +#endif // !defined(WEBRTC_CHROMIUM_BUILD) + +#endif // RTC_BASE_SYSTEM_NO_CFI_ICALL_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/no_unique_address.h b/pkg/apm/webrtc/rtc_base/system/no_unique_address.h new file mode 100644 index 00000000..a40db345 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/no_unique_address.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYSTEM_NO_UNIQUE_ADDRESS_H_ +#define RTC_BASE_SYSTEM_NO_UNIQUE_ADDRESS_H_ + +// RTC_NO_UNIQUE_ADDRESS is a portable annotation to tell the compiler that +// a data member need not have an address distinct from all other non-static +// data members of its class. +// It allows empty types to actually occupy zero bytes as class members, +// instead of occupying at least one byte just so that they get their own +// address. There is almost never any reason not to use it on class members +// that could possibly be empty. +// The macro expands to [[no_unique_address]] if the compiler supports the +// attribute, it expands to nothing otherwise. +// Clang should supports this attribute since C++11, while other compilers +// should add support for it starting from C++20. Among clang compilers, +// clang-cl doesn't support it yet and support is unclear also when the target +// platform is iOS. +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(x) 0 +#endif +#if __has_cpp_attribute(no_unique_address) +// NOLINTNEXTLINE(whitespace/braces) +#define RTC_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define RTC_NO_UNIQUE_ADDRESS +#endif + +#endif // RTC_BASE_SYSTEM_NO_UNIQUE_ADDRESS_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/rtc_export.h b/pkg/apm/webrtc/rtc_base/system/rtc_export.h new file mode 100644 index 00000000..d1eb60ad --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/rtc_export.h @@ -0,0 +1,43 @@ +/* + * 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 RTC_BASE_SYSTEM_RTC_EXPORT_H_ +#define RTC_BASE_SYSTEM_RTC_EXPORT_H_ + +// RTC_EXPORT is used to mark symbols as exported or imported when WebRTC is +// built or used as a shared library. +// When WebRTC is built as a static library the RTC_EXPORT macro expands to +// nothing. + +#ifdef WEBRTC_ENABLE_SYMBOL_EXPORT + +#ifdef WEBRTC_WIN + +#ifdef WEBRTC_LIBRARY_IMPL +#define RTC_EXPORT __declspec(dllexport) +#else +#define RTC_EXPORT __declspec(dllimport) +#endif + +#else // WEBRTC_WIN + +#if __has_attribute(visibility) && defined(WEBRTC_LIBRARY_IMPL) +#define RTC_EXPORT __attribute__((visibility("default"))) +#endif + +#endif // WEBRTC_WIN + +#endif // WEBRTC_ENABLE_SYMBOL_EXPORT + +#ifndef RTC_EXPORT +#define RTC_EXPORT +#endif + +#endif // RTC_BASE_SYSTEM_RTC_EXPORT_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/rtc_export_template.h b/pkg/apm/webrtc/rtc_base/system/rtc_export_template.h new file mode 100644 index 00000000..4ac70438 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/rtc_export_template.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYSTEM_RTC_EXPORT_TEMPLATE_H_ +#define RTC_BASE_SYSTEM_RTC_EXPORT_TEMPLATE_H_ + +// clang-format off +// clang formating would cause cpplint errors in the macros below. + +// Most of this was borrowed (with minor modifications) from Chromium's +// base/export_template.h. + +// Synopsis +// +// This header provides macros for using RTC_EXPORT macros with explicit +// template instantiation declarations and definitions. +// Generally, the RTC_EXPORT macros are used at declarations, +// and GCC requires them to be used at explicit instantiation declarations, +// but MSVC requires __declspec(dllexport) to be used at the explicit +// instantiation definitions instead. + +// Usage +// +// In a header file, write: +// +// extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) foo; +// +// In a source file, write: +// +// template class RTC_EXPORT_TEMPLATE_DEFINE(RTC_EXPORT) foo; + +// Implementation notes +// +// On Windows, when building when RTC_EXPORT expands to __declspec(dllexport)), +// we want the two lines to expand to: +// +// extern template class foo; +// template class RTC_EXPORT foo; +// +// In all other cases (non-Windows, and Windows when RTC_EXPORT expands to +// __declspec(dllimport)), we want: +// +// extern template class RTC_EXPORT foo; +// template class foo; +// +// The implementation of this header uses some subtle macro semantics to +// detect what the provided RTC_EXPORT value was defined as and then +// to dispatch to appropriate macro definitions. Unfortunately, +// MSVC's C preprocessor is rather non-compliant and requires special +// care to make it work. +// +// Issue 1. +// +// #define F(x) +// F() +// +// MSVC emits warning C4003 ("not enough actual parameters for macro +// 'F'), even though it's a valid macro invocation. This affects the +// macros below that take just an "export" parameter, because export +// may be empty. +// +// As a workaround, we can add a dummy parameter and arguments: +// +// #define F(x,_) +// F(,) +// +// Issue 2. +// +// #define F(x) G##x +// #define Gj() ok +// F(j()) +// +// The correct replacement for "F(j())" is "ok", but MSVC replaces it +// with "Gj()". As a workaround, we can pass the result to an +// identity macro to force MSVC to look for replacements again. (This +// is why RTC_EXPORT_TEMPLATE_STYLE_3 exists.) + +#define RTC_EXPORT_TEMPLATE_DECLARE(export) \ + RTC_EXPORT_TEMPLATE_INVOKE( \ + DECLARE, \ + RTC_EXPORT_TEMPLATE_STYLE(export, ), export) // NOLINT +#define RTC_EXPORT_TEMPLATE_DEFINE(export) \ + RTC_EXPORT_TEMPLATE_INVOKE( \ + DEFINE, \ + RTC_EXPORT_TEMPLATE_STYLE(export, ), export) // NOLINT + +// INVOKE is an internal helper macro to perform parameter replacements +// and token pasting to chain invoke another macro. E.g., +// RTC_EXPORT_TEMPLATE_INVOKE(DECLARE, DEFAULT, RTC_EXPORT) +// will export to call +// RTC_EXPORT_TEMPLATE_DECLARE_DEFAULT(RTC_EXPORT, ) +// (but with RTC_EXPORT expanded too). +#define RTC_EXPORT_TEMPLATE_INVOKE(which, style, export) \ + RTC_EXPORT_TEMPLATE_INVOKE_2(which, style, export) +#define RTC_EXPORT_TEMPLATE_INVOKE_2(which, style, export) \ + RTC_EXPORT_TEMPLATE_##which##_##style(export, ) + +// Default style is to apply the RTC_EXPORT macro at declaration sites. +#define RTC_EXPORT_TEMPLATE_DECLARE_DEFAULT(export, _) export +#define RTC_EXPORT_TEMPLATE_DEFINE_DEFAULT(export, _) + +// The "MSVC hack" style is used when RTC_EXPORT is defined +// as __declspec(dllexport), which MSVC requires to be used at +// definition sites instead. +#define RTC_EXPORT_TEMPLATE_DECLARE_MSVC_HACK(export, _) +#define RTC_EXPORT_TEMPLATE_DEFINE_MSVC_HACK(export, _) export + +// RTC_EXPORT_TEMPLATE_STYLE is an internal helper macro that identifies which +// export style needs to be used for the provided RTC_EXPORT macro definition. +// "", "__attribute__(...)", and "__declspec(dllimport)" are mapped +// to "DEFAULT"; while "__declspec(dllexport)" is mapped to "MSVC_HACK". +// +// It's implemented with token pasting to transform the __attribute__ and +// __declspec annotations into macro invocations. E.g., if RTC_EXPORT is +// defined as "__declspec(dllimport)", it undergoes the following sequence of +// macro substitutions: +// RTC_EXPORT_TEMPLATE_STYLE(RTC_EXPORT,) +// RTC_EXPORT_TEMPLATE_STYLE_2(__declspec(dllimport),) +// RTC_EXPORT_TEMPLATE_STYLE_3( +// RTC_EXPORT_TEMPLATE_STYLE_MATCH__declspec(dllimport)) +// RTC_EXPORT_TEMPLATE_STYLE_MATCH__declspec(dllimport) +// RTC_EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport +// DEFAULT +#define RTC_EXPORT_TEMPLATE_STYLE(export, _) \ + RTC_EXPORT_TEMPLATE_STYLE_2(export, ) +#define RTC_EXPORT_TEMPLATE_STYLE_2(export, _) \ + RTC_EXPORT_TEMPLATE_STYLE_3( \ + RTC_EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA##export) +#define RTC_EXPORT_TEMPLATE_STYLE_3(style) style + +// Internal helper macros for RTC_EXPORT_TEMPLATE_STYLE. +// +// XXX: C++ reserves all identifiers containing "__" for the implementation, +// but "__attribute__" and "__declspec" already contain "__" and the token-paste +// operator can only add characters; not remove them. To minimize the risk of +// conflict with implementations, we include "foj3FJo5StF0OvIzl7oMxA" (a random +// 128-bit string, encoded in Base64) in the macro name. +#define RTC_EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA DEFAULT +#define RTC_EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__attribute__( \ + ...) \ + DEFAULT +#define RTC_EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__declspec(arg) \ + RTC_EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_##arg + +// Internal helper macros for RTC_EXPORT_TEMPLATE_STYLE. +#define RTC_EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllexport MSVC_HACK +#define RTC_EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport DEFAULT + +// Sanity checks. +// +// RTC_EXPORT_TEMPLATE_TEST uses the same macro invocation pattern as +// RTC_EXPORT_TEMPLATE_DECLARE and RTC_EXPORT_TEMPLATE_DEFINE do to check that +// they're working correctly. When they're working correctly, the sequence of +// macro replacements should go something like: +// +// RTC_EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport)); +// +// static_assert(RTC_EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT, +// RTC_EXPORT_TEMPLATE_STYLE(__declspec(dllimport), ), +// __declspec(dllimport)), "__declspec(dllimport)"); +// +// static_assert(RTC_EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT, +// DEFAULT, __declspec(dllimport)), "__declspec(dllimport)"); +// +// static_assert(RTC_EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT( +// __declspec(dllimport)), "__declspec(dllimport)"); +// +// static_assert(true, "__declspec(dllimport)"); +// +// When they're not working correctly, a syntax error should occur instead. +#define RTC_EXPORT_TEMPLATE_TEST(want, export) \ + static_assert( \ + RTC_EXPORT_TEMPLATE_INVOKE( \ + TEST_##want, \ + RTC_EXPORT_TEMPLATE_STYLE(export, ), export), #export) // NOLINT +#define RTC_EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT(...) true +#define RTC_EXPORT_TEMPLATE_TEST_MSVC_HACK_MSVC_HACK(...) true + +RTC_EXPORT_TEMPLATE_TEST(DEFAULT, ); // NOLINT +RTC_EXPORT_TEMPLATE_TEST(DEFAULT, __attribute__((visibility("default")))); +RTC_EXPORT_TEMPLATE_TEST(MSVC_HACK, __declspec(dllexport)); +RTC_EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport)); + +#undef RTC_EXPORT_TEMPLATE_TEST +#undef RTC_EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT +#undef RTC_EXPORT_TEMPLATE_TEST_MSVC_HACK_MSVC_HACK + +// clang-format on + +#endif // RTC_BASE_SYSTEM_RTC_EXPORT_TEMPLATE_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/system.go b/pkg/apm/webrtc/rtc_base/system/system.go new file mode 100644 index 00000000..5611e735 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/system.go @@ -0,0 +1,10 @@ +//go:build console + +package system + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/rtc_base/system/unused.h b/pkg/apm/webrtc/rtc_base/system/unused.h new file mode 100644 index 00000000..03d0c2f0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/unused.h @@ -0,0 +1,28 @@ +/* + * 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 RTC_BASE_SYSTEM_UNUSED_H_ +#define RTC_BASE_SYSTEM_UNUSED_H_ + +// Prevent the compiler from warning about an unused variable. For example: +// int result = DoSomething(); +// RTC_DCHECK(result == 17); +// RTC_UNUSED(result); +// Note: In most cases it is better to remove the unused variable rather than +// suppressing the compiler warning. +#ifndef RTC_UNUSED +#ifdef __cplusplus +#define RTC_UNUSED(x) static_cast(x) +#else +#define RTC_UNUSED(x) (void)(x) +#endif +#endif // RTC_UNUSED + +#endif // RTC_BASE_SYSTEM_UNUSED_H_ diff --git a/pkg/apm/webrtc/rtc_base/system/warn_current_thread_is_deadlocked.h b/pkg/apm/webrtc/rtc_base/system/warn_current_thread_is_deadlocked.h new file mode 100644 index 00000000..4a0ba9dc --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system/warn_current_thread_is_deadlocked.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYSTEM_WARN_CURRENT_THREAD_IS_DEADLOCKED_H_ +#define RTC_BASE_SYSTEM_WARN_CURRENT_THREAD_IS_DEADLOCKED_H_ + +namespace webrtc { + +#if defined(WEBRTC_ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) +void WarnThatTheCurrentThreadIsProbablyDeadlocked(); +#else +inline void WarnThatTheCurrentThreadIsProbablyDeadlocked() {} +#endif + +} // namespace webrtc + +#endif // RTC_BASE_SYSTEM_WARN_CURRENT_THREAD_IS_DEADLOCKED_H_ diff --git a/pkg/apm/webrtc/rtc_base/system_time.cc b/pkg/apm/webrtc/rtc_base/system_time.cc new file mode 100644 index 00000000..c6a9b7b7 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system_time.cc @@ -0,0 +1,102 @@ +/* + * Copyright 2021 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// If WEBRTC_EXCLUDE_SYSTEM_TIME is set, an implementation of +// webrtc::SystemTimeNanos() must be provided externally. +#ifndef WEBRTC_EXCLUDE_SYSTEM_TIME + +#include + +#include + +#if defined(WEBRTC_POSIX) +#include +#if defined(WEBRTC_MAC) +#include +#endif +#endif + +#if defined(WEBRTC_WIN) +// clang-format off +// clang formatting would put last, +// which leads to compilation failure. +#include +#include +#include +// clang-format on +#endif + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/system_time.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +int64_t SystemTimeNanos() { + int64_t ticks; +#if defined(WEBRTC_MAC) + static mach_timebase_info_data_t timebase; + if (timebase.denom == 0) { + // Get the timebase if this is the first time we run. + // Recommended by Apple's QA1398. + if (mach_timebase_info(&timebase) != KERN_SUCCESS) { + RTC_DCHECK_NOTREACHED(); + } + } + // Use timebase to convert absolute time tick units into nanoseconds. + const auto mul = [](uint64_t a, uint32_t b) -> int64_t { + RTC_DCHECK_NE(b, 0); + RTC_DCHECK_LE(a, std::numeric_limits::max() / b) + << "The multiplication " << a << " * " << b << " overflows"; + return webrtc::dchecked_cast(a * b); + }; + ticks = mul(mach_absolute_time(), timebase.numer) / timebase.denom; +#elif defined(WEBRTC_POSIX) + struct timespec ts; + // TODO(deadbeef): Do we need to handle the case when CLOCK_MONOTONIC is not + // supported? + clock_gettime(CLOCK_MONOTONIC, &ts); + ticks = kNumNanosecsPerSec * static_cast(ts.tv_sec) + + static_cast(ts.tv_nsec); +#elif defined(WINUWP) + ticks = WinUwpSystemTimeNanos(); +#elif defined(WEBRTC_WIN) + // TODO(webrtc:14601): Fix the volatile increment instead of suppressing the + // warning. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-volatile" + static volatile LONG last_timegettime = 0; + static volatile int64_t num_wrap_timegettime = 0; + volatile LONG* last_timegettime_ptr = &last_timegettime; + DWORD now = timeGetTime(); + // Atomically update the last gotten time + DWORD old = InterlockedExchange(last_timegettime_ptr, now); + if (now < old) { + // If now is earlier than old, there may have been a race between threads. + // 0x0fffffff ~3.1 days, the code will not take that long to execute + // so it must have been a wrap around. + if (old > 0xf0000000 && now < 0x0fffffff) { + num_wrap_timegettime++; + } + } + ticks = now + (num_wrap_timegettime << 32); + // TODO(deadbeef): Calculate with nanosecond precision. Otherwise, we're + // just wasting a multiply and divide when doing Time() on Windows. + ticks = ticks * webrtc::kNumNanosecsPerMillisec; +#pragma clang diagnostic pop +#else +#error Unsupported platform. +#endif + return ticks; +} + +} // namespace webrtc +#endif // WEBRTC_EXCLUDE_SYSTEM_TIME diff --git a/pkg/apm/webrtc/rtc_base/system_time.h b/pkg/apm/webrtc/rtc_base/system_time.h new file mode 100644 index 00000000..2cae4286 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/system_time.h @@ -0,0 +1,35 @@ +/* + * Copyright 2021 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYSTEM_TIME_H_ +#define RTC_BASE_SYSTEM_TIME_H_ + +#include + +namespace webrtc { + +// Returns the actual system time, even if a clock is set for testing. +// Useful for timeouts while using a test clock, or for logging. +int64_t SystemTimeNanos(); + +} // namespace webrtc + +// TODO(bugs.webrtc.org/4222596): Remove once Chrome has migrated. +#define RTC_SYSTEM_TIME_IN_WEBRTC_NAMESPACE 1 + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::SystemTimeNanos; +} +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_SYSTEM_TIME_H_ diff --git a/pkg/apm/webrtc/rtc_base/task_queue_gcd.h b/pkg/apm/webrtc/rtc_base/task_queue_gcd.h new file mode 100644 index 00000000..dc6039e9 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/task_queue_gcd.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_TASK_QUEUE_GCD_H_ +#define RTC_BASE_TASK_QUEUE_GCD_H_ + +#include + +#include "api/task_queue/task_queue_factory.h" + +namespace webrtc { + +std::unique_ptr CreateTaskQueueGcdFactory(); + +} // namespace webrtc + +#endif // RTC_BASE_TASK_QUEUE_GCD_H_ diff --git a/pkg/apm/webrtc/rtc_base/task_queue_stdlib.h b/pkg/apm/webrtc/rtc_base/task_queue_stdlib.h new file mode 100644 index 00000000..fb03dff3 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/task_queue_stdlib.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_TASK_QUEUE_STDLIB_H_ +#define RTC_BASE_TASK_QUEUE_STDLIB_H_ + +#include + +#include "api/task_queue/task_queue_factory.h" + +namespace webrtc { + +std::unique_ptr CreateTaskQueueStdlibFactory(); + +} // namespace webrtc + +#endif // RTC_BASE_TASK_QUEUE_STDLIB_H_ diff --git a/pkg/apm/webrtc/rtc_base/task_queue_win.h b/pkg/apm/webrtc/rtc_base/task_queue_win.h new file mode 100644 index 00000000..972611ab --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/task_queue_win.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_TASK_QUEUE_WIN_H_ +#define RTC_BASE_TASK_QUEUE_WIN_H_ + +#include + +#include "api/task_queue/task_queue_factory.h" + +namespace webrtc { + +std::unique_ptr CreateTaskQueueWinFactory(); + +} + +#endif // RTC_BASE_TASK_QUEUE_WIN_H_ diff --git a/pkg/apm/webrtc/rtc_base/task_utils/repeating_task.h b/pkg/apm/webrtc/rtc_base/task_utils/repeating_task.h new file mode 100644 index 00000000..5779fc1a --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/task_utils/repeating_task.h @@ -0,0 +1,91 @@ +/* + * Copyright 2019 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_TASK_UTILS_REPEATING_TASK_H_ +#define RTC_BASE_TASK_UTILS_REPEATING_TASK_H_ + +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "api/task_queue/pending_task_safety_flag.h" +#include "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { + +namespace webrtc_repeating_task_impl { + +// Methods simplifying external tracing of RepeatingTaskHandle operations. +void RepeatingTaskHandleDTraceProbeStart(); +void RepeatingTaskHandleDTraceProbeDelayedStart(); +void RepeatingTaskImplDTraceProbeRun(); + +} // namespace webrtc_repeating_task_impl + +// Allows starting tasks that repeat themselves on a TaskQueue indefinately +// until they are stopped or the TaskQueue is destroyed. It allows starting and +// stopping multiple times, but you must stop one task before starting another +// and it can only be stopped when in the running state. The public interface is +// not thread safe. +class RepeatingTaskHandle { + public: + RepeatingTaskHandle() = default; + ~RepeatingTaskHandle() = default; + RepeatingTaskHandle(RepeatingTaskHandle&& other) = default; + RepeatingTaskHandle& operator=(RepeatingTaskHandle&& other) = default; + RepeatingTaskHandle(const RepeatingTaskHandle&) = delete; + RepeatingTaskHandle& operator=(const RepeatingTaskHandle&) = delete; + + // Start can be used to start a task that will be reposted with a delay + // determined by the return value of the provided closure. The actual task is + // owned by the TaskQueue and will live until it has been stopped or the + // TaskQueue deletes it. It's perfectly fine to destroy the handle while the + // task is running, since the repeated task is owned by the TaskQueue. + // The tasks are scheduled onto the task queue using the specified precision. + static RepeatingTaskHandle Start( + TaskQueueBase* task_queue, + absl::AnyInvocable closure, + TaskQueueBase::DelayPrecision precision = + TaskQueueBase::DelayPrecision::kLow, + Clock* clock = Clock::GetRealTimeClock(), + const Location& location = Location::Current()); + + // DelayedStart is equivalent to Start except that the first invocation of the + // closure will be delayed by the given amount. + static RepeatingTaskHandle DelayedStart( + TaskQueueBase* task_queue, + TimeDelta first_delay, + absl::AnyInvocable closure, + TaskQueueBase::DelayPrecision precision = + TaskQueueBase::DelayPrecision::kLow, + Clock* clock = Clock::GetRealTimeClock(), + const Location& location = Location::Current()); + + // Stops future invocations of the repeating task closure. Can only be called + // from the TaskQueue where the task is running. The closure is guaranteed to + // not be running after Stop() returns unless Stop() is called from the + // closure itself. + void Stop(); + + // Returns true until Stop() was called. + // Can only be called from the TaskQueue where the task is running. + bool Running() const; + + private: + explicit RepeatingTaskHandle(scoped_refptr alive_flag) + : repeating_task_(std::move(alive_flag)) {} + scoped_refptr repeating_task_; +}; + +} // namespace webrtc +#endif // RTC_BASE_TASK_UTILS_REPEATING_TASK_H_ diff --git a/pkg/apm/webrtc/rtc_base/third_party/base64/base64.h b/pkg/apm/webrtc/rtc_base/third_party/base64/base64.h new file mode 100644 index 00000000..7a4b1e62 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/third_party/base64/base64.h @@ -0,0 +1,135 @@ + +//********************************************************************* +//* C_Base64 - a simple base64 encoder and decoder. +//* +//* Copyright (c) 1999, Bob Withers - bwit@pobox.com +//* +//* This code may be freely used for any purpose, either personal +//* or commercial, provided the authors copyright notice remains +//* intact. +//********************************************************************* + +#ifndef RTC_BASE_THIRD_PARTY_BASE64_BASE64_H_ +#define RTC_BASE_THIRD_PARTY_BASE64_BASE64_H_ + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" + +namespace webrtc { + +class Base64 { + public: + enum DecodeOption { + DO_PARSE_STRICT = 1, // Parse only base64 characters + DO_PARSE_WHITE = 2, // Parse only base64 and whitespace characters + DO_PARSE_ANY = 3, // Parse all characters + DO_PARSE_MASK = 3, + + DO_PAD_YES = 4, // Padding is required + DO_PAD_ANY = 8, // Padding is optional + DO_PAD_NO = 12, // Padding is disallowed + DO_PAD_MASK = 12, + + DO_TERM_BUFFER = 16, // Must termiante at end of buffer + DO_TERM_CHAR = 32, // May terminate at any character boundary + DO_TERM_ANY = 48, // May terminate at a sub-character bit offset + DO_TERM_MASK = 48, + + // Strictest interpretation + DO_STRICT = DO_PARSE_STRICT | DO_PAD_YES | DO_TERM_BUFFER, + + DO_LAX = DO_PARSE_ANY | DO_PAD_ANY | DO_TERM_CHAR, + }; + typedef int DecodeFlags; + + static bool IsBase64Char(char ch); + + // Get the char next to the `ch` from the Base64Table. + // If the `ch` is the last one in the Base64Table then returns + // the first one from the table. + // Expects the `ch` be a base64 char. + // The result will be saved in `next_ch`. + // Returns true on success. + static bool GetNextBase64Char(char ch, char* next_ch); + + // Determines whether the given string consists entirely of valid base64 + // encoded characters. + static bool IsBase64Encoded(absl::string_view str); + + static void EncodeFromArray(const void* data, + size_t len, + std::string* result); + static bool DecodeFromArray(const char* data, + size_t len, + DecodeFlags flags, + std::string* result, + size_t* data_used); + static bool DecodeFromArray(const char* data, + size_t len, + DecodeFlags flags, + std::vector* result, + size_t* data_used); + static bool DecodeFromArray(const char* data, + size_t len, + DecodeFlags flags, + std::vector* result, + size_t* data_used); + + // Convenience Methods + static inline std::string Encode(absl::string_view data) { + std::string result; + EncodeFromArray(data.data(), data.size(), &result); + return result; + } + static inline std::string Decode(absl::string_view data, DecodeFlags flags) { + std::string result; + DecodeFromArray(data.data(), data.size(), flags, &result, nullptr); + return result; + } + static inline bool Decode(absl::string_view data, + DecodeFlags flags, + std::string* result, + size_t* data_used) { + return DecodeFromArray(data.data(), data.size(), flags, result, data_used); + } + static inline bool Decode(absl::string_view data, + DecodeFlags flags, + std::vector* result, + size_t* data_used) { + return DecodeFromArray(data.data(), data.size(), flags, result, data_used); + } + + private: + static const char Base64Table[]; + static const unsigned char DecodeTable[]; + + static size_t GetNextQuantum(DecodeFlags parse_flags, + bool illegal_pads, + const char* data, + size_t len, + size_t* dpos, + unsigned char qbuf[4], + bool* padded); + template + static bool DecodeFromArrayTemplate(const char* data, + size_t len, + DecodeFlags flags, + T* result, + size_t* data_used); +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::Base64; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif /* RTC_BASE_THIRD_PARTY_BASE64_BASE64_H_ */ diff --git a/pkg/apm/webrtc/rtc_base/third_party/sigslot/sigslot.h b/pkg/apm/webrtc/rtc_base/third_party/sigslot/sigslot.h new file mode 100644 index 00000000..a4de0e66 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/third_party/sigslot/sigslot.h @@ -0,0 +1,649 @@ +// sigslot.h: Signal/Slot classes +// +// Written by Sarah Thompson (sarah@telergy.com) 2002. +// +// License: Public domain. You are free to use this code however you like, with +// the proviso that the author takes on no responsibility or liability for any +// use. +// +// QUICK DOCUMENTATION +// +// (see also the full documentation at http://sigslot.sourceforge.net/) +// +// #define switches +// SIGSLOT_PURE_ISO: +// Define this to force ISO C++ compliance. This also disables all of +// the thread safety support on platforms where it is available. +// +// SIGSLOT_USE_POSIX_THREADS: +// Force use of Posix threads when using a C++ compiler other than gcc +// on a platform that supports Posix threads. (When using gcc, this is +// the default - use SIGSLOT_PURE_ISO to disable this if necessary) +// +// SIGSLOT_DEFAULT_MT_POLICY: +// Where thread support is enabled, this defaults to +// multi_threaded_global. Otherwise, the default is single_threaded. +// #define this yourself to override the default. In pure ISO mode, +// anything other than single_threaded will cause a compiler error. +// +// PLATFORM NOTES +// +// Win32: +// On Win32, the WEBRTC_WIN symbol must be #defined. Most mainstream +// compilers do this by default, but you may need to define it yourself +// if your build environment is less standard. This causes the Win32 +// thread support to be compiled in and used automatically. +// +// Unix/Linux/BSD, etc.: +// If you're using gcc, it is assumed that you have Posix threads +// available, so they are used automatically. You can override this (as +// under Windows) with the SIGSLOT_PURE_ISO switch. If you're using +// something other than gcc but still want to use Posix threads, you +// need to #define SIGSLOT_USE_POSIX_THREADS. +// +// ISO C++: +// If none of the supported platforms are detected, or if +// SIGSLOT_PURE_ISO is defined, all multithreading support is turned +// off, along with any code that might cause a pure ISO C++ environment +// to complain. Before you ask, gcc -ansi -pedantic won't compile this +// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of +// errors that aren't really there. If you feel like investigating this, +// please contact the author. +// +// +// THREADING MODES +// +// single_threaded: +// Your program is assumed to be single threaded from the point of view +// of signal/slot usage (i.e. all objects using signals and slots are +// created and destroyed from a single thread). Behaviour if objects are +// destroyed concurrently is undefined (i.e. you'll get the occasional +// segmentation fault/memory exception). +// +// multi_threaded_global: +// Your program is assumed to be multi threaded. Objects using signals +// and slots can be safely created and destroyed from any thread, even +// when connections exist. In multi_threaded_global mode, this is +// achieved by a single global mutex (actually a critical section on +// Windows because they are faster). This option uses less OS resources, +// but results in more opportunities for contention, possibly resulting +// in more context switches than are strictly necessary. +// +// multi_threaded_local: +// Behaviour in this mode is essentially the same as +// multi_threaded_global, except that each signal, and each object that +// inherits has_slots, all have their own mutex/critical section. In +// practice, this means that mutex collisions (and hence context +// switches) only happen if they are absolutely essential. However, on +// some platforms, creating a lot of mutexes can slow down the whole OS, +// so use this option with care. +// +// USING THE LIBRARY +// +// See the full documentation at http://sigslot.sourceforge.net/ +// +// Libjingle specific: +// +// This file has been modified such that has_slots and signalx do not have to be +// using the same threading requirements. E.g. it is possible to connect a +// has_slots and signal0 or +// has_slots and signal0. +// If has_slots is single threaded the user must ensure that it is not trying +// to connect or disconnect to signalx concurrently or data race may occur. +// If signalx is single threaded the user must ensure that disconnect, connect +// or signal is not happening concurrently or data race may occur. + +#ifndef RTC_BASE_THIRD_PARTY_SIGSLOT_SIGSLOT_H_ +#define RTC_BASE_THIRD_PARTY_SIGSLOT_SIGSLOT_H_ + +#include +#include +#include + +// On our copy of sigslot.h, we set single threading as default. +#define SIGSLOT_DEFAULT_MT_POLICY single_threaded + +#if defined(SIGSLOT_PURE_ISO) || \ + (!defined(WEBRTC_WIN) && !defined(__GNUG__) && \ + !defined(SIGSLOT_USE_POSIX_THREADS)) +#define _SIGSLOT_SINGLE_THREADED +#elif defined(WEBRTC_WIN) +#define _SIGSLOT_HAS_WIN32_THREADS +#include "windows.h" +#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS) +#define _SIGSLOT_HAS_POSIX_THREADS +#include +#else +#define _SIGSLOT_SINGLE_THREADED +#endif + +#ifndef SIGSLOT_DEFAULT_MT_POLICY +#ifdef _SIGSLOT_SINGLE_THREADED +#define SIGSLOT_DEFAULT_MT_POLICY single_threaded +#else +#define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local +#endif +#endif + +// TODO: change this namespace to rtc? +namespace sigslot { + +class single_threaded { + public: + void lock() {} + void unlock() {} +}; + +#ifdef _SIGSLOT_HAS_WIN32_THREADS +// The multi threading policies only get compiled in if they are enabled. +class multi_threaded_global { + public: + multi_threaded_global() { + static bool isinitialised = false; + + if (!isinitialised) { + InitializeCriticalSection(get_critsec()); + isinitialised = true; + } + } + + void lock() { EnterCriticalSection(get_critsec()); } + + void unlock() { LeaveCriticalSection(get_critsec()); } + + private: + CRITICAL_SECTION* get_critsec() { + static CRITICAL_SECTION g_critsec; + return &g_critsec; + } +}; + +class multi_threaded_local { + public: + multi_threaded_local() { InitializeCriticalSection(&m_critsec); } + + multi_threaded_local(const multi_threaded_local&) { + InitializeCriticalSection(&m_critsec); + } + + ~multi_threaded_local() { DeleteCriticalSection(&m_critsec); } + + void lock() { EnterCriticalSection(&m_critsec); } + + void unlock() { LeaveCriticalSection(&m_critsec); } + + private: + CRITICAL_SECTION m_critsec; +}; +#endif // _SIGSLOT_HAS_WIN32_THREADS + +#ifdef _SIGSLOT_HAS_POSIX_THREADS +// The multi threading policies only get compiled in if they are enabled. +class multi_threaded_global { + public: + void lock() { pthread_mutex_lock(get_mutex()); } + void unlock() { pthread_mutex_unlock(get_mutex()); } + + private: + static pthread_mutex_t* get_mutex(); +}; + +class multi_threaded_local { + public: + multi_threaded_local() { pthread_mutex_init(&m_mutex, nullptr); } + multi_threaded_local(const multi_threaded_local&) { + pthread_mutex_init(&m_mutex, nullptr); + } + ~multi_threaded_local() { pthread_mutex_destroy(&m_mutex); } + void lock() { pthread_mutex_lock(&m_mutex); } + void unlock() { pthread_mutex_unlock(&m_mutex); } + + private: + pthread_mutex_t m_mutex; +}; +#endif // _SIGSLOT_HAS_POSIX_THREADS + +template +class lock_block { + public: + mt_policy* m_mutex; + + lock_block(mt_policy* mtx) : m_mutex(mtx) { m_mutex->lock(); } + + ~lock_block() { m_mutex->unlock(); } +}; + +class _signal_base_interface; + +class has_slots_interface { + private: + typedef void (*signal_connect_t)(has_slots_interface* self, + _signal_base_interface* sender); + typedef void (*signal_disconnect_t)(has_slots_interface* self, + _signal_base_interface* sender); + typedef void (*disconnect_all_t)(has_slots_interface* self); + + const signal_connect_t m_signal_connect; + const signal_disconnect_t m_signal_disconnect; + const disconnect_all_t m_disconnect_all; + + protected: + has_slots_interface(signal_connect_t conn, + signal_disconnect_t disc, + disconnect_all_t disc_all) + : m_signal_connect(conn), + m_signal_disconnect(disc), + m_disconnect_all(disc_all) {} + + // Doesn't really need to be virtual, but is for backwards compatibility + // (it was virtual in a previous version of sigslot). + virtual ~has_slots_interface() {} + + public: + void signal_connect(_signal_base_interface* sender) { + m_signal_connect(this, sender); + } + + void signal_disconnect(_signal_base_interface* sender) { + m_signal_disconnect(this, sender); + } + + void disconnect_all() { m_disconnect_all(this); } +}; + +class _signal_base_interface { + private: + typedef void (*slot_disconnect_t)(_signal_base_interface* self, + has_slots_interface* pslot); + typedef void (*slot_duplicate_t)(_signal_base_interface* self, + const has_slots_interface* poldslot, + has_slots_interface* pnewslot); + + const slot_disconnect_t m_slot_disconnect; + const slot_duplicate_t m_slot_duplicate; + + protected: + _signal_base_interface(slot_disconnect_t disc, slot_duplicate_t dupl) + : m_slot_disconnect(disc), m_slot_duplicate(dupl) {} + + ~_signal_base_interface() {} + + public: + void slot_disconnect(has_slots_interface* pslot) { + m_slot_disconnect(this, pslot); + } + + void slot_duplicate(const has_slots_interface* poldslot, + has_slots_interface* pnewslot) { + m_slot_duplicate(this, poldslot, pnewslot); + } +}; + +class _opaque_connection { + private: + typedef void (*emit_t)(const _opaque_connection*); + template + union union_caster { + FromT from; + ToT to; + }; + + emit_t pemit; + has_slots_interface* pdest; + // Pointers to member functions may be up to 16 bytes (24 bytes for MSVC) + // for virtual classes, so make sure we have enough space to store it. +#if defined(_MSC_VER) && !defined(__clang__) + unsigned char pmethod[24]; +#else + unsigned char pmethod[16]; +#endif + + public: + template + _opaque_connection(DestT* pd, void (DestT::*pm)(Args...)) : pdest(pd) { + typedef void (DestT::*pm_t)(Args...); + static_assert(sizeof(pm_t) <= sizeof(pmethod), + "Size of slot function pointer too large."); + + std::memcpy(pmethod, &pm, sizeof(pm_t)); + + typedef void (*em_t)(const _opaque_connection* self, Args...); + union_caster caster2; + caster2.from = &_opaque_connection::emitter; + pemit = caster2.to; + } + + has_slots_interface* getdest() const { return pdest; } + + _opaque_connection duplicate(has_slots_interface* newtarget) const { + _opaque_connection res = *this; + res.pdest = newtarget; + return res; + } + + // Just calls the stored "emitter" function pointer stored at construction + // time. + template + void emit(Args... args) const { + typedef void (*em_t)(const _opaque_connection*, Args...); + union_caster caster; + caster.from = pemit; + (caster.to)(this, args...); + } + + private: + template + static void emitter(const _opaque_connection* self, Args... args) { + typedef void (DestT::*pm_t)(Args...); + pm_t pm; + static_assert(sizeof(pm_t) <= sizeof(pmethod), + "Size of slot function pointer too large."); + std::memcpy(&pm, self->pmethod, sizeof(pm_t)); + (static_cast(self->pdest)->*(pm))(args...); + } +}; + +template +class _signal_base : public _signal_base_interface, public mt_policy { + protected: + typedef std::list<_opaque_connection> connections_list; + + _signal_base() + : _signal_base_interface(&_signal_base::do_slot_disconnect, + &_signal_base::do_slot_duplicate), + m_current_iterator(m_connected_slots.end()) {} + + ~_signal_base() { disconnect_all(); } + + private: + _signal_base& operator=(_signal_base const& that); + + public: + _signal_base(const _signal_base& o) + : _signal_base_interface(&_signal_base::do_slot_disconnect, + &_signal_base::do_slot_duplicate), + m_current_iterator(m_connected_slots.end()) { + lock_block lock(this); + for (const auto& connection : o.m_connected_slots) { + connection.getdest()->signal_connect(this); + m_connected_slots.push_back(connection); + } + } + + bool is_empty() { + lock_block lock(this); + return m_connected_slots.empty(); + } + + void disconnect_all() { + lock_block lock(this); + + while (!m_connected_slots.empty()) { + has_slots_interface* pdest = m_connected_slots.front().getdest(); + m_connected_slots.pop_front(); + pdest->signal_disconnect(static_cast<_signal_base_interface*>(this)); + } + // If disconnect_all is called while the signal is firing, advance the + // current slot iterator to the end to avoid an invalidated iterator from + // being dereferenced. + m_current_iterator = m_connected_slots.end(); + } + +#if !defined(NDEBUG) + bool connected(has_slots_interface* pclass) { + lock_block lock(this); + connections_list::const_iterator it = m_connected_slots.begin(); + connections_list::const_iterator itEnd = m_connected_slots.end(); + while (it != itEnd) { + if (it->getdest() == pclass) + return true; + ++it; + } + return false; + } +#endif + + void disconnect(has_slots_interface* pclass) { + lock_block lock(this); + connections_list::iterator it = m_connected_slots.begin(); + connections_list::iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if (it->getdest() == pclass) { + // If we're currently using this iterator because the signal is firing, + // advance it to avoid it being invalidated. + if (m_current_iterator == it) { + m_current_iterator = m_connected_slots.erase(it); + } else { + m_connected_slots.erase(it); + } + pclass->signal_disconnect(static_cast<_signal_base_interface*>(this)); + return; + } + ++it; + } + } + + private: + static void do_slot_disconnect(_signal_base_interface* p, + has_slots_interface* pslot) { + _signal_base* const self = static_cast<_signal_base*>(p); + lock_block lock(self); + connections_list::iterator it = self->m_connected_slots.begin(); + connections_list::iterator itEnd = self->m_connected_slots.end(); + + while (it != itEnd) { + connections_list::iterator itNext = it; + ++itNext; + + if (it->getdest() == pslot) { + // If we're currently using this iterator because the signal is firing, + // advance it to avoid it being invalidated. + if (self->m_current_iterator == it) { + self->m_current_iterator = self->m_connected_slots.erase(it); + } else { + self->m_connected_slots.erase(it); + } + } + + it = itNext; + } + } + + static void do_slot_duplicate(_signal_base_interface* p, + const has_slots_interface* oldtarget, + has_slots_interface* newtarget) { + _signal_base* const self = static_cast<_signal_base*>(p); + lock_block lock(self); + connections_list::iterator it = self->m_connected_slots.begin(); + connections_list::iterator itEnd = self->m_connected_slots.end(); + + while (it != itEnd) { + if (it->getdest() == oldtarget) { + self->m_connected_slots.push_back(it->duplicate(newtarget)); + } + + ++it; + } + } + + protected: + connections_list m_connected_slots; + + // Used to handle a slot being disconnected while a signal is + // firing (iterating m_connected_slots). + connections_list::iterator m_current_iterator; + bool m_erase_current_iterator = false; +}; + +template +class has_slots : public has_slots_interface, public mt_policy { + private: + typedef std::set<_signal_base_interface*> sender_set; + typedef sender_set::const_iterator const_iterator; + + public: + has_slots() + : has_slots_interface(&has_slots::do_signal_connect, + &has_slots::do_signal_disconnect, + &has_slots::do_disconnect_all) {} + + has_slots(has_slots const& o) + : has_slots_interface(&has_slots::do_signal_connect, + &has_slots::do_signal_disconnect, + &has_slots::do_disconnect_all) { + lock_block lock(this); + for (auto* sender : o.m_senders) { + sender->slot_duplicate(&o, this); + m_senders.insert(sender); + } + } + + ~has_slots() { this->disconnect_all(); } + + private: + has_slots& operator=(has_slots const&); + + static void do_signal_connect(has_slots_interface* p, + _signal_base_interface* sender) { + has_slots* const self = static_cast(p); + lock_block lock(self); + self->m_senders.insert(sender); + } + + static void do_signal_disconnect(has_slots_interface* p, + _signal_base_interface* sender) { + has_slots* const self = static_cast(p); + lock_block lock(self); + self->m_senders.erase(sender); + } + + static void do_disconnect_all(has_slots_interface* p) { + has_slots* const self = static_cast(p); + lock_block lock(self); + while (!self->m_senders.empty()) { + std::set<_signal_base_interface*> senders; + senders.swap(self->m_senders); + const_iterator it = senders.begin(); + const_iterator itEnd = senders.end(); + + while (it != itEnd) { + _signal_base_interface* s = *it; + ++it; + s->slot_disconnect(p); + } + } + } + + private: + sender_set m_senders; +}; + +template +class signal_with_thread_policy : public _signal_base { + private: + typedef _signal_base base; + + protected: + typedef typename base::connections_list connections_list; + + public: + signal_with_thread_policy() {} + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(Args...)) { + lock_block lock(this); + this->m_connected_slots.push_back(_opaque_connection(pclass, pmemfun)); + pclass->signal_connect(static_cast<_signal_base_interface*>(this)); + } + + void emit(Args... args) { + lock_block lock(this); + this->m_current_iterator = this->m_connected_slots.begin(); + while (this->m_current_iterator != this->m_connected_slots.end()) { + _opaque_connection const& conn = *this->m_current_iterator; + ++(this->m_current_iterator); + conn.emit(args...); + } + } + + void operator()(Args... args) { emit(args...); } +}; + +// Alias with default thread policy. Needed because both default arguments +// and variadic template arguments must go at the end of the list, so we +// can't have both at once. +template +using signal = signal_with_thread_policy; + +// The previous verion of sigslot didn't use variadic templates, so you would +// need to write "sigslot::signal2", for example. +// Now you can just write "sigslot::signal", but these aliases +// exist for backwards compatibility. +template +using signal0 = signal_with_thread_policy; + +template +using signal1 = signal_with_thread_policy; + +template +using signal2 = signal_with_thread_policy; + +template +using signal3 = signal_with_thread_policy; + +template +using signal4 = signal_with_thread_policy; + +template +using signal5 = signal_with_thread_policy; + +template +using signal6 = signal_with_thread_policy; + +template +using signal7 = + signal_with_thread_policy; + +template +using signal8 = + signal_with_thread_policy; + +} // namespace sigslot + +#endif /* RTC_BASE_THIRD_PARTY_SIGSLOT_SIGSLOT_H_ */ diff --git a/pkg/apm/webrtc/rtc_base/thread.h b/pkg/apm/webrtc/rtc_base/thread.h new file mode 100644 index 00000000..69c74feb --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/thread.h @@ -0,0 +1,580 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_THREAD_H_ +#define RTC_BASE_THREAD_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" + +#if defined(WEBRTC_POSIX) +#include +#endif +#include "absl/base/attributes.h" +#include "absl/functional/any_invocable.h" +#include "api/function_view.h" +#include "api/location.h" +#include "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" +#include "rtc_base/checks.h" +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/socket_server.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread_annotations.h" + +#if defined(WEBRTC_WIN) +#include "rtc_base/win32.h" +#endif + +#if RTC_DCHECK_IS_ON +// Counts how many `Thread::BlockingCall` are made from within a scope and logs +// the number of blocking calls at the end of the scope. +#define RTC_LOG_THREAD_BLOCK_COUNT() \ + webrtc::Thread::ScopedCountBlockingCalls blocked_call_count_printer( \ + [func = __func__](uint32_t actual_block, uint32_t could_block) { \ + auto total = actual_block + could_block; \ + if (total) { \ + RTC_LOG(LS_WARNING) << "Blocking " << func << ": total=" << total \ + << " (actual=" << actual_block \ + << ", could=" << could_block << ")"; \ + } \ + }) + +// Adds an RTC_DCHECK_LE that checks that the number of blocking calls are +// less than or equal to a specific value. Use to avoid regressing in the +// number of blocking thread calls. +// Note: Use of this macro, requires RTC_LOG_THREAD_BLOCK_COUNT() to be called +// first. +#define RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(x) \ + do { \ + blocked_call_count_printer.set_minimum_call_count_for_callback(x + 1); \ + RTC_DCHECK_LE(blocked_call_count_printer.GetTotalBlockedCallCount(), x); \ + } while (0) +#else +#define RTC_LOG_THREAD_BLOCK_COUNT() +#define RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(x) +#endif + +namespace webrtc { + +class Thread; + +class RTC_EXPORT ThreadManager { + public: + static const int kForever = -1; + + // Singleton, constructor and destructor are private. + static ThreadManager* Instance(); + + static void Add(Thread* message_queue); + static void Remove(Thread* message_queue); + + // For testing purposes, for use with a simulated clock. + // Ensures that all message queues have processed delayed messages + // up until the current point in time. + static void ProcessAllMessageQueuesForTesting(); + + Thread* CurrentThread(); + void SetCurrentThread(Thread* thread); + // Allows changing the current thread, this is intended for tests where we + // want to simulate multiple threads running on a single physical thread. + void ChangeCurrentThreadForTest(Thread* thread); + + // Returns a thread object with its thread_ ivar set + // to whatever the OS uses to represent the thread. + // If there already *is* a Thread object corresponding to this thread, + // this method will return that. Otherwise it creates a new Thread + // object whose wrapped() method will return true, and whose + // handle will, on Win32, be opened with only synchronization privileges - + // if you need more privilegs, rather than changing this method, please + // write additional code to adjust the privileges, or call a different + // factory method of your own devising, because this one gets used in + // unexpected contexts (like inside browser plugins) and it would be a + // shame to break it. It is also conceivable on Win32 that we won't even + // be able to get synchronization privileges, in which case the result + // will have a null handle. + Thread* WrapCurrentThread(); + void UnwrapCurrentThread(); + +#if RTC_DCHECK_IS_ON + // Registers that a Send operation is to be performed between `source` and + // `target`, while checking that this does not cause a send cycle that could + // potentially cause a deadlock. + void RegisterSendAndCheckForCycles(Thread* source, Thread* target); +#endif + + private: + ThreadManager(); + ~ThreadManager(); + + ThreadManager(const ThreadManager&) = delete; + ThreadManager& operator=(const ThreadManager&) = delete; + + void SetCurrentThreadInternal(Thread* thread); + void AddInternal(Thread* message_queue); + void RemoveInternal(Thread* message_queue); + void ProcessAllMessageQueuesInternal(); +#if RTC_DCHECK_IS_ON + void RemoveFromSendGraph(Thread* thread) RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); +#endif + + // This list contains all live Threads. + std::vector message_queues_ RTC_GUARDED_BY(crit_); + + Mutex crit_; + +#if RTC_DCHECK_IS_ON + // Represents all thread seand actions by storing all send targets per thread. + // This is used by RegisterSendAndCheckForCycles. This graph has no cycles + // since we will trigger a CHECK failure if a cycle is introduced. + std::map> send_graph_ RTC_GUARDED_BY(crit_); +#endif + +#if defined(WEBRTC_POSIX) + pthread_key_t key_; +#endif + +#if defined(WEBRTC_WIN) + const DWORD key_; +#endif +}; + +// WARNING! SUBCLASSES MUST CALL Stop() IN THEIR DESTRUCTORS! See ~Thread(). + +class RTC_LOCKABLE RTC_EXPORT Thread : public TaskQueueBase { + public: + static const int kForever = -1; + + // Create a new Thread and optionally assign it to the passed + // SocketServer. Subclasses that override Clear should pass false for + // init_queue and call DoInit() from their constructor to prevent races + // with the ThreadManager using the object while the vtable is still + // being created. + explicit Thread(SocketServer* ss); + explicit Thread(std::unique_ptr ss); + + // Constructors meant for subclasses; they should call DoInit themselves and + // pass false for `do_init`, so that DoInit is called only on the fully + // instantiated class, which avoids a vptr data race. + Thread(SocketServer* ss, bool do_init); + Thread(std::unique_ptr ss, bool do_init); + + // NOTE: ALL SUBCLASSES OF Thread MUST CALL Stop() IN THEIR DESTRUCTORS (or + // guarantee Stop() is explicitly called before the subclass is destroyed). + // This is required to avoid a data race between the destructor modifying the + // vtable, and the Thread::PreRun calling the virtual method Run(). + + // NOTE: SUBCLASSES OF Thread THAT OVERRIDE Clear MUST CALL + // DoDestroy() IN THEIR DESTRUCTORS! This is required to avoid a data race + // between the destructor modifying the vtable, and the ThreadManager + // calling Clear on the object from a different thread. + ~Thread() override; + + Thread(const Thread&) = delete; + Thread& operator=(const Thread&) = delete; + + static std::unique_ptr CreateWithSocketServer(); + static std::unique_ptr Create(); + static Thread* Current(); + + // Used to catch performance regressions. Use this to disallow BlockingCall + // for a given scope. If a synchronous call is made while this is in + // effect, an assert will be triggered. + // Note that this is a single threaded class. + class ScopedDisallowBlockingCalls { + public: + ScopedDisallowBlockingCalls(); + ScopedDisallowBlockingCalls(const ScopedDisallowBlockingCalls&) = delete; + ScopedDisallowBlockingCalls& operator=(const ScopedDisallowBlockingCalls&) = + delete; + ~ScopedDisallowBlockingCalls(); + + private: + Thread* const thread_; + const bool previous_state_; + }; + +#if RTC_DCHECK_IS_ON + class ScopedCountBlockingCalls { + public: + ScopedCountBlockingCalls(std::function callback); + ScopedCountBlockingCalls(const ScopedDisallowBlockingCalls&) = delete; + ScopedCountBlockingCalls& operator=(const ScopedDisallowBlockingCalls&) = + delete; + ~ScopedCountBlockingCalls(); + + uint32_t GetBlockingCallCount() const; + uint32_t GetCouldBeBlockingCallCount() const; + uint32_t GetTotalBlockedCallCount() const; + + void set_minimum_call_count_for_callback(uint32_t minimum) { + min_blocking_calls_for_callback_ = minimum; + } + + private: + Thread* const thread_; + const uint32_t base_blocking_call_count_; + const uint32_t base_could_be_blocking_call_count_; + // The minimum number of blocking calls required in order to issue the + // result_callback_. This is used by RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN to + // tame log spam. + // By default we always issue the callback, regardless of callback count. + uint32_t min_blocking_calls_for_callback_ = 0; + std::function result_callback_; + }; + + uint32_t GetBlockingCallCount() const; + uint32_t GetCouldBeBlockingCallCount() const; +#endif + + SocketServer* socketserver(); + + // Note: The behavior of Thread has changed. When a thread is stopped, + // futher Posts and Sends will fail. However, any pending Sends and *ready* + // Posts (as opposed to unexpired delayed Posts) will be delivered before + // Get (or Peek) returns false. By guaranteeing delivery of those messages, + // we eliminate the race condition when an MessageHandler and Thread + // may be destroyed independently of each other. + virtual void Quit(); + virtual bool IsQuitting(); + virtual void Restart(); + // Not all message queues actually process messages (such as SignalThread). + // In those cases, it's important to know, before posting, that it won't be + // Processed. Normally, this would be true until IsQuitting() is true. + virtual bool IsProcessingMessagesForTesting(); + + // Amount of time until the next message can be retrieved + virtual int GetDelay(); + + bool empty() const { return size() == 0u; } + size_t size() const { + MutexLock lock(&mutex_); + return messages_.size() + delayed_messages_.size(); + } + + bool IsCurrent() const; + + // Sleeps the calling thread for the specified number of milliseconds, during + // which time no processing is performed. Returns false if sleeping was + // interrupted by a signal (POSIX only). + static bool SleepMs(int millis); + + // Sets the thread's name, for debugging. Must be called before Start(). + // If `obj` is non-null, its value is appended to `name`. + const std::string& name() const { return name_; } + bool SetName(absl::string_view name, const void* obj); + + // Sets the expected processing time in ms. The thread will write + // log messages when Dispatch() takes more time than this. + // Default is 50 ms. + void SetDispatchWarningMs(int deadline); + + // Starts the execution of the thread. + bool Start(); + + // Tells the thread to stop and waits until it is joined. + // Never call Stop on the current thread. Instead use the inherited Quit + // function which will exit the base Thread without terminating the + // underlying OS thread. + virtual void Stop(); + + // By default, Thread::Run() calls ProcessMessages(kForever). To do other + // work, override Run(). To receive and dispatch messages, call + // ProcessMessages occasionally. + virtual void Run(); + + // Convenience method to invoke a functor on another thread. + // Blocks the current thread until execution is complete. + // Ex: thread.BlockingCall([&] { result = MyFunctionReturningBool(); }); + // NOTE: This function can only be called when synchronous calls are allowed. + // See ScopedDisallowBlockingCalls for details. + // NOTE: Blocking calls are DISCOURAGED, consider if what you're doing can + // be achieved with PostTask() and callbacks instead. + void BlockingCall(FunctionView functor, + const Location& location = Location::Current()) { + BlockingCallImpl(std::move(functor), location); + } + + template , + typename = typename std::enable_if_t>> + ReturnT BlockingCall(Functor&& functor, + const Location& location = Location::Current()) { + ReturnT result; + BlockingCall([&] { result = std::forward(functor)(); }, location); + return result; + } + + // Allows BlockingCall to specified `thread`. Thread never will be + // dereferenced and will be used only for reference-based comparison, so + // instance can be safely deleted. If NDEBUG is defined and RTC_DCHECK_IS_ON + // is undefined do nothing. + void AllowInvokesToThread(Thread* thread); + + // If NDEBUG is defined and RTC_DCHECK_IS_ON is undefined do nothing. + void DisallowAllInvokes(); + // Returns true if `target` was allowed by AllowInvokesToThread() or if no + // calls were made to AllowInvokesToThread and DisallowAllInvokes. Otherwise + // returns false. + // If NDEBUG is defined and RTC_DCHECK_IS_ON is undefined always returns + // true. + bool IsInvokeToThreadAllowed(Thread* target); + + // From TaskQueueBase + void Delete() override; + + // ProcessMessages will process I/O and dispatch messages until: + // 1) cms milliseconds have elapsed (returns true) + // 2) Stop() is called (returns false) + bool ProcessMessages(int cms); + + // Returns true if this is a thread that we created using the standard + // constructor, false if it was created by a call to + // ThreadManager::WrapCurrentThread(). The main thread of an application + // is generally not owned, since the OS representation of the thread + // obviously exists before we can get to it. + // You cannot call Start on non-owned threads. + bool IsOwned(); + + // Expose private method IsRunning() for tests. + // + // DANGER: this is a terrible public API. Most callers that might want to + // call this likely do not have enough control/knowledge of the Thread in + // question to guarantee that the returned value remains true for the duration + // of whatever code is conditionally executing because of the return value! + bool RunningForTest() { return IsRunning(); } + + // These functions are public to avoid injecting test hooks. Don't call them + // outside of tests. + // This method should be called when thread is created using non standard + // method, like derived implementation of webrtc::Thread and it can not be + // started by calling Start(). This will set started flag to true and + // owned to false. This must be called from the current thread. + bool WrapCurrent(); + void UnwrapCurrent(); + + // Sets the per-thread allow-blocking-calls flag to false; this is + // irrevocable. Must be called on this thread. + void DisallowBlockingCalls() { SetAllowBlockingCalls(false); } + + protected: + class CurrentThreadSetter : CurrentTaskQueueSetter { + public: + explicit CurrentThreadSetter(Thread* thread) + : CurrentTaskQueueSetter(thread), + manager_(ThreadManager::Instance()), + previous_(manager_->CurrentThread()) { + manager_->ChangeCurrentThreadForTest(thread); + } + ~CurrentThreadSetter() { manager_->ChangeCurrentThreadForTest(previous_); } + + private: + ThreadManager* const manager_; + Thread* const previous_; + }; + + // DelayedMessage goes into a priority queue, sorted by trigger time. Messages + // with the same trigger time are processed in num_ (FIFO) order. + struct DelayedMessage { + bool operator<(const DelayedMessage& dmsg) const { + return (dmsg.run_time_ms < run_time_ms) || + ((dmsg.run_time_ms == run_time_ms) && + (dmsg.message_number < message_number)); + } + + int64_t delay_ms; // for debugging + int64_t run_time_ms; + // Monotonicaly incrementing number used for ordering of messages + // targeted to execute at the same time. + uint32_t message_number; + // std::priority_queue doesn't allow to extract elements, but functor + // is move-only and thus need to be changed when pulled out of the + // priority queue. That is ok because `functor` doesn't affect operator< + mutable absl::AnyInvocable functor; + }; + + // TaskQueueBase implementation. + void PostTaskImpl(absl::AnyInvocable task, + const PostTaskTraits& traits, + const Location& location) override; + void PostDelayedTaskImpl(absl::AnyInvocable task, + TimeDelta delay, + const PostDelayedTaskTraits& traits, + const Location& location) override; + + virtual void BlockingCallImpl(FunctionView functor, + const Location& location); + + // Perform initialization, subclasses must call this from their constructor + // if false was passed as init_queue to the Thread constructor. + void DoInit(); + + // Perform cleanup; subclasses must call this from the destructor, + // and are not expected to actually hold the lock. + void DoDestroy() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + void WakeUpSocketServer(); + + // Same as WrapCurrent except that it never fails as it does not try to + // acquire the synchronization access of the thread. The caller should never + // call Stop() or Join() on this thread. + void SafeWrapCurrent(); + + // Blocks the calling thread until this thread has terminated. + void Join(); + + static void AssertBlockingIsAllowedOnCurrentThread(); + + friend class ScopedDisallowBlockingCalls; + + private: + static const int kSlowDispatchLoggingThreshold = 50; // 50 ms + + // Get() will process I/O until: + // 1) A task is available (returns it) + // 2) cmsWait seconds have elapsed (returns empty task) + // 3) Stop() is called (returns empty task) + absl::AnyInvocable Get(int cmsWait); + void Dispatch(absl::AnyInvocable task); + + // Sets the per-thread allow-blocking-calls flag and returns the previous + // value. Must be called on this thread. + bool SetAllowBlockingCalls(bool allow); + +#if defined(WEBRTC_WIN) + static DWORD WINAPI PreRun(LPVOID context); +#else + static void* PreRun(void* pv); +#endif + + // ThreadManager calls this instead WrapCurrent() because + // ThreadManager::Instance() cannot be used while ThreadManager is + // being created. + // The method tries to get synchronization rights of the thread on Windows if + // `need_synchronize_access` is true. + bool WrapCurrentWithThreadManager(ThreadManager* thread_manager, + bool need_synchronize_access); + + // Return true if the thread is currently running. + bool IsRunning(); + + // Called by the ThreadManager when being set as the current thread. + void EnsureIsCurrentTaskQueue(); + + // Called by the ThreadManager when being unset as the current thread. + void ClearCurrentTaskQueue(); + + std::queue> messages_ RTC_GUARDED_BY(mutex_); + std::priority_queue delayed_messages_ RTC_GUARDED_BY(mutex_); + uint32_t delayed_next_num_ RTC_GUARDED_BY(mutex_); +#if RTC_DCHECK_IS_ON + uint32_t blocking_call_count_ RTC_GUARDED_BY(this) = 0; + uint32_t could_be_blocking_call_count_ RTC_GUARDED_BY(this) = 0; + std::vector allowed_threads_ RTC_GUARDED_BY(this); + bool invoke_policy_enabled_ RTC_GUARDED_BY(this) = false; +#endif + mutable Mutex mutex_; + bool fInitialized_; + bool fDestroyed_; + + std::atomic stop_; + + // The SocketServer might not be owned by Thread. + SocketServer* const ss_; + // Used if SocketServer ownership lies with `this`. + std::unique_ptr own_ss_; + + std::string name_; + + // TODO(tommi): Add thread checks for proper use of control methods. + // Ideally we should be able to just use PlatformThread. + +#if defined(WEBRTC_POSIX) + pthread_t thread_ = 0; +#endif + +#if defined(WEBRTC_WIN) + HANDLE thread_ = nullptr; + DWORD thread_id_ = 0; +#endif + + // Indicates whether or not ownership of the worker thread lies with + // this instance or not. (i.e. owned_ == !wrapped). + // Must only be modified when the worker thread is not running. + bool owned_ = true; + + // Only touched from the worker thread itself. + bool blocking_calls_allowed_ = true; + + std::unique_ptr + task_queue_registration_; + + friend class ThreadManager; + + int dispatch_warning_ms_ RTC_GUARDED_BY(this) = kSlowDispatchLoggingThreshold; +}; + +// AutoThread automatically installs itself at construction +// uninstalls at destruction, if a Thread object is +// _not already_ associated with the current OS thread. +// +// NOTE: *** This class should only be used by tests *** +// +class AutoThread : public Thread { + public: + AutoThread(); + ~AutoThread() override; + + AutoThread(const AutoThread&) = delete; + AutoThread& operator=(const AutoThread&) = delete; +}; + +// AutoSocketServerThread automatically installs itself at +// construction and uninstalls at destruction. If a Thread object is +// already associated with the current OS thread, it is temporarily +// disassociated and restored by the destructor. + +class AutoSocketServerThread : public Thread { + public: + explicit AutoSocketServerThread(SocketServer* ss); + ~AutoSocketServerThread() override; + + AutoSocketServerThread(const AutoSocketServerThread&) = delete; + AutoSocketServerThread& operator=(const AutoSocketServerThread&) = delete; + + private: + Thread* old_thread_; +}; +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::AutoSocketServerThread; +using ::webrtc::AutoThread; +using ::webrtc::Thread; +using ::webrtc::ThreadManager; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_THREAD_H_ diff --git a/pkg/apm/webrtc/rtc_base/thread_annotations.h b/pkg/apm/webrtc/rtc_base/thread_annotations.h new file mode 100644 index 00000000..689f6681 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/thread_annotations.h @@ -0,0 +1,98 @@ +// +// Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// +// Borrowed from +// https://code.google.com/p/gperftools/source/browse/src/base/thread_annotations.h +// but adapted for clang attributes instead of the gcc. +// +// This header file contains the macro definitions for thread safety +// annotations that allow the developers to document the locking policies +// of their multi-threaded code. The annotations can also help program +// analysis tools to identify potential thread safety issues. + +#ifndef RTC_BASE_THREAD_ANNOTATIONS_H_ +#define RTC_BASE_THREAD_ANNOTATIONS_H_ + +#if defined(__clang__) && (!defined(SWIG)) +#define RTC_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define RTC_THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +// Document if a shared variable/field needs to be protected by a lock. +// GUARDED_BY allows the user to specify a particular lock that should be +// held when accessing the annotated variable. +#define RTC_GUARDED_BY(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +// Document if the memory location pointed to by a pointer should be guarded +// by a lock when dereferencing the pointer. Note that a pointer variable to a +// shared memory location could itself be a shared variable. For example, if a +// shared global pointer q, which is guarded by mu1, points to a shared memory +// location that is guarded by mu2, q should be annotated as follows: +// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2); +#define RTC_PT_GUARDED_BY(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +// Document the acquisition order between locks that can be held +// simultaneously by a thread. For any two locks that need to be annotated +// to establish an acquisition order, only one of them needs the annotation. +// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER +// and ACQUIRED_BEFORE.) +#define RTC_ACQUIRED_AFTER(x) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(x)) +#define RTC_ACQUIRED_BEFORE(x) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(x)) + +// The following three annotations document the lock requirements for +// functions/methods. + +// Document if a function expects certain locks to be held before it is called +#define RTC_EXCLUSIVE_LOCKS_REQUIRED(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) +#define RTC_SHARED_LOCKS_REQUIRED(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) + +// Document the locks acquired in the body of the function. These locks +// cannot be held when calling this function (as google3's Mutex locks are +// non-reentrant). +#define RTC_LOCKS_EXCLUDED(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +// Document the lock the annotated function returns without acquiring it. +#define RTC_LOCK_RETURNED(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +// Document if a class/type is a lockable type (such as the Mutex class). +#define RTC_LOCKABLE RTC_THREAD_ANNOTATION_ATTRIBUTE__(lockable) + +// Document if a class is a scoped lockable type (such as the MutexLock class). +#define RTC_SCOPED_LOCKABLE RTC_THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +// The following annotations specify lock and unlock primitives. +#define RTC_EXCLUSIVE_LOCK_FUNCTION(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) + +#define RTC_SHARED_LOCK_FUNCTION(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) + +#define RTC_EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) + +#define RTC_SHARED_TRYLOCK_FUNCTION(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) + +#define RTC_UNLOCK_FUNCTION(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) + +#define RTC_ASSERT_EXCLUSIVE_LOCK(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) + +// An escape hatch for thread safety analysis to ignore the annotated function. +#define RTC_NO_THREAD_SAFETY_ANALYSIS \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +#endif // RTC_BASE_THREAD_ANNOTATIONS_H_ diff --git a/pkg/apm/webrtc/rtc_base/time_utils.cc b/pkg/apm/webrtc/rtc_base/time_utils.cc new file mode 100644 index 00000000..d8ce42a5 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/time_utils.cc @@ -0,0 +1,258 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#if defined(WEBRTC_POSIX) +#include +#endif + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/system_time.h" +#include "rtc_base/time_utils.h" +#if defined(WEBRTC_WIN) +#include "rtc_base/win32.h" +#endif +#if defined(WEBRTC_WIN) +#include +#endif + +namespace webrtc { + +#if defined(WEBRTC_WIN) || defined(WINUWP) +// FileTime (January 1st 1601) to Unix time (January 1st 1970) +// offset in units of 100ns. +static constexpr uint64_t kFileTimeToUnixTimeEpochOffset = + 116444736000000000ULL; +static constexpr uint64_t kFileTimeToMicroSeconds = 10LL; +#endif + +ClockInterface* g_clock = nullptr; + +ClockInterface* SetClockForTesting(ClockInterface* clock) { + ClockInterface* prev = g_clock; + g_clock = clock; + return prev; +} + +ClockInterface* GetClockForTesting() { + return g_clock; +} + +#if defined(WINUWP) + +namespace { + +class TimeHelper final { + public: + TimeHelper(const TimeHelper&) = delete; + + // Resets the clock based upon an NTP server. This routine must be called + // prior to the main system start-up to ensure all clocks are based upon + // an NTP server time if NTP synchronization is required. No critical + // section is used thus this method must be called prior to any clock + // routines being used. + static void SyncWithNtp(int64_t ntp_server_time_ms) { + auto& singleton = Singleton(); + TIME_ZONE_INFORMATION time_zone; + GetTimeZoneInformation(&time_zone); + int64_t time_zone_bias_ns = + dchecked_cast(time_zone.Bias) * 60 * 1000 * 1000 * 1000; + singleton.app_start_time_ns_ = + (ntp_server_time_ms - kNTPTimeToUnixTimeEpochOffset) * 1000000 - + time_zone_bias_ns; + singleton.UpdateReferenceTime(); + } + + // Returns the number of nanoseconds that have passed since unix epoch. + static int64_t TicksNs() { + auto& singleton = Singleton(); + int64_t result = 0; + LARGE_INTEGER qpcnt; + QueryPerformanceCounter(&qpcnt); + result = dchecked_cast( + (dchecked_cast(qpcnt.QuadPart) * 100000 / + dchecked_cast(singleton.os_ticks_per_second_)) * + 10000); + result = singleton.app_start_time_ns_ + result - + singleton.time_since_os_start_ns_; + return result; + } + + private: + TimeHelper() { + TIME_ZONE_INFORMATION time_zone; + GetTimeZoneInformation(&time_zone); + int64_t time_zone_bias_ns = + dchecked_cast(time_zone.Bias) * 60 * 1000 * 1000 * 1000; + FILETIME ft; + // This will give us system file in UTC format. + GetSystemTimeAsFileTime(&ft); + LARGE_INTEGER li; + li.HighPart = ft.dwHighDateTime; + li.LowPart = ft.dwLowDateTime; + + app_start_time_ns_ = (li.QuadPart - kFileTimeToUnixTimeEpochOffset) * 100 - + time_zone_bias_ns; + + UpdateReferenceTime(); + } + + static TimeHelper& Singleton() { + static TimeHelper singleton; + return singleton; + } + + void UpdateReferenceTime() { + LARGE_INTEGER qpfreq; + QueryPerformanceFrequency(&qpfreq); + os_ticks_per_second_ = dchecked_cast(qpfreq.QuadPart); + + LARGE_INTEGER qpcnt; + QueryPerformanceCounter(&qpcnt); + time_since_os_start_ns_ = dchecked_cast( + (dchecked_cast(qpcnt.QuadPart) * 100000 / + dchecked_cast(os_ticks_per_second_)) * + 10000); + } + + private: + static constexpr uint64_t kNTPTimeToUnixTimeEpochOffset = 2208988800000L; + + // The number of nanoseconds since unix system epoch + int64_t app_start_time_ns_; + // The number of nanoseconds since the OS started + int64_t time_since_os_start_ns_; + // The OS calculated ticks per second + int64_t os_ticks_per_second_; +}; + +} // namespace + +void SyncWithNtp(int64_t time_from_ntp_server_ms) { + TimeHelper::SyncWithNtp(time_from_ntp_server_ms); +} + +int64_t WinUwpSystemTimeNanos() { + return TimeHelper::TicksNs(); +} + +#endif // defined(WINUWP) + +int64_t SystemTimeMillis() { + return static_cast(SystemTimeNanos() / kNumNanosecsPerMillisec); +} + +int64_t TimeNanos() { + if (g_clock) { + return g_clock->TimeNanos(); + } + return SystemTimeNanos(); +} + +uint32_t Time32() { + return static_cast(TimeNanos() / kNumNanosecsPerMillisec); +} + +int64_t TimeMillis() { + return TimeNanos() / kNumNanosecsPerMillisec; +} + +int64_t TimeMicros() { + return TimeNanos() / kNumNanosecsPerMicrosec; +} + +int64_t TimeAfter(int64_t elapsed) { + RTC_DCHECK_GE(elapsed, 0); + return TimeMillis() + elapsed; +} + +int32_t TimeDiff32(uint32_t later, uint32_t earlier) { + return later - earlier; +} + +int64_t TimeDiff(int64_t later, int64_t earlier) { + return later - earlier; +} + +int64_t TmToSeconds(const tm& tm) { + static short int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + static short int cumul_mdays[12] = {0, 31, 59, 90, 120, 151, + 181, 212, 243, 273, 304, 334}; + int year = tm.tm_year + 1900; + int month = tm.tm_mon; + int day = tm.tm_mday - 1; // Make 0-based like the rest. + int hour = tm.tm_hour; + int min = tm.tm_min; + int sec = tm.tm_sec; + + bool expiry_in_leap_year = + (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); + + if (year < 1970) + return -1; + if (month < 0 || month > 11) + return -1; + if (day < 0 || day >= mdays[month] + (expiry_in_leap_year && month == 2 - 1)) + return -1; + if (hour < 0 || hour > 23) + return -1; + if (min < 0 || min > 59) + return -1; + if (sec < 0 || sec > 59) + return -1; + + day += cumul_mdays[month]; + + // Add number of leap days between 1970 and the expiration year, inclusive. + day += ((year / 4 - 1970 / 4) - (year / 100 - 1970 / 100) + + (year / 400 - 1970 / 400)); + + // We will have added one day too much above if expiration is during a leap + // year, and expiration is in January or February. + if (expiry_in_leap_year && month <= 2 - 1) // `month` is zero based. + day -= 1; + + // Combine all variables into seconds from 1970-01-01 00:00 (except `month` + // which was accumulated into `day` above). + return (((static_cast(year - 1970) * 365 + day) * 24 + hour) * 60 + + min) * + 60 + + sec; +} + +int64_t TimeUTCMicros() { + if (g_clock) { + return g_clock->TimeNanos() / kNumNanosecsPerMicrosec; + } +#if defined(WEBRTC_POSIX) + struct timeval time; + gettimeofday(&time, nullptr); + // Convert from second (1.0) and microsecond (1e-6). + return (static_cast(time.tv_sec) * kNumMicrosecsPerSec + + time.tv_usec); +#elif defined(WEBRTC_WIN) + FILETIME ft; + // This will give us system file in UTC format in multiples of 100ns. + GetSystemTimeAsFileTime(&ft); + LARGE_INTEGER li; + li.HighPart = ft.dwHighDateTime; + li.LowPart = ft.dwLowDateTime; + return (li.QuadPart - kFileTimeToUnixTimeEpochOffset) / + kFileTimeToMicroSeconds; +#endif +} + +int64_t TimeUTCMillis() { + return TimeUTCMicros() / kNumMicrosecsPerMillisec; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/time_utils.h b/pkg/apm/webrtc/rtc_base/time_utils.h new file mode 100644 index 00000000..30cb8b86 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/time_utils.h @@ -0,0 +1,171 @@ +/* + * Copyright 2005 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_TIME_UTILS_H_ +#define RTC_BASE_TIME_UTILS_H_ + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/system_time.h" + +namespace webrtc { + +static const int64_t kNumMillisecsPerSec = INT64_C(1000); +static const int64_t kNumMicrosecsPerSec = INT64_C(1000000); +static const int64_t kNumNanosecsPerSec = INT64_C(1000000000); + +static const int64_t kNumMicrosecsPerMillisec = + kNumMicrosecsPerSec / kNumMillisecsPerSec; +static const int64_t kNumNanosecsPerMillisec = + kNumNanosecsPerSec / kNumMillisecsPerSec; +static const int64_t kNumNanosecsPerMicrosec = + kNumNanosecsPerSec / kNumMicrosecsPerSec; + +// Elapsed milliseconds between NTP base, 1900 January 1 00:00 GMT +// (see https://tools.ietf.org/html/rfc868), and January 1 00:00 GMT 1970 +// epoch. This is useful when converting between the NTP time base and the +// time base used in RTCP reports. +constexpr int64_t kNtpJan1970Millisecs = 2'208'988'800 * kNumMillisecsPerSec; + +// TODO(honghaiz): Define a type for the time value specifically. + +class ClockInterface { + public: + virtual ~ClockInterface() {} + virtual int64_t TimeNanos() const = 0; +}; + +// Sets the global source of time. This is useful mainly for unit tests. +// +// Returns the previously set ClockInterface, or nullptr if none is set. +// +// Does not transfer ownership of the clock. SetClockForTesting(nullptr) +// should be called before the ClockInterface is deleted. +// +// This method is not thread-safe; it should only be used when no other thread +// is running (for example, at the start/end of a unit test, or start/end of +// main()). +// +// TODO(deadbeef): Instead of having functions that access this global +// ClockInterface, we may want to pass the ClockInterface into everything +// that uses it, eliminating the need for a global variable and this function. +RTC_EXPORT ClockInterface* SetClockForTesting(ClockInterface* clock); + +// Returns previously set clock, or nullptr if no custom clock is being used. +RTC_EXPORT ClockInterface* GetClockForTesting(); + +#if defined(WINUWP) +// Synchronizes the current clock based upon an NTP server's epoch in +// milliseconds. +void SyncWithNtp(int64_t time_from_ntp_server_ms); + +// Returns the current time in nanoseconds. The clock is synchonized with the +// system wall clock time upon instatiation. It may also be synchronized using +// the SyncWithNtp() function above. Please note that the clock will most likely +// drift away from the system wall clock time as time goes by. +int64_t WinUwpSystemTimeNanos(); +#endif // defined(WINUWP) + +// Returns the actual system time, even if a clock is set for testing. +// Useful for timeouts while using a test clock, or for logging. +int64_t SystemTimeMillis(); + +// Returns the current time in milliseconds in 32 bits. +uint32_t Time32(); + +// Returns the current time in milliseconds in 64 bits. +RTC_EXPORT int64_t TimeMillis(); +// Deprecated. Do not use this in any new code. +inline int64_t Time() { + return TimeMillis(); +} + +// Returns the current time in microseconds. +RTC_EXPORT int64_t TimeMicros(); + +// Returns the current time in nanoseconds. +RTC_EXPORT int64_t TimeNanos(); + +// Returns a future timestamp, 'elapsed' milliseconds from now. +int64_t TimeAfter(int64_t elapsed); + +// Number of milliseconds that would elapse between 'earlier' and 'later' +// timestamps. The value is negative if 'later' occurs before 'earlier'. +int64_t TimeDiff(int64_t later, int64_t earlier); +int32_t TimeDiff32(uint32_t later, uint32_t earlier); + +// The number of milliseconds that have elapsed since 'earlier'. +inline int64_t TimeSince(int64_t earlier) { + return TimeMillis() - earlier; +} + +// The number of milliseconds that will elapse between now and 'later'. +inline int64_t TimeUntil(int64_t later) { + return later - TimeMillis(); +} + +// Convert from tm, which is relative to 1900-01-01 00:00 to number of +// seconds from 1970-01-01 00:00 ("epoch"). Don't return time_t since that +// is still 32 bits on many systems. +int64_t TmToSeconds(const tm& tm); + +// Return the number of microseconds since January 1, 1970, UTC. +// Useful mainly when producing logs to be correlated with other +// devices, and when the devices in question all have properly +// synchronized clocks. +// +// Note that this function obeys the system's idea about what the time +// is. It is not guaranteed to be monotonic; it will jump in case the +// system time is changed, e.g., by some other process calling +// settimeofday. Always use webrtc::TimeMicros(), not this function, for +// measuring time intervals and timeouts. +RTC_EXPORT int64_t TimeUTCMicros(); + +// Return the number of milliseconds since January 1, 1970, UTC. +// See above. +RTC_EXPORT int64_t TimeUTCMillis(); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::ClockInterface; +using ::webrtc::GetClockForTesting; +using ::webrtc::kNtpJan1970Millisecs; +using ::webrtc::kNumMicrosecsPerMillisec; +using ::webrtc::kNumMicrosecsPerSec; +using ::webrtc::kNumMillisecsPerSec; +using ::webrtc::kNumNanosecsPerMicrosec; +using ::webrtc::kNumNanosecsPerMillisec; +using ::webrtc::kNumNanosecsPerSec; +using ::webrtc::SetClockForTesting; +using ::webrtc::SystemTimeMillis; +using ::webrtc::Time; +using ::webrtc::Time32; +using ::webrtc::TimeAfter; +using ::webrtc::TimeDiff; +using ::webrtc::TimeDiff32; +using ::webrtc::TimeMicros; +using ::webrtc::TimeMillis; +using ::webrtc::TimeNanos; +using ::webrtc::TimeSince; +using ::webrtc::TimeUntil; +using ::webrtc::TimeUTCMicros; +using ::webrtc::TimeUTCMillis; +using ::webrtc::TmToSeconds; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_TIME_UTILS_H_ diff --git a/pkg/apm/webrtc/rtc_base/timestamp_aligner.h b/pkg/apm/webrtc/rtc_base/timestamp_aligner.h new file mode 100644 index 00000000..d060a354 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/timestamp_aligner.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_TIMESTAMP_ALIGNER_H_ +#define RTC_BASE_TIMESTAMP_ALIGNER_H_ + +#include + +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +// The TimestampAligner class helps translating timestamps of a capture system +// into the same timescale as is used by webrtc::TimeMicros(). Some capture +// systems provide timestamps, which comes from the capturing hardware (camera +// or sound card) or stamped close to the capturing hardware. Such timestamps +// are more accurate (less jittery) than reading the system clock, but may have +// a different epoch and unknown clock drift. Frame timestamps in webrtc should +// use webrtc::TimeMicros (system monotonic time), and this class provides a +// filter which lets us use the webrtc::TimeMicros timescale, and at the same +// time take advantage of higher accuracy of the capturer's clock. + +// This class is not thread safe, so all calls to it must be synchronized +// externally. +class RTC_EXPORT TimestampAligner { + public: + TimestampAligner(); + ~TimestampAligner(); + + TimestampAligner(const TimestampAligner&) = delete; + TimestampAligner& operator=(const TimestampAligner&) = delete; + + public: + // Minimum difference of two timestamps generated by + // "TranslateTimestamp(int64_t capturer_time_us, int64_t system_time_us)" + // This avoids the caller from getting two timestamps with the same + // millisecond. + static constexpr int64_t kMinFrameIntervalUs = kNumMicrosecsPerMillisec; + + // Translates timestamps of a capture system to the same timescale as is used + // by webrtc::TimeMicros(). `capturer_time_us` is assumed to be accurate, but + // with an unknown epoch and clock drift. `system_time_us` is + // time according to webrtc::TimeMicros(), preferably read as soon as + // possible when the frame is captured. It may have poor accuracy + // due to poor resolution or scheduling delays. Returns the + // translated timestamp. + int64_t TranslateTimestamp(int64_t capturer_time_us, int64_t system_time_us); + + // Returns the translated timestamp without updating the states. This is to + // allow TimestampAligner to translate capturer time into system clock based + // on earlier observations. It won't guarantee monotonicity. + int64_t TranslateTimestamp(int64_t capturer_time_us) const; + + protected: + // Update the estimated offset between capturer's time and system monotonic + // time. + int64_t UpdateOffset(int64_t capturer_time_us, int64_t system_time_us); + + // Clip timestamp, return value is always + // <= `system_time_us`, and + // >= min(`prev_translated_time_us_` + `kMinFrameIntervalUs`, + // `system_time_us`). + int64_t ClipTimestamp(int64_t filtered_time_us, int64_t system_time_us); + + private: + // State for the timestamp translation. + int frames_seen_; + // Estimated offset between capturer's time and system monotonic time. + int64_t offset_us_; + + // State for the ClipTimestamp method, applied after the filter. + // A large negative clock drift of the capturer tends to push translated + // timestamps into the future. `clip_bias_us_` is subtracted from the + // translated timestamps, to get them back from the future. + int64_t clip_bias_us_; + // Used to ensure that translated timestamps are monotonous. + int64_t prev_translated_time_us_; + // Offset between `prev_translated_time_us_` and the corresponding capturer + // time. + int64_t prev_time_offset_us_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::TimestampAligner; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_TIMESTAMP_ALIGNER_H_ diff --git a/pkg/apm/webrtc/rtc_base/trace_categories.h b/pkg/apm/webrtc/rtc_base/trace_categories.h new file mode 100644 index 00000000..ef4070ca --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/trace_categories.h @@ -0,0 +1,37 @@ +/* + * 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 RTC_BASE_TRACE_CATEGORIES_H_ +#define RTC_BASE_TRACE_CATEGORIES_H_ + +#if defined(RTC_USE_PERFETTO) + +#define PERFETTO_ENABLE_LEGACY_TRACE_EVENTS 1 + +#include "rtc_base/system/rtc_export.h" +#include "third_party/perfetto/include/perfetto/tracing/track_event.h" // IWYU pragma: export +#include "third_party/perfetto/include/perfetto/tracing/track_event_category_registry.h" +#include "third_party/perfetto/include/perfetto/tracing/track_event_legacy.h" // IWYU pragma: export + +PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES("webrtc-test"); + +PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS( + webrtc, + RTC_EXPORT, + perfetto::Category("webrtc"), + perfetto::Category("webrtc_stats"), + perfetto::Category(TRACE_DISABLED_BY_DEFAULT("webrtc")), + perfetto::Category(TRACE_DISABLED_BY_DEFAULT("webrtc_stats"))); + +PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(webrtc); + +#endif // RTC_USE_PERFETTO + +#endif // RTC_BASE_TRACE_CATEGORIES_H_ diff --git a/pkg/apm/webrtc/rtc_base/trace_event.h b/pkg/apm/webrtc/rtc_base/trace_event.h new file mode 100644 index 00000000..196d45bc --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/trace_event.h @@ -0,0 +1,15 @@ +#ifndef RTC_BASE_TRACE_EVENT_H_ +#define RTC_BASE_TRACE_EVENT_H_ + +// Stub - perfetto tracing not needed for APM standalone build +#define TRACE_EVENT0(category, name) +#define TRACE_EVENT1(category, name, arg1_name, arg1_val) +#define TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_INSTANT0(category, name) +#define TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) +#define TRACE_EVENT_BEGIN0(category, name) +#define TRACE_EVENT_END0(category, name) +#define TRACE_EVENT_ASYNC_BEGIN0(category, name, id) +#define TRACE_EVENT_ASYNC_END0(category, name, id) + +#endif // RTC_BASE_TRACE_EVENT_H_ diff --git a/pkg/apm/webrtc/rtc_base/type_traits.h b/pkg/apm/webrtc/rtc_base/type_traits.h new file mode 100644 index 00000000..9f17acb5 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/type_traits.h @@ -0,0 +1,152 @@ +/* + * Copyright 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. + */ + +#ifndef RTC_BASE_TYPE_TRAITS_H_ +#define RTC_BASE_TYPE_TRAITS_H_ + +#include +#include +#include + +namespace webrtc { + +// Determines if the given class has zero-argument .data() and .size() methods +// whose return values are convertible to T* and size_t, respectively. +template +class HasDataAndSize { + private: + template < + typename C, + typename std::enable_if< + std::is_convertible().data()), T*>::value && + std::is_convertible().size()), + std::size_t>::value>::type* = nullptr> + static int Test(int); + + template + static char Test(...); + + public: + static constexpr bool value = std::is_same(0)), int>::value; +}; + +namespace test_has_data_and_size { + +template +struct Test1 { + DR data(); + SR size(); +}; +static_assert(HasDataAndSize, int>::value, ""); +static_assert(HasDataAndSize, const int>::value, ""); +static_assert(HasDataAndSize, const int>::value, ""); +static_assert(!HasDataAndSize, int>::value, + "implicit cast of const int* to int*"); +static_assert(!HasDataAndSize, int>::value, + "implicit cast of char* to int*"); + +struct Test2 { + int* data; + size_t size; +}; +static_assert(!HasDataAndSize::value, + ".data and .size aren't functions"); + +struct Test3 { + int* data(); +}; +static_assert(!HasDataAndSize::value, ".size() is missing"); + +class Test4 { + int* data(); + size_t size(); +}; +static_assert(!HasDataAndSize::value, + ".data() and .size() are private"); + +} // namespace test_has_data_and_size + +namespace type_traits_impl { + +// Determines if the given type is an enum that converts implicitly to +// an integral type. +template +struct IsIntEnum { + private: + // This overload is used if the type is an enum, and unary plus + // compiles and turns it into an integral type. + template ::value && + std::is_integral())>::value>::type* = + nullptr> + static int Test(int); + + // Otherwise, this overload is used. + template + static char Test(...); + + public: + static constexpr bool value = + std::is_same::type>(0)), + int>::value; +}; + +} // namespace type_traits_impl + +// Determines if the given type is integral, or an enum that +// converts implicitly to an integral type. +template +struct IsIntlike { + private: + using X = typename std::remove_reference::type; + + public: + static constexpr bool value = + std::is_integral::value || type_traits_impl::IsIntEnum::value; +}; + +namespace test_enum_intlike { + +enum E1 { e1 }; +enum { e2 }; +enum class E3 { e3 }; +struct S {}; + +static_assert(type_traits_impl::IsIntEnum::value, ""); +static_assert(type_traits_impl::IsIntEnum::value, ""); +static_assert(!type_traits_impl::IsIntEnum::value, ""); +static_assert(!type_traits_impl::IsIntEnum::value, ""); +static_assert(!type_traits_impl::IsIntEnum::value, ""); +static_assert(!type_traits_impl::IsIntEnum::value, ""); + +static_assert(IsIntlike::value, ""); +static_assert(IsIntlike::value, ""); +static_assert(!IsIntlike::value, ""); +static_assert(IsIntlike::value, ""); +static_assert(!IsIntlike::value, ""); +static_assert(!IsIntlike::value, ""); + +} // namespace test_enum_intlike + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +template +using HasDataAndSize = ::webrtc::HasDataAndSize; +template +using IsIntlike = ::webrtc::IsIntlike; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_TYPE_TRAITS_H_ diff --git a/pkg/apm/webrtc/rtc_base/unique_id_generator.h b/pkg/apm/webrtc/rtc_base/unique_id_generator.h new file mode 100644 index 00000000..5fe3366e --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/unique_id_generator.h @@ -0,0 +1,160 @@ +/* + * 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 RTC_BASE_UNIQUE_ID_GENERATOR_H_ +#define RTC_BASE_UNIQUE_ID_GENERATOR_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/sequence_checker.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/system/no_unique_address.h" + +namespace webrtc { + +// This class will generate numbers. A common use case is for identifiers. +// The generated numbers will be unique, in the local scope of the generator. +// This means that a generator will never generate the same number twice. +// The generator can also be initialized with a sequence of known ids. +// In such a case, it will never generate an id from that list. +// Recommendedations: +// * Prefer unsigned types. +// * Prefer larger types (uint8_t will run out quickly). +template +class UniqueNumberGenerator { + public: + typedef TIntegral value_type; + UniqueNumberGenerator(); + // Creates a generator that will never return any value from the given list. + explicit UniqueNumberGenerator(ArrayView known_ids); + ~UniqueNumberGenerator(); + + // Generates a number that this generator has never produced before. + // If there are no available numbers to generate, this method will fail + // with an `RTC_CHECK`. + TIntegral GenerateNumber(); + + // Alias for GenerateId, used for allowing typed testing + TIntegral Generate() { return GenerateNumber(); } + + // Adds an id that this generator should no longer generate. + // Return value indicates whether the ID was hitherto unknown. + bool AddKnownId(TIntegral value); + + private: + RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_{ + webrtc::SequenceChecker::kDetached}; + static_assert(std::is_integral::value, "Must be integral type."); + TIntegral counter_ RTC_GUARDED_BY(sequence_checker_); + std::set known_ids_ RTC_GUARDED_BY(sequence_checker_); +}; + +// This class will generate unique ids. Ids are 32 bit unsigned integers. +// The generated ids will be unique, in the local scope of the generator. +// This means that a generator will never generate the same id twice. +// The generator can also be initialized with a sequence of known ids. +// In such a case, it will never generate an id from that list. +class UniqueRandomIdGenerator { + public: + typedef uint32_t value_type; + UniqueRandomIdGenerator(); + // Create a generator that will never return any value from the given list. + explicit UniqueRandomIdGenerator(ArrayView known_ids); + ~UniqueRandomIdGenerator(); + + // Generates a random id that this generator has never produced before. + // This method becomes more expensive with each use, as the probability of + // collision for the randomly generated numbers increases. + uint32_t GenerateId(); + + // Alias for GenerateId, used for allowing typed testing + uint32_t Generate() { return GenerateId(); } + + // Adds an id that this generator should no longer generate. + // Return value indicates whether the ID was hitherto unknown. + bool AddKnownId(uint32_t value); + + private: + // TODO(bugs.webrtc.org/12666): This lock is needed due to an instance in + // SdpOfferAnswerHandler being shared between threads. + Mutex mutex_; + std::set known_ids_ RTC_GUARDED_BY(&mutex_); +}; + +// This class will generate strings. A common use case is for identifiers. +// The generated strings will be unique, in the local scope of the generator. +// This means that a generator will never generate the same string twice. +// The generator can also be initialized with a sequence of known ids. +// In such a case, it will never generate an id from that list. +class UniqueStringGenerator { + public: + typedef std::string value_type; + UniqueStringGenerator(); + explicit UniqueStringGenerator(ArrayView known_ids); + ~UniqueStringGenerator(); + + std::string GenerateString(); + // Alias for GenerateString, used for allowing typed testing + std::string Generate() { return GenerateString(); } + + // Adds an id that this generator should no longer generate. + // Return value indicates whether the ID was hitherto unknown. + bool AddKnownId(absl::string_view value); + + private: + // This implementation will be simple and will generate "0", "1", ... + UniqueNumberGenerator unique_number_generator_; +}; + +template +UniqueNumberGenerator::UniqueNumberGenerator() : counter_(0) {} + +template +UniqueNumberGenerator::UniqueNumberGenerator( + ArrayView known_ids) + : counter_(0), known_ids_(known_ids.begin(), known_ids.end()) {} + +template +UniqueNumberGenerator::~UniqueNumberGenerator() {} + +template +TIntegral UniqueNumberGenerator::GenerateNumber() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + while (true) { + RTC_CHECK_LT(counter_, std::numeric_limits::max()); + auto pair = known_ids_.insert(counter_++); + if (pair.second) { + return *pair.first; + } + } +} + +template +bool UniqueNumberGenerator::AddKnownId(TIntegral value) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + return known_ids_.insert(value).second; +} +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::UniqueNumberGenerator; +using ::webrtc::UniqueRandomIdGenerator; +using ::webrtc::UniqueStringGenerator; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_UNIQUE_ID_GENERATOR_H_ diff --git a/pkg/apm/webrtc/rtc_base/units/unit_base.h b/pkg/apm/webrtc/rtc_base/units/unit_base.h new file mode 100644 index 00000000..2820c13b --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/units/unit_base.h @@ -0,0 +1,311 @@ +/* + * 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 RTC_BASE_UNITS_UNIT_BASE_H_ +#define RTC_BASE_UNITS_UNIT_BASE_H_ + +#include + +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/divide_round.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { +namespace rtc_units_impl { + +// UnitBase is a base class for implementing custom value types with a specific +// unit. It provides type safety and commonly useful operations. The underlying +// storage is always an int64_t, it's up to the unit implementation to choose +// what scale it represents. +// +// It's used like: +// class MyUnit: public UnitBase {...}; +// +// Unit_T is the subclass representing the specific unit. +template +class UnitBase { + public: + UnitBase() = delete; + static constexpr Unit_T Zero() { return Unit_T(0); } + static constexpr Unit_T PlusInfinity() { return Unit_T(PlusInfinityVal()); } + static constexpr Unit_T MinusInfinity() { return Unit_T(MinusInfinityVal()); } + + constexpr bool IsZero() const { return value_ == 0; } + constexpr bool IsFinite() const { return !IsInfinite(); } + constexpr bool IsInfinite() const { + return value_ == PlusInfinityVal() || value_ == MinusInfinityVal(); + } + constexpr bool IsPlusInfinity() const { return value_ == PlusInfinityVal(); } + constexpr bool IsMinusInfinity() const { + return value_ == MinusInfinityVal(); + } + + constexpr bool operator==(const UnitBase& other) const { + return value_ == other.value_; + } + constexpr bool operator!=(const UnitBase& other) const { + return value_ != other.value_; + } + constexpr bool operator<=(const UnitBase& other) const { + return value_ <= other.value_; + } + constexpr bool operator>=(const UnitBase& other) const { + return value_ >= other.value_; + } + constexpr bool operator>(const UnitBase& other) const { + return value_ > other.value_; + } + constexpr bool operator<(const UnitBase& other) const { + return value_ < other.value_; + } + constexpr Unit_T RoundTo(const Unit_T& resolution) const { + RTC_DCHECK(IsFinite()); + RTC_DCHECK(resolution.IsFinite()); + RTC_DCHECK_GT(resolution.value_, 0); + return Unit_T((value_ + resolution.value_ / 2) / resolution.value_) * + resolution.value_; + } + constexpr Unit_T RoundUpTo(const Unit_T& resolution) const { + RTC_DCHECK(IsFinite()); + RTC_DCHECK(resolution.IsFinite()); + RTC_DCHECK_GT(resolution.value_, 0); + return Unit_T((value_ + resolution.value_ - 1) / resolution.value_) * + resolution.value_; + } + constexpr Unit_T RoundDownTo(const Unit_T& resolution) const { + RTC_DCHECK(IsFinite()); + RTC_DCHECK(resolution.IsFinite()); + RTC_DCHECK_GT(resolution.value_, 0); + return Unit_T(value_ / resolution.value_) * resolution.value_; + } + + protected: + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static constexpr Unit_T FromValue(T value) { + if (Unit_T::one_sided) + RTC_DCHECK_GE(value, 0); + RTC_DCHECK_GT(value, MinusInfinityVal()); + RTC_DCHECK_LT(value, PlusInfinityVal()); + return Unit_T(dchecked_cast(value)); + } + template ::value>::type* = + nullptr> + static constexpr Unit_T FromValue(T value) { + if (value == std::numeric_limits::infinity()) { + return PlusInfinity(); + } else if (value == -std::numeric_limits::infinity()) { + return MinusInfinity(); + } else { + return FromValue(dchecked_cast(value)); + } + } + + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static constexpr Unit_T FromFraction(int64_t denominator, T value) { + if (Unit_T::one_sided) + RTC_DCHECK_GE(value, 0); + RTC_DCHECK_GT(value, MinusInfinityVal() / denominator); + RTC_DCHECK_LT(value, PlusInfinityVal() / denominator); + return Unit_T(dchecked_cast(value * denominator)); + } + template ::value>::type* = + nullptr> + static constexpr Unit_T FromFraction(int64_t denominator, T value) { + return FromValue(value * denominator); + } + + template + constexpr typename std::enable_if::value, T>::type + ToValue() const { + RTC_DCHECK(IsFinite()); + return dchecked_cast(value_); + } + template + constexpr typename std::enable_if::value, T>::type + ToValue() const { + return IsPlusInfinity() ? std::numeric_limits::infinity() + : IsMinusInfinity() ? -std::numeric_limits::infinity() + : value_; + } + template + constexpr T ToValueOr(T fallback_value) const { + return IsFinite() ? value_ : fallback_value; + } + + template + constexpr typename std::enable_if::value, T>::type + ToFraction() const { + RTC_DCHECK(IsFinite()); + return dchecked_cast(DivideRoundToNearest(value_, Denominator)); + } + template + constexpr typename std::enable_if::value, T>::type + ToFraction() const { + return ToValue() * (1 / static_cast(Denominator)); + } + + template + constexpr int64_t ToFractionOr(int64_t fallback_value) const { + return IsFinite() ? DivideRoundToNearest(value_, Denominator) + : fallback_value; + } + + template + constexpr typename std::enable_if::value, T>::type + ToMultiple() const { + RTC_DCHECK_GE(ToValue(), std::numeric_limits::min() / Factor); + RTC_DCHECK_LE(ToValue(), std::numeric_limits::max() / Factor); + return dchecked_cast(ToValue() * Factor); + } + template + constexpr typename std::enable_if::value, T>::type + ToMultiple() const { + return ToValue() * Factor; + } + + explicit constexpr UnitBase(int64_t value) : value_(value) {} + + private: + template + friend class RelativeUnit; + + static inline constexpr int64_t PlusInfinityVal() { + return std::numeric_limits::max(); + } + static inline constexpr int64_t MinusInfinityVal() { + return std::numeric_limits::min(); + } + + constexpr Unit_T& AsSubClassRef() { return static_cast(*this); } + constexpr const Unit_T& AsSubClassRef() const { + return static_cast(*this); + } + + int64_t value_; +}; + +// Extends UnitBase to provide operations for relative units, that is, units +// that have a meaningful relation between values such that a += b is a +// sensible thing to do. For a,b <- same unit. +template +class RelativeUnit : public UnitBase { + public: + constexpr Unit_T Clamped(Unit_T min_value, Unit_T max_value) const { + return std::max(min_value, + std::min(UnitBase::AsSubClassRef(), max_value)); + } + constexpr void Clamp(Unit_T min_value, Unit_T max_value) { + *this = Clamped(min_value, max_value); + } + constexpr Unit_T operator+(const Unit_T other) const { + if (this->IsPlusInfinity() || other.IsPlusInfinity()) { + RTC_DCHECK(!this->IsMinusInfinity()); + RTC_DCHECK(!other.IsMinusInfinity()); + return this->PlusInfinity(); + } else if (this->IsMinusInfinity() || other.IsMinusInfinity()) { + RTC_DCHECK(!this->IsPlusInfinity()); + RTC_DCHECK(!other.IsPlusInfinity()); + return this->MinusInfinity(); + } + return UnitBase::FromValue(this->ToValue() + other.ToValue()); + } + constexpr Unit_T operator-(const Unit_T other) const { + if (this->IsPlusInfinity() || other.IsMinusInfinity()) { + RTC_DCHECK(!this->IsMinusInfinity()); + RTC_DCHECK(!other.IsPlusInfinity()); + return this->PlusInfinity(); + } else if (this->IsMinusInfinity() || other.IsPlusInfinity()) { + RTC_DCHECK(!this->IsPlusInfinity()); + RTC_DCHECK(!other.IsMinusInfinity()); + return this->MinusInfinity(); + } + return UnitBase::FromValue(this->ToValue() - other.ToValue()); + } + constexpr Unit_T& operator+=(const Unit_T other) { + *this = *this + other; + return this->AsSubClassRef(); + } + constexpr Unit_T& operator-=(const Unit_T other) { + *this = *this - other; + return this->AsSubClassRef(); + } + constexpr double operator/(const Unit_T other) const { + return UnitBase::template ToValue() / + other.template ToValue(); + } + template >* = nullptr> + constexpr Unit_T operator/(T scalar) const { + return UnitBase::FromValue(std::llround(this->ToValue() / scalar)); + } + template >* = nullptr> + constexpr Unit_T operator/(T scalar) const { + return UnitBase::FromValue(this->ToValue() / scalar); + } + constexpr Unit_T operator*(double scalar) const { + return UnitBase::FromValue(std::llround(this->ToValue() * scalar)); + } + constexpr Unit_T operator*(int64_t scalar) const { + return UnitBase::FromValue(this->ToValue() * scalar); + } + constexpr Unit_T operator*(int32_t scalar) const { + return UnitBase::FromValue(this->ToValue() * scalar); + } + constexpr Unit_T operator*(size_t scalar) const { + return UnitBase::FromValue(this->ToValue() * scalar); + } + + protected: + using UnitBase::UnitBase; + constexpr RelativeUnit() : UnitBase(0) {} +}; + +template +inline constexpr Unit_T operator*(double scalar, RelativeUnit other) { + return other * scalar; +} +template +inline constexpr Unit_T operator*(int64_t scalar, RelativeUnit other) { + return other * scalar; +} +template +inline constexpr Unit_T operator*(int32_t scalar, RelativeUnit other) { + return other * scalar; +} +template +inline constexpr Unit_T operator*(size_t scalar, RelativeUnit other) { + return other * scalar; +} + +template +inline constexpr Unit_T operator-(RelativeUnit other) { + if (other.IsPlusInfinity()) + return UnitBase::MinusInfinity(); + if (other.IsMinusInfinity()) + return UnitBase::PlusInfinity(); + return -1 * other; +} + +} // namespace rtc_units_impl + +} // namespace webrtc + +#endif // RTC_BASE_UNITS_UNIT_BASE_H_ diff --git a/pkg/apm/webrtc/rtc_base/untyped_function.h b/pkg/apm/webrtc/rtc_base/untyped_function.h new file mode 100644 index 00000000..b7e1de15 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/untyped_function.h @@ -0,0 +1,325 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_UNTYPED_FUNCTION_H_ +#define RTC_BASE_UNTYPED_FUNCTION_H_ + +#include +#include +#include +#include +#include + +#include "rtc_base/system/assume.h" + +namespace webrtc { +namespace webrtc_function_impl { + +using FunVoid = void(); + +// Inline storage size is this many machine words. +enum : size_t { kInlineStorageWords = 4 }; + +union VoidUnion { + void* void_ptr; + FunVoid* fun_ptr; + // std::max_align_t satisfies alignment requirements for every type. + alignas(std::max_align_t) char inline_storage[kInlineStorageWords * + sizeof(uintptr_t)]; +}; + +// Returns the number of elements of the `inline_storage` array required to +// store an object of type T. +template +constexpr size_t InlineStorageSize() { + // sizeof(T) / sizeof(uintptr_t), but rounded up. + return (sizeof(T) + sizeof(uintptr_t) - 1) / sizeof(uintptr_t); +} + +template +struct CallHelpers; +template +struct CallHelpers { + // Return type of the three helpers below. + using return_type = RetT; + // Complete function type of the three helpers below. + using function_type = RetT(VoidUnion*, ArgT...); + // Helper for calling the `void_ptr` case of VoidUnion. + template + static RetT CallVoidPtr(VoidUnion* vu, ArgT... args) { + return (*static_cast(vu->void_ptr))(std::forward(args)...); + } + // Helper for calling the `fun_ptr` case of VoidUnion. + static RetT CallFunPtr(VoidUnion* vu, ArgT... args) { + return (reinterpret_cast(vu->fun_ptr))( + std::forward(args)...); + } + // Helper for calling the `inline_storage` case of VoidUnion. + template + static RetT CallInlineStorage(VoidUnion* vu, ArgT... args) { + return (*reinterpret_cast(&vu->inline_storage))( + std::forward(args)...); + } +}; + +} // namespace webrtc_function_impl + +// A class that holds (and owns) any callable. The same function call signature +// must be provided when constructing and calling the object. +// +// The point of not having the call signature as a class template parameter is +// to have one single concrete type for all signatures; this reduces binary +// size. +class UntypedFunction final { + public: + // Callables of at most this size can be stored inline, if they are trivial. + // (Useful in tests and benchmarks; avoid using this in production code.) + enum : size_t { + kInlineStorageSize = sizeof(webrtc_function_impl::VoidUnion::inline_storage) + }; + static_assert(kInlineStorageSize == + webrtc_function_impl::kInlineStorageWords * + sizeof(uintptr_t), + ""); + + // The *UntypedFunctionArgs structs are used to transfer arguments from + // PrepareArgs() to Create(). They are trivial, but may own heap allocations, + // so make sure to pass them to Create() exactly once! + // + // The point of doing Create(PrepareArgs(foo)) instead of just Create(foo) is + // to separate the code that has to be inlined (PrepareArgs) from the code + // that can be noninlined (Create); the *UntypedFunctionArgs types are + // designed to efficiently carry the required information from one to the + // other. + template + struct TrivialUntypedFunctionArgs { + static_assert(N >= 1, ""); + static_assert(N <= webrtc_function_impl::kInlineStorageWords, ""); + // We use an uintptr_t array here instead of std::aligned_storage, because + // the former can be efficiently passed in registers when using + // TrivialUntypedFunctionArgs as a function argument. (We can't do the same + // in VoidUnion, because std::aligned_storage but not uintptr_t can be + // legally reinterpret_casted to arbitrary types. + // TrivialUntypedFunctionArgs, on the other hand, only needs to handle + // placement new and memcpy.) + alignas(std::max_align_t) uintptr_t inline_storage[N]; + webrtc_function_impl::FunVoid* call; + }; + struct NontrivialUntypedFunctionArgs { + void* void_ptr; + webrtc_function_impl::FunVoid* call; + void (*del)(webrtc_function_impl::VoidUnion*); + }; + struct FunctionPointerUntypedFunctionArgs { + webrtc_function_impl::FunVoid* fun_ptr; + webrtc_function_impl::FunVoid* call; + }; + + // Create function for lambdas and other callables that are trivial and small; + // it accepts every type of argument except those noted in its enable_if call. + template < + typename Signature, + typename F, + typename F_deref = typename std::remove_reference::type, + typename std::enable_if< + // Not for function pointers; we have another overload for that below. + !std::is_function< + typename std::remove_pointer::type>::value && + + // Not for nullptr; we have a constructor for that below. + !std::is_same::type>::value && + + // Not for UntypedFunction objects; use move construction or + // assignment. + !std::is_same::type>::value && + + // Only for trivial callables that will fit in inline storage. + std::is_trivially_move_constructible::value && + std::is_trivially_destructible::value && + sizeof(F_deref) <= kInlineStorageSize>::type* = nullptr, + size_t InlineSize = webrtc_function_impl::InlineStorageSize()> + static TrivialUntypedFunctionArgs PrepareArgs(F&& f) { + // The callable is trivial and small enough, so we just store its bytes + // in the inline storage. + TrivialUntypedFunctionArgs args; + new (&args.inline_storage) F_deref(std::forward(f)); + args.call = reinterpret_cast( + webrtc_function_impl::CallHelpers< + Signature>::template CallInlineStorage); + return args; + } + template + static UntypedFunction Create(TrivialUntypedFunctionArgs args) { + webrtc_function_impl::VoidUnion vu; + std::memcpy(&vu.inline_storage, args.inline_storage, + sizeof(args.inline_storage)); + return UntypedFunction(vu, args.call, nullptr); + } + + // Create function for lambdas and other callables that are nontrivial or + // large; it accepts every type of argument except those noted in its + // enable_if call. + template ::type, + typename std::enable_if< + // Not for function pointers; we have another overload for that + // below. + !std::is_function< + typename std::remove_pointer::type>::value && + + // Not for nullptr; we have a constructor for that below. + !std::is_same::type>::value && + + // Not for UntypedFunction objects; use move construction or + // assignment. + !std::is_same::type>::value && + + // Only for nontrivial callables, or callables that won't fit in + // inline storage. + !(std::is_trivially_move_constructible::value && + std::is_trivially_destructible::value && + sizeof(F_deref) <= kInlineStorageSize)>::type* = nullptr> + static NontrivialUntypedFunctionArgs PrepareArgs(F&& f) { + // The callable is either nontrivial or too large, so we can't keep it + // in the inline storage; use the heap instead. + NontrivialUntypedFunctionArgs args; + args.void_ptr = new F_deref(std::forward(f)); + args.call = reinterpret_cast( + webrtc_function_impl::CallHelpers::template CallVoidPtr< + F_deref>); + args.del = static_cast( + [](webrtc_function_impl::VoidUnion* vu) { + // Assuming that this pointer isn't null allows the + // compiler to eliminate a null check in the (inlined) + // delete operation. + RTC_ASSUME(vu->void_ptr != nullptr); + delete reinterpret_cast(vu->void_ptr); + }); + return args; + } + static UntypedFunction Create(NontrivialUntypedFunctionArgs args) { + webrtc_function_impl::VoidUnion vu; + vu.void_ptr = args.void_ptr; + return UntypedFunction(vu, args.call, args.del); + } + + // Create function that accepts function pointers. If the argument is null, + // the result is an empty UntypedFunction. + template + static FunctionPointerUntypedFunctionArgs PrepareArgs(Signature* f) { + FunctionPointerUntypedFunctionArgs args; + args.fun_ptr = reinterpret_cast(f); + args.call = reinterpret_cast( + webrtc_function_impl::CallHelpers::CallFunPtr); + return args; + } + static UntypedFunction Create(FunctionPointerUntypedFunctionArgs args) { + webrtc_function_impl::VoidUnion vu; + vu.fun_ptr = args.fun_ptr; + return UntypedFunction(vu, args.fun_ptr == nullptr ? nullptr : args.call, + nullptr); + } + + // Prepares arguments and creates an UntypedFunction in one go. + template + static UntypedFunction Create(F&& f) { + return Create(PrepareArgs(std::forward(f))); + } + + // Default constructor. Creates an empty UntypedFunction. + UntypedFunction() : call_(nullptr), delete_(nullptr) {} + + // Nullptr constructor and assignment. Creates an empty UntypedFunction. + UntypedFunction(std::nullptr_t) // NOLINT(runtime/explicit) + : call_(nullptr), delete_(nullptr) {} + UntypedFunction& operator=(std::nullptr_t) { + call_ = nullptr; + if (delete_) { + delete_(&f_); + delete_ = nullptr; + } + return *this; + } + + // Not copyable. + UntypedFunction(const UntypedFunction&) = delete; + UntypedFunction& operator=(const UntypedFunction&) = delete; + + // Move construction and assignment. + UntypedFunction(UntypedFunction&& other) + : f_(other.f_), call_(other.call_), delete_(other.delete_) { + other.delete_ = nullptr; + } + UntypedFunction& operator=(UntypedFunction&& other) { + if (delete_) { + delete_(&f_); + } + f_ = other.f_; + call_ = other.call_; + delete_ = other.delete_; + other.delete_ = nullptr; + return *this; + } + + ~UntypedFunction() { + if (delete_) { + delete_(&f_); + } + } + + friend void swap(UntypedFunction& a, UntypedFunction& b) { + using std::swap; + swap(a.f_, b.f_); + swap(a.call_, b.call_); + swap(a.delete_, b.delete_); + } + + // Returns true if we have a function, false if we don't (i.e., we're null). + explicit operator bool() const { return call_ != nullptr; } + + template + typename webrtc_function_impl::CallHelpers::return_type Call( + ArgT&&... args) { + return reinterpret_cast< + typename webrtc_function_impl::CallHelpers::function_type*>( + call_)(&f_, std::forward(args)...); + } + + // Returns true iff we don't need to call a destructor. This is guaranteed + // to hold for a moved-from object. + bool IsTriviallyDestructible() { return delete_ == nullptr; } + + private: + UntypedFunction(webrtc_function_impl::VoidUnion f, + webrtc_function_impl::FunVoid* call, + void (*del)(webrtc_function_impl::VoidUnion*)) + : f_(f), call_(call), delete_(del) {} + + // The callable thing, or a pointer to it. + webrtc_function_impl::VoidUnion f_; + + // Pointer to a dispatch function that knows the type of the callable thing + // that's stored in f_, and how to call it. An UntypedFunction object is empty + // (null) iff call_ is null. + webrtc_function_impl::FunVoid* call_; + + // Pointer to a function that knows how to delete the callable thing that's + // stored in f_. Null if `f_` is trivially deletable. + void (*delete_)(webrtc_function_impl::VoidUnion*); +}; + +} // namespace webrtc + +#endif // RTC_BASE_UNTYPED_FUNCTION_H_ diff --git a/pkg/apm/webrtc/rtc_base/virtual_socket_server.h b/pkg/apm/webrtc/rtc_base/virtual_socket_server.h new file mode 100644 index 00000000..32e1765e --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/virtual_socket_server.h @@ -0,0 +1,491 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_VIRTUAL_SOCKET_SERVER_H_ +#define RTC_BASE_VIRTUAL_SOCKET_SERVER_H_ + +#include +#include +#include +#include + +#include "api/make_ref_counted.h" +#include "api/ref_counted_base.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_base.h" +#include "rtc_base/checks.h" +#include "rtc_base/event.h" +#include "rtc_base/fake_clock.h" +#include "rtc_base/socket_address_pair.h" +#include "rtc_base/socket_server.h" +#include "rtc_base/synchronization/mutex.h" + +namespace webrtc { + +class VirtualSocketPacket; +class VirtualSocketServer; + +// Implements the socket interface using the virtual network. Packets are +// passed in tasks using the thread of the socket server. +class VirtualSocket : public Socket, public sigslot::has_slots<> { + public: + VirtualSocket(VirtualSocketServer* server, int family, int type); + ~VirtualSocket() override; + + SocketAddress GetLocalAddress() const override; + SocketAddress GetRemoteAddress() const override; + + int Bind(const SocketAddress& addr) override; + int Connect(const SocketAddress& addr) override; + int Close() override; + int Send(const void* pv, size_t cb) override; + int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override; + int Recv(void* pv, size_t cb, int64_t* timestamp) override; + int RecvFrom(void* pv, + size_t cb, + SocketAddress* paddr, + int64_t* timestamp) override; + int Listen(int backlog) override; + VirtualSocket* Accept(SocketAddress* paddr) override; + + int GetError() const override; + void SetError(int error) override; + ConnState GetState() const override; + int GetOption(Option opt, int* value) override; + int SetOption(Option opt, int value) override; + + size_t recv_buffer_size() const { return recv_buffer_size_; } + size_t send_buffer_size() const { return send_buffer_.size(); } + const char* send_buffer_data() const { return send_buffer_.data(); } + + // Used by server sockets to set the local address without binding. + void SetLocalAddress(const SocketAddress& addr); + + bool was_any() { return was_any_; } + void set_was_any(bool was_any) { was_any_ = was_any; } + + void SetToBlocked(); + + void UpdateRecv(size_t data_size); + void UpdateSend(size_t data_size); + + void MaybeSignalWriteEvent(size_t capacity); + + // Adds a packet to be sent. Returns delay, based on network_size_. + uint32_t AddPacket(int64_t cur_time, size_t packet_size); + + int64_t UpdateOrderedDelivery(int64_t ts); + + // Removes stale packets from the network. Returns current size. + size_t PurgeNetworkPackets(int64_t cur_time); + + void PostPacket(TimeDelta delay, std::unique_ptr packet); + void PostConnect(TimeDelta delay, const SocketAddress& remote_addr); + void PostDisconnect(TimeDelta delay); + + private: + // Struct shared with pending tasks that may outlive VirtualSocket. + class SafetyBlock : public RefCountedNonVirtual { + public: + explicit SafetyBlock(VirtualSocket* socket); + SafetyBlock(const SafetyBlock&) = delete; + SafetyBlock& operator=(const SafetyBlock&) = delete; + ~SafetyBlock(); + + // Prohibits posted delayed task to access owning VirtualSocket and + // cleanups members protected by the `mutex`. + void SetNotAlive(); + bool IsAlive(); + + // Copies up to `size` bytes into buffer from the next received packet + // and fills `addr` with remote address of that received packet. + // Returns number of bytes copied or negative value on failure. + int RecvFrom(void* buffer, size_t size, SocketAddress& addr); + + void Listen(); + + struct AcceptResult { + int error = 0; + std::unique_ptr socket; + SocketAddress remote_addr; + }; + AcceptResult Accept(); + + bool AddPacket(std::unique_ptr packet); + void PostConnect(TimeDelta delay, const SocketAddress& remote_addr); + + private: + enum class Signal { kNone, kReadEvent, kConnectEvent }; + // `PostConnect` rely on the fact that std::list iterators are not + // invalidated on any changes to other elements in the container. + using PostedConnects = std::list; + + void PostSignalReadEvent() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void MaybeSignalReadEvent(); + Signal Connect(PostedConnects::iterator remote_addr_it); + + Mutex mutex_; + VirtualSocket& socket_; + bool alive_ RTC_GUARDED_BY(mutex_) = true; + // Flag indicating if async Task to signal SignalReadEvent is posted. + // To avoid posting multiple such tasks. + bool pending_read_signal_event_ RTC_GUARDED_BY(mutex_) = false; + + // Members below do not need to outlive VirtualSocket, but are used by the + // posted tasks. Keeping them in the VirtualSocket confuses thread + // annotations because they can't detect that locked mutex is the same mutex + // this members are guarded by. + + // Addresses of the sockets for potential connect. For each address there + // is a posted task that should finilze the connect. + PostedConnects posted_connects_ RTC_GUARDED_BY(mutex_); + + // Data which has been received from the network + std::list> recv_buffer_ + RTC_GUARDED_BY(mutex_); + + // Pending sockets which can be Accepted + std::optional> listen_queue_ + RTC_GUARDED_BY(mutex_); + }; + + struct NetworkEntry { + size_t size; + int64_t done_time; + }; + + typedef std::deque NetworkQueue; + typedef std::vector SendBuffer; + typedef std::map OptionsMap; + + int InitiateConnect(const SocketAddress& addr, bool use_delay); + void CompleteConnect(const SocketAddress& addr); + int SendUdp(const void* pv, size_t cb, const SocketAddress& addr); + int SendTcp(const void* pv, size_t cb); + + void OnSocketServerReadyToSend(); + + VirtualSocketServer* const server_; + const int type_; + ConnState state_; + int error_; + SocketAddress local_addr_; + SocketAddress remote_addr_; + + const scoped_refptr safety_ = + make_ref_counted(this); + + // Data which tcp has buffered for sending + SendBuffer send_buffer_; + // Set to false if the last attempt to send resulted in EWOULDBLOCK. + // Set back to true when the socket can send again. + bool ready_to_send_ = true; + + // Network model that enforces bandwidth and capacity constraints + NetworkQueue network_; + size_t network_size_; + // The scheduled delivery time of the last packet sent on this socket. + // It is used to ensure ordered delivery of packets sent on this socket. + int64_t last_delivery_time_ = 0; + + // The amount of data which is in flight or in recv_buffer_ + size_t recv_buffer_size_; + + // Is this socket bound? + bool bound_; + + // When we bind a socket to Any, VSS's Bind gives it another address. For + // dual-stack sockets, we want to distinguish between sockets that were + // explicitly given a particular address and sockets that had one picked + // for them by VSS. + bool was_any_; + + // Store the options that are set + OptionsMap options_map_; +}; + +// Simulates a network in the same manner as a loopback interface. The +// interface can create as many addresses as you want. All of the sockets +// created by this network will be able to communicate with one another, unless +// they are bound to addresses from incompatible families. +class VirtualSocketServer : public SocketServer { + public: + VirtualSocketServer(); + // This constructor needs to be used if the test uses a fake clock and + // ProcessMessagesUntilIdle, since ProcessMessagesUntilIdle needs a way of + // advancing time. + explicit VirtualSocketServer(ThreadProcessingFakeClock* fake_clock); + ~VirtualSocketServer() override; + + VirtualSocketServer(const VirtualSocketServer&) = delete; + VirtualSocketServer& operator=(const VirtualSocketServer&) = delete; + + // The default source address specifies which local address to use when a + // socket is bound to the 'any' address, e.g. 0.0.0.0. (If not set, the 'any' + // address is used as the source address on outgoing virtual packets, exposed + // to recipient's RecvFrom). + IPAddress GetDefaultSourceAddress(int family); + void SetDefaultSourceAddress(const IPAddress& from_addr); + + // Limits the network bandwidth (maximum bytes per second). Zero means that + // all sends occur instantly. Defaults to 0. + void set_bandwidth(uint32_t bandwidth) RTC_LOCKS_EXCLUDED(mutex_); + + // Limits the amount of data which can be in flight on the network without + // packet loss (on a per sender basis). Defaults to 64 KB. + void set_network_capacity(uint32_t capacity) RTC_LOCKS_EXCLUDED(mutex_); + + // The amount of data which can be buffered by tcp on the sender's side + uint32_t send_buffer_capacity() const RTC_LOCKS_EXCLUDED(mutex_); + void set_send_buffer_capacity(uint32_t capacity) RTC_LOCKS_EXCLUDED(mutex_); + + // The amount of data which can be buffered by tcp on the receiver's side + uint32_t recv_buffer_capacity() const RTC_LOCKS_EXCLUDED(mutex_); + void set_recv_buffer_capacity(uint32_t capacity) RTC_LOCKS_EXCLUDED(mutex_); + + // Controls the (transit) delay for packets sent in the network. This does + // not inclue the time required to sit in the send queue. Both of these + // values are measured in milliseconds. Defaults to no delay. + void set_delay_mean(uint32_t delay_mean) RTC_LOCKS_EXCLUDED(mutex_); + void set_delay_stddev(uint32_t delay_stddev) RTC_LOCKS_EXCLUDED(mutex_); + void set_delay_samples(uint32_t delay_samples) RTC_LOCKS_EXCLUDED(mutex_); + + // If the (transit) delay parameters are modified, this method should be + // called to recompute the new distribution. + void UpdateDelayDistribution() RTC_LOCKS_EXCLUDED(mutex_); + + // Controls the (uniform) probability that any sent packet is dropped. This + // is separate from calculations to drop based on queue size. + void set_drop_probability(double drop_prob) RTC_LOCKS_EXCLUDED(mutex_); + + // Controls the maximum UDP payload for the networks simulated + // by this server. Any UDP payload sent that is larger than this will + // be dropped. + void set_max_udp_payload(size_t payload_size) RTC_LOCKS_EXCLUDED(mutex_); + + // If `blocked` is true, subsequent attempts to send will result in -1 being + // returned, with the socket error set to EWOULDBLOCK. + // + // If this method is later called with `blocked` set to false, any sockets + // that previously failed to send with EWOULDBLOCK will emit SignalWriteEvent. + // + // This can be used to simulate the send buffer on a network interface being + // full, and test functionality related to EWOULDBLOCK/SignalWriteEvent. + void SetSendingBlocked(bool blocked) RTC_LOCKS_EXCLUDED(mutex_); + + // SocketFactory: + VirtualSocket* CreateSocket(int family, int type) override; + + // SocketServer: + void SetMessageQueue(Thread* queue) override; + bool Wait(TimeDelta max_wait_duration, bool process_io) override; + void WakeUp() override; + + void SetDelayOnAddress(const SocketAddress& address, int delay_ms) { + delay_by_ip_[address.ipaddr()] = delay_ms; + } + + // Used by TurnPortTest and TcpPortTest (for example), to mimic a case where + // a proxy returns the local host address instead of the original one the + // port was bound against. Please see WebRTC issue 3927 for more detail. + // + // If SetAlternativeLocalAddress(A, B) is called, then when something + // attempts to bind a socket to address A, it will get a socket bound to + // address B instead. + void SetAlternativeLocalAddress(const IPAddress& address, + const IPAddress& alternative); + + typedef std::pair Point; + typedef std::vector Function; + + static std::unique_ptr CreateDistribution(uint32_t mean, + uint32_t stddev, + uint32_t samples); + + // Similar to Thread::ProcessMessages, but it only processes messages until + // there are no immediate messages or pending network traffic. Returns false + // if Thread::Stop() was called. + bool ProcessMessagesUntilIdle(); + + // Sets the next port number to use for testing. + void SetNextPortForTesting(uint16_t port); + + // Close a pair of Tcp connections by addresses. Both connections will have + // its own OnClose invoked. + bool CloseTcpConnections(const SocketAddress& addr_local, + const SocketAddress& addr_remote); + + // Number of packets that clients have attempted to send through this virtual + // socket server. Intended to be used for test assertions. + uint32_t sent_packets() const RTC_LOCKS_EXCLUDED(mutex_); + + // Assign IP and Port if application's address is unspecified. Also apply + // `alternative_address_mapping_`. + SocketAddress AssignBindAddress(const SocketAddress& app_addr); + + // Binds the given socket to the given (fully-defined) address. + int Bind(VirtualSocket* socket, const SocketAddress& addr); + + int Unbind(const SocketAddress& addr, VirtualSocket* socket); + + // Adds a mapping between this socket pair and the socket. + void AddConnection(const SocketAddress& client, + const SocketAddress& server, + VirtualSocket* socket); + + // Connects the given socket to the socket at the given address + int Connect(VirtualSocket* socket, + const SocketAddress& remote_addr, + bool use_delay); + + // Sends a disconnect message to the socket at the given address + bool Disconnect(VirtualSocket* socket); + + // Lookup address, and disconnect corresponding socket. + bool Disconnect(const SocketAddress& addr); + + // Lookup connection, close corresponding socket. + bool Disconnect(const SocketAddress& local_addr, + const SocketAddress& remote_addr); + + // Sends the given packet to the socket at the given address (if one exists). + int SendUdp(VirtualSocket* socket, + const char* data, + size_t data_size, + const SocketAddress& remote_addr); + + // Moves as much data as possible from the sender's buffer to the network + void SendTcp(VirtualSocket* socket) RTC_LOCKS_EXCLUDED(mutex_); + + // Like above, but lookup sender by address. + void SendTcp(const SocketAddress& addr) RTC_LOCKS_EXCLUDED(mutex_); + + // Computes the number of milliseconds required to send a packet of this size. + uint32_t SendDelay(uint32_t size) RTC_LOCKS_EXCLUDED(mutex_); + + // Sending was previously blocked, but now isn't. + sigslot::signal0<> SignalReadyToSend; + + protected: + // Returns a new IP not used before in this network. + IPAddress GetNextIP(int family); + + // Find the socket bound to the given address + VirtualSocket* LookupBinding(const SocketAddress& addr); + + private: + friend VirtualSocket; + uint16_t GetNextPort(); + + // Find the socket pair corresponding to this server address. + VirtualSocket* LookupConnection(const SocketAddress& client, + const SocketAddress& server); + + void RemoveConnection(const SocketAddress& client, + const SocketAddress& server); + + // Places a packet on the network. + void AddPacketToNetwork(VirtualSocket* socket, + VirtualSocket* recipient, + int64_t cur_time, + const char* data, + size_t data_size, + size_t header_size, + bool ordered); + + // If the delay has been set for the address of the socket, returns the set + // delay. Otherwise, returns a random transit delay chosen from the + // appropriate distribution. + uint32_t GetTransitDelay(Socket* socket); + + // Basic operations on functions. + static std::unique_ptr Accumulate(std::unique_ptr f); + static std::unique_ptr Invert(std::unique_ptr f); + static std::unique_ptr Resample(std::unique_ptr f, + double x1, + double x2, + uint32_t samples); + static double Evaluate(const Function* f, double x); + + // Determine if two sockets should be able to communicate. + // We don't (currently) specify an address family for sockets; instead, + // the currently bound address is used to infer the address family. + // Any socket that is not explicitly bound to an IPv4 address is assumed to be + // dual-stack capable. + // This function tests if two addresses can communicate, as well as the + // sockets to which they may be bound (the addresses may or may not yet be + // bound to the sockets). + // First the addresses are tested (after normalization): + // If both have the same family, then communication is OK. + // If only one is IPv4 then false, unless the other is bound to ::. + // This applies even if the IPv4 address is 0.0.0.0. + // The socket arguments are optional; the sockets are checked to see if they + // were explicitly bound to IPv6-any ('::'), and if so communication is + // permitted. + // NB: This scheme doesn't permit non-dualstack IPv6 sockets. + static bool CanInteractWith(VirtualSocket* local, VirtualSocket* remote); + + typedef std::map AddressMap; + typedef std::map ConnectionMap; + + // May be null if the test doesn't use a fake clock, or it does but doesn't + // use ProcessMessagesUntilIdle. + ThreadProcessingFakeClock* fake_clock_ = nullptr; + + // Used to implement Wait/WakeUp. + Event wakeup_; + Thread* msg_queue_; + bool stop_on_idle_; + in_addr next_ipv4_; + in6_addr next_ipv6_; + uint16_t next_port_; + AddressMap* bindings_; + ConnectionMap* connections_; + + IPAddress default_source_address_v4_; + IPAddress default_source_address_v6_; + + mutable Mutex mutex_; + + uint32_t bandwidth_ RTC_GUARDED_BY(mutex_); + uint32_t network_capacity_ RTC_GUARDED_BY(mutex_); + uint32_t send_buffer_capacity_ RTC_GUARDED_BY(mutex_); + uint32_t recv_buffer_capacity_ RTC_GUARDED_BY(mutex_); + uint32_t delay_mean_ RTC_GUARDED_BY(mutex_); + uint32_t delay_stddev_ RTC_GUARDED_BY(mutex_); + uint32_t delay_samples_ RTC_GUARDED_BY(mutex_); + + // Used for testing. + uint32_t sent_packets_ RTC_GUARDED_BY(mutex_) = 0; + + std::map delay_by_ip_; + std::map alternative_address_mapping_; + std::unique_ptr delay_dist_; + + double drop_prob_ RTC_GUARDED_BY(mutex_); + // The largest UDP payload permitted on this virtual socket server. + // The default is the max size of IPv4 fragmented UDP packet payload: + // 65535 bytes - 8 bytes UDP header - 20 bytes IP header. + size_t max_udp_payload_ RTC_GUARDED_BY(mutex_) = 65507; + + bool sending_blocked_ RTC_GUARDED_BY(mutex_) = false; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::VirtualSocketServer; +} +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_VIRTUAL_SOCKET_SERVER_H_ diff --git a/pkg/apm/webrtc/rtc_base/weak_ptr.h b/pkg/apm/webrtc/rtc_base/weak_ptr.h new file mode 100644 index 00000000..00b77fd6 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/weak_ptr.h @@ -0,0 +1,291 @@ +/* + * Copyright 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. + */ + +#ifndef RTC_BASE_WEAK_PTR_H_ +#define RTC_BASE_WEAK_PTR_H_ + +#include +#include + +#include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "rtc_base/checks.h" +#include "rtc_base/ref_count.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread_annotations.h" +#include "rtc_base/weak_ptr.h" + +// The implementation is borrowed from chromium except that it does not +// implement SupportsWeakPtr. + +// Weak pointers are pointers to an object that do not affect its lifetime, +// and which may be invalidated (i.e. reset to nullptr) by the object, or its +// owner, at any time, most commonly when the object is about to be deleted. + +// Weak pointers are useful when an object needs to be accessed safely by one +// or more objects other than its owner, and those callers can cope with the +// object vanishing and e.g. tasks posted to it being silently dropped. +// Reference-counting such an object would complicate the ownership graph and +// make it harder to reason about the object's lifetime. + +// EXAMPLE: +// +// class Controller { +// public: +// Controller() : weak_factory_(this) {} +// void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); } +// void WorkComplete(const Result& result) { ... } +// private: +// // Member variables should appear before the WeakPtrFactory, to ensure +// // that any WeakPtrs to Controller are invalidated before its members +// // variable's destructors are executed, rendering them invalid. +// WeakPtrFactory weak_factory_; +// }; +// +// class Worker { +// public: +// static void StartNew(const WeakPtr& controller) { +// Worker* worker = new Worker(controller); +// // Kick off asynchronous processing... +// } +// private: +// Worker(const WeakPtr& controller) +// : controller_(controller) {} +// void DidCompleteAsynchronousProcessing(const Result& result) { +// if (controller_) +// controller_->WorkComplete(result); +// } +// WeakPtr controller_; +// }; +// +// With this implementation a caller may use SpawnWorker() to dispatch multiple +// Workers and subsequently delete the Controller, without waiting for all +// Workers to have completed. + +// ------------------------- IMPORTANT: Thread-safety ------------------------- + +// Weak pointers may be passed safely between threads, but must always be +// dereferenced and invalidated on the same TaskQueue or thread, otherwise +// checking the pointer would be racey. +// +// To ensure correct use, the first time a WeakPtr issued by a WeakPtrFactory +// is dereferenced, the factory and its WeakPtrs become bound to the calling +// TaskQueue/thread, and cannot be dereferenced or +// invalidated on any other TaskQueue/thread. Bound WeakPtrs can still be handed +// off to other TaskQueues, e.g. to use to post tasks back to object on the +// bound sequence. +// +// Thus, at least one WeakPtr object must exist and have been dereferenced on +// the correct thread to enforce that other WeakPtr objects will enforce they +// are used on the desired thread. + +namespace webrtc { + +namespace internal { + +class WeakReference { + public: + // Although Flag is bound to a specific sequence, it may be + // deleted from another via base::WeakPtr::~WeakPtr(). + class Flag { + public: + Flag() = default; + + void Invalidate(); + bool IsValid() const; + + private: + friend class FinalRefCountedObject; + + ~Flag() = default; + + RTC_NO_UNIQUE_ADDRESS SequenceChecker checker_{ + webrtc::SequenceChecker::kDetached}; + bool is_valid_ RTC_GUARDED_BY(checker_) = true; + }; + + // `RefCountedFlag` is the reference counted (shared), non-virtual, flag type. + using RefCountedFlag = FinalRefCountedObject; + + WeakReference(); + explicit WeakReference(const RefCountedFlag* flag); + ~WeakReference(); + + WeakReference(WeakReference&& other); + WeakReference(const WeakReference& other); + WeakReference& operator=(WeakReference&& other) = default; + WeakReference& operator=(const WeakReference& other) = default; + + bool is_valid() const; + + private: + scoped_refptr flag_; +}; + +class WeakReferenceOwner { + public: + WeakReferenceOwner(); + ~WeakReferenceOwner(); + + WeakReference GetRef() const; + + bool HasRefs() const { return flag_.get() && !flag_->HasOneRef(); } + + void Invalidate(); + + private: + mutable scoped_refptr flag_; +}; + +// This class simplifies the implementation of WeakPtr's type conversion +// constructor by avoiding the need for a public accessor for ref_. A +// WeakPtr cannot access the private members of WeakPtr, so this +// base class gives us a way to access ref_ in a protected fashion. +class WeakPtrBase { + public: + WeakPtrBase(); + ~WeakPtrBase(); + + WeakPtrBase(const WeakPtrBase& other) = default; + WeakPtrBase(WeakPtrBase&& other) = default; + WeakPtrBase& operator=(const WeakPtrBase& other) = default; + WeakPtrBase& operator=(WeakPtrBase&& other) = default; + + protected: + explicit WeakPtrBase(const WeakReference& ref); + + WeakReference ref_; +}; + +} // namespace internal + +template +class WeakPtrFactory; + +template +class WeakPtr : public internal::WeakPtrBase { + public: + WeakPtr() : ptr_(nullptr) {} + + // Allow conversion from U to T provided U "is a" T. Note that this + // is separate from the (implicit) copy and move constructors. + template + WeakPtr(const WeakPtr& other) + : internal::WeakPtrBase(other), ptr_(other.ptr_) {} + template + WeakPtr(WeakPtr&& other) + : internal::WeakPtrBase(std::move(other)), ptr_(other.ptr_) {} + + T* get() const { return ref_.is_valid() ? ptr_ : nullptr; } + + T& operator*() const { + RTC_DCHECK(get() != nullptr); + return *get(); + } + T* operator->() const { + RTC_DCHECK(get() != nullptr); + return get(); + } + + void reset() { + ref_ = internal::WeakReference(); + ptr_ = nullptr; + } + + // Allow conditionals to test validity, e.g. if (weak_ptr) {...}; + explicit operator bool() const { return get() != nullptr; } + + private: + template + friend class WeakPtr; + friend class WeakPtrFactory; + + WeakPtr(const internal::WeakReference& ref, T* ptr) + : internal::WeakPtrBase(ref), ptr_(ptr) {} + + // This pointer is only valid when ref_.is_valid() is true. Otherwise, its + // value is undefined (as opposed to nullptr). + T* ptr_; +}; + +// Allow callers to compare WeakPtrs against nullptr to test validity. +template +bool operator!=(const WeakPtr& weak_ptr, std::nullptr_t) { + return !(weak_ptr == nullptr); +} +template +bool operator!=(std::nullptr_t, const WeakPtr& weak_ptr) { + return weak_ptr != nullptr; +} +template +bool operator==(const WeakPtr& weak_ptr, std::nullptr_t) { + return weak_ptr.get() == nullptr; +} +template +bool operator==(std::nullptr_t, const WeakPtr& weak_ptr) { + return weak_ptr == nullptr; +} + +// A class may be composed of a WeakPtrFactory and thereby +// control how it exposes weak pointers to itself. This is helpful if you only +// need weak pointers within the implementation of a class. This class is also +// useful when working with primitive types. For example, you could have a +// WeakPtrFactory that is used to pass around a weak reference to a bool. + +// Note that GetWeakPtr must be called on one and only one TaskQueue or thread +// and the WeakPtr must only be dereferenced and invalidated on that same +// TaskQueue/thread. A WeakPtr instance can be copied and posted to other +// sequences though as long as it is not dereferenced (WeakPtr::get()). +template +class WeakPtrFactory { + public: + explicit WeakPtrFactory(T* ptr) : ptr_(ptr) {} + + WeakPtrFactory() = delete; + WeakPtrFactory(const WeakPtrFactory&) = delete; + WeakPtrFactory& operator=(const WeakPtrFactory&) = delete; + + ~WeakPtrFactory() { ptr_ = nullptr; } + + WeakPtr GetWeakPtr() { + RTC_DCHECK(ptr_); + return WeakPtr(weak_reference_owner_.GetRef(), ptr_); + } + + // Call this method to invalidate all existing weak pointers. + void InvalidateWeakPtrs() { + RTC_DCHECK(ptr_); + weak_reference_owner_.Invalidate(); + } + + // Call this method to determine if any weak pointers exist. + bool HasWeakPtrs() const { + RTC_DCHECK(ptr_); + return weak_reference_owner_.HasRefs(); + } + + private: + internal::WeakReferenceOwner weak_reference_owner_; + T* ptr_; +}; + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::WeakPtr; +using ::webrtc::WeakPtrFactory; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_WEAK_PTR_H_ diff --git a/pkg/apm/webrtc/rtc_base/win/create_direct3d_device.h b/pkg/apm/webrtc/rtc_base/win/create_direct3d_device.h new file mode 100644 index 00000000..7c21f872 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/win/create_direct3d_device.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_WIN_CREATE_DIRECT3D_DEVICE_H_ +#define RTC_BASE_WIN_CREATE_DIRECT3D_DEVICE_H_ + +#include +#include +#include +#include + +namespace webrtc { + +// Callers must check the return value of ResolveCoreWinRTDirect3DDelayload() +// before using CreateDirect3DDeviceFromDXGIDevice(). +bool ResolveCoreWinRTDirect3DDelayload(); + +// Allows for the creating of Direct3D Devices from a DXGI device on versions +// of Windows greater than Win7. +HRESULT CreateDirect3DDeviceFromDXGIDevice( + IDXGIDevice* dxgi_device, + ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice** + out_d3d11_device); + +} // namespace webrtc + +#endif // RTC_BASE_WIN_CREATE_DIRECT3D_DEVICE_H_ diff --git a/pkg/apm/webrtc/rtc_base/win/get_activation_factory.h b/pkg/apm/webrtc/rtc_base/win/get_activation_factory.h new file mode 100644 index 00000000..08f602f0 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/win/get_activation_factory.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_WIN_GET_ACTIVATION_FACTORY_H_ +#define RTC_BASE_WIN_GET_ACTIVATION_FACTORY_H_ + +#include + +#include "rtc_base/win/hstring.h" + +namespace webrtc { + +// Provides access to Core WinRT functions which may not be available on +// Windows 7. Loads functions dynamically at runtime to prevent library +// dependencies. + +// Callers must check the return value of ResolveCoreWinRTDelayLoad() before +// using these functions. + +bool ResolveCoreWinRTDelayload(); + +HRESULT RoGetActivationFactoryProxy(HSTRING class_id, + const IID& iid, + void** out_factory); + +// Retrieves an activation factory for the type specified. +template +HRESULT GetActivationFactory(InterfaceType** factory) { + HSTRING class_id_hstring; + HRESULT hr = CreateHstring(runtime_class_id, wcslen(runtime_class_id), + &class_id_hstring); + if (FAILED(hr)) + return hr; + + hr = RoGetActivationFactoryProxy(class_id_hstring, IID_PPV_ARGS(factory)); + if (FAILED(hr)) { + DeleteHstring(class_id_hstring); + return hr; + } + + return DeleteHstring(class_id_hstring); +} + +} // namespace webrtc + +#endif // RTC_BASE_WIN_GET_ACTIVATION_FACTORY_H_ diff --git a/pkg/apm/webrtc/rtc_base/win/hstring.h b/pkg/apm/webrtc/rtc_base/win/hstring.h new file mode 100644 index 00000000..8fb119a9 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/win/hstring.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_WIN_HSTRING_H_ +#define RTC_BASE_WIN_HSTRING_H_ + +#include +#include +#include + +namespace webrtc { + +// Callers must check the return value of ResolveCoreWinRTStringDelayLoad() +// before using these functions. +bool ResolveCoreWinRTStringDelayload(); + +HRESULT CreateHstring(const wchar_t* src, uint32_t len, HSTRING* out_hstr); + +HRESULT DeleteHstring(HSTRING hstr); + +} // namespace webrtc + +#endif // RTC_BASE_WIN_HSTRING_H_ diff --git a/pkg/apm/webrtc/rtc_base/win/scoped_com_initializer.h b/pkg/apm/webrtc/rtc_base/win/scoped_com_initializer.h new file mode 100644 index 00000000..2427097b --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/win/scoped_com_initializer.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_WIN_SCOPED_COM_INITIALIZER_H_ +#define RTC_BASE_WIN_SCOPED_COM_INITIALIZER_H_ + +#include + +namespace webrtc { + +// Initializes COM in the constructor (STA or MTA), and uninitializes COM in the +// destructor. Taken from base::win::ScopedCOMInitializer. +// +// WARNING: This should only be used once per thread, ideally scoped to a +// similar lifetime as the thread itself. You should not be using this in +// random utility functions that make COM calls; instead ensure that these +// functions are running on a COM-supporting thread! +// See https://msdn.microsoft.com/en-us/library/ms809971.aspx for details. +class ScopedCOMInitializer { + public: + // Enum value provided to initialize the thread as an MTA instead of STA. + // There are two types of apartments, Single Threaded Apartments (STAs) + // and Multi Threaded Apartments (MTAs). Within a given process there can + // be multiple STA’s but there is only one MTA. STA is typically used by + // "GUI applications" and MTA by "worker threads" with no UI message loop. + enum SelectMTA { kMTA }; + + // Constructor for STA initialization. + ScopedCOMInitializer(); + + // Constructor for MTA initialization. + explicit ScopedCOMInitializer(SelectMTA mta); + + ~ScopedCOMInitializer(); + + ScopedCOMInitializer(const ScopedCOMInitializer&) = delete; + ScopedCOMInitializer& operator=(const ScopedCOMInitializer&) = delete; + + bool Succeeded() { return SUCCEEDED(hr_); } + + private: + void Initialize(COINIT init); + + HRESULT hr_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_WIN_SCOPED_COM_INITIALIZER_H_ diff --git a/pkg/apm/webrtc/rtc_base/win/windows_version.h b/pkg/apm/webrtc/rtc_base/win/windows_version.h new file mode 100644 index 00000000..cbe7cfd8 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/win/windows_version.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_WIN_WINDOWS_VERSION_H_ +#define RTC_BASE_WIN_WINDOWS_VERSION_H_ + +#include + +#include + +typedef void* HANDLE; + +namespace webrtc { +namespace rtc_win { + +// The running version of Windows. This is declared outside OSInfo for +// syntactic sugar reasons; see the declaration of GetVersion() below. +// NOTE: Keep these in order so callers can do things like +// "if (rtc_win::GetVersion() >= rtc_win::VERSION_VISTA) ...". +// +// This enum is used in metrics histograms, so they shouldn't be reordered or +// removed. New values can be added before VERSION_WIN_LAST. +enum Version { + VERSION_PRE_XP = 0, // Not supported. + VERSION_XP = 1, + VERSION_SERVER_2003 = 2, // Also includes XP Pro x64 and Server 2003 R2. + VERSION_VISTA = 3, // Also includes Windows Server 2008. + VERSION_WIN7 = 4, // Also includes Windows Server 2008 R2. + VERSION_WIN8 = 5, // Also includes Windows Server 2012. + VERSION_WIN8_1 = 6, // Also includes Windows Server 2012 R2. + VERSION_WIN10 = 7, // Threshold 1: Version 1507, Build 10240. + VERSION_WIN10_TH2 = 8, // Threshold 2: Version 1511, Build 10586. + VERSION_WIN10_RS1 = 9, // Redstone 1: Version 1607, Build 14393. + VERSION_WIN10_RS2 = 10, // Redstone 2: Version 1703, Build 15063. + VERSION_WIN10_RS3 = 11, // Redstone 3: Version 1709, Build 16299. + VERSION_WIN10_RS4 = 12, // Redstone 4: Version 1803, Build 17134. + VERSION_WIN10_RS5 = 13, // Redstone 5: Version 1809, Build 17763. + VERSION_WIN10_19H1 = 14, // 19H1: Version 1903, Build 18362. + VERSION_WIN10_19H2 = 15, // 19H2: Version 1909, Build 18363. + VERSION_WIN10_20H1 = 16, // 20H1: Version 2004, Build 19041. + VERSION_WIN10_20H2 = 17, // 20H2: Build 19042. + VERSION_WIN10_21H1 = 18, // 21H1: Build 19043. + VERSION_WIN10_21H2 = 19, // 21H2: Build 19044. + VERSION_SERVER_2022 = 20, // Server 2022: Build 20348. + VERSION_WIN11 = 21, // Windows 11: Build 22000. + VERSION_WIN_LAST, // Indicates error condition. +}; + +// A rough bucketing of the available types of versions of Windows. This is used +// to distinguish enterprise enabled versions from home versions and potentially +// server versions. Keep these values in the same order, since they are used as +// is for metrics histogram ids. +enum VersionType { + SUITE_HOME = 0, + SUITE_PROFESSIONAL, + SUITE_SERVER, + SUITE_ENTERPRISE, + SUITE_EDUCATION, + SUITE_LAST, +}; + +// A singleton that can be used to query various pieces of information about the +// OS and process state. Note that this doesn't use the base Singleton class, so +// it can be used without an AtExitManager. +class OSInfo { + public: + struct VersionNumber { + int major; + int minor; + int build; + int patch; + }; + + struct ServicePack { + int major; + int minor; + }; + + // The processor architecture this copy of Windows natively uses. For + // example, given an x64-capable processor, we have three possibilities: + // 32-bit Chrome running on 32-bit Windows: X86_ARCHITECTURE + // 32-bit Chrome running on 64-bit Windows via WOW64: X64_ARCHITECTURE + // 64-bit Chrome running on 64-bit Windows: X64_ARCHITECTURE + enum WindowsArchitecture { + X86_ARCHITECTURE, + X64_ARCHITECTURE, + IA64_ARCHITECTURE, + OTHER_ARCHITECTURE, + }; + + // Whether a process is running under WOW64 (the wrapper that allows 32-bit + // processes to run on 64-bit versions of Windows). This will return + // WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit + // Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g. + // the process does not have sufficient access rights to determine this. + enum WOW64Status { + WOW64_DISABLED, + WOW64_ENABLED, + WOW64_UNKNOWN, + }; + + OSInfo(const OSInfo&) = delete; + OSInfo& operator=(const OSInfo&) = delete; + + static OSInfo* GetInstance(); + + Version version() const { return version_; } + VersionNumber version_number() const { return version_number_; } + VersionType version_type() const { return version_type_; } + ServicePack service_pack() const { return service_pack_; } + std::string service_pack_str() const { return service_pack_str_; } + WindowsArchitecture architecture() const { return architecture_; } + int processors() const { return processors_; } + size_t allocation_granularity() const { return allocation_granularity_; } + WOW64Status wow64_status() const { return wow64_status_; } + std::string processor_model_name(); + + // Like wow64_status(), but for the supplied handle instead of the current + // process. This doesn't touch member state, so you can bypass the singleton. + static WOW64Status GetWOW64StatusForProcess(HANDLE process_handle); + + private: + OSInfo(); + ~OSInfo(); + + Version version_; + VersionNumber version_number_; + VersionType version_type_; + ServicePack service_pack_; + + // A string, such as "Service Pack 3", that indicates the latest Service Pack + // installed on the system. If no Service Pack has been installed, the string + // is empty. + std::string service_pack_str_; + WindowsArchitecture architecture_; + int processors_; + size_t allocation_granularity_; + WOW64Status wow64_status_; + std::string processor_model_name_; +}; + +// Because this is by far the most commonly-requested value from the above +// singleton, we add a global-scope accessor here as syntactic sugar. +Version GetVersion(); + +} // namespace rtc_win +} // namespace webrtc + +#endif // RTC_BASE_WIN_WINDOWS_VERSION_H_ diff --git a/pkg/apm/webrtc/rtc_base/win32.h b/pkg/apm/webrtc/rtc_base/win32.h new file mode 100644 index 00000000..19bc067a --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/win32.h @@ -0,0 +1,57 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_WIN32_H_ +#define RTC_BASE_WIN32_H_ + +#ifndef WEBRTC_WIN +#error "Only #include this header in Windows builds" +#endif + +// Make sure we don't get min/max macros +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include + +// Must be after winsock2.h. +#include + +typedef int socklen_t; + +#ifndef SECURITY_MANDATORY_LABEL_AUTHORITY +// Add defines that we use if we are compiling against older sdks +#define SECURITY_MANDATORY_MEDIUM_RID (0x00002000L) +#define TokenIntegrityLevel static_cast(0x19) +typedef struct _TOKEN_MANDATORY_LABEL { + SID_AND_ATTRIBUTES Label; +} TOKEN_MANDATORY_LABEL, *PTOKEN_MANDATORY_LABEL; +#endif // SECURITY_MANDATORY_LABEL_AUTHORITY + +#undef SetPort + +namespace webrtc { + +const char* win32_inet_ntop(int af, const void* src, char* dst, socklen_t size); +int win32_inet_pton(int af, const char* src, void* dst); + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::win32_inet_ntop; +using ::webrtc::win32_inet_pton; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_WIN32_H_ diff --git a/pkg/apm/webrtc/rtc_base/win32_socket_init.h b/pkg/apm/webrtc/rtc_base/win32_socket_init.h new file mode 100644 index 00000000..6499cd82 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/win32_socket_init.h @@ -0,0 +1,41 @@ +/* + * Copyright 2009 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_WIN32_SOCKET_INIT_H_ +#define RTC_BASE_WIN32_SOCKET_INIT_H_ + +#ifndef WEBRTC_WIN +#error "Only #include this header in Windows builds" +#endif + +#include "rtc_base/win32.h" + +namespace webrtc { + +class WinsockInitializer { + public: + WinsockInitializer() { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(1, 0); + err_ = WSAStartup(wVersionRequested, &wsaData); + } + ~WinsockInitializer() { + if (!err_) + WSACleanup(); + } + int error() { return err_; } + + private: + int err_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_WIN32_SOCKET_INIT_H_ diff --git a/pkg/apm/webrtc/rtc_base/zero_memory.cc b/pkg/apm/webrtc/rtc_base/zero_memory.cc new file mode 100644 index 00000000..2727ed9e --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/zero_memory.cc @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#if defined(WEBRTC_WIN) +#include +#else +#include +#endif + +#include "rtc_base/checks.h" +#include "rtc_base/zero_memory.h" + +namespace webrtc { + +// Code and comment taken from "OPENSSL_cleanse" of BoringSSL. +void ExplicitZeroMemory(void* ptr, size_t len) { + RTC_DCHECK(ptr || !len); +#if defined(WEBRTC_WIN) + SecureZeroMemory(ptr, len); +#else + memset(ptr, 0, len); +#if !defined(__pnacl__) + /* As best as we can tell, this is sufficient to break any optimisations that + might try to eliminate "superfluous" memsets. If there's an easy way to + detect memset_s, it would be better to use that. */ + __asm__ __volatile__("" : : "r"(ptr) : "memory"); // NOLINT +#endif +#endif // !WEBRTC_WIN +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/rtc_base/zero_memory.h b/pkg/apm/webrtc/rtc_base/zero_memory.h new file mode 100644 index 00000000..1babf370 --- /dev/null +++ b/pkg/apm/webrtc/rtc_base/zero_memory.h @@ -0,0 +1,43 @@ +/* + * 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 RTC_BASE_ZERO_MEMORY_H_ +#define RTC_BASE_ZERO_MEMORY_H_ + +#include + +#include + +#include "api/array_view.h" + +namespace webrtc { + +// Fill memory with zeros in a way that the compiler doesn't optimize it away +// even if the pointer is not used afterwards. +void ExplicitZeroMemory(void* ptr, size_t len); + +template ::value && + std::is_trivial::value>::type* = nullptr> +void ExplicitZeroMemory(ArrayView a) { + ExplicitZeroMemory(a.data(), a.size()); +} + +} // namespace webrtc + +// Re-export symbols from the webrtc namespace for backwards compatibility. +// TODO(bugs.webrtc.org/4222596): Remove once all references are updated. +#ifdef WEBRTC_ALLOW_DEPRECATED_NAMESPACES +namespace rtc { +using ::webrtc::ExplicitZeroMemory; +} // namespace rtc +#endif // WEBRTC_ALLOW_DEPRECATED_NAMESPACES + +#endif // RTC_BASE_ZERO_MEMORY_H_ diff --git a/pkg/apm/webrtc/system_wrappers/include/clock.h b/pkg/apm/webrtc/system_wrappers/include/clock.h new file mode 100644 index 00000000..ec4bae0a --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/include/clock.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_ +#define SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_ + +#include + +#include +#include + +#include "api/units/timestamp.h" +#include "rtc_base/system/rtc_export.h" +#include "system_wrappers/include/ntp_time.h" + +namespace webrtc { + +// January 1970, in NTP seconds. +const uint32_t kNtpJan1970 = 2208988800UL; + +// Magic NTP fractional unit. +const double kMagicNtpFractionalUnit = 4.294967296E+9; + +// A clock interface that allows reading of absolute and relative timestamps. +class RTC_EXPORT Clock { + public: + virtual ~Clock() {} + + // Return a timestamp relative to an unspecified epoch. + virtual Timestamp CurrentTime() = 0; + int64_t TimeInMilliseconds() { return CurrentTime().ms(); } + int64_t TimeInMicroseconds() { return CurrentTime().us(); } + + // Retrieve an NTP absolute timestamp (with an epoch of Jan 1, 1900). + NtpTime CurrentNtpTime() { return ConvertTimestampToNtpTime(CurrentTime()); } + int64_t CurrentNtpInMilliseconds() { return CurrentNtpTime().ToMs(); } + + // Converts between a relative timestamp returned by this clock, to NTP time. + virtual NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) = 0; + int64_t ConvertTimestampToNtpTimeInMilliseconds(int64_t timestamp_ms) { + return ConvertTimestampToNtpTime(Timestamp::Millis(timestamp_ms)).ToMs(); + } + + // Converts NtpTime to a Timestamp with UTC epoch. + // A `Minus Infinity` Timestamp is returned if the NtpTime is invalid. + static Timestamp NtpToUtc(NtpTime ntp_time) { + if (!ntp_time.Valid()) { + return Timestamp::MinusInfinity(); + } + // Seconds since UTC epoch. + int64_t time = ntp_time.seconds() - kNtpJan1970; + // Microseconds since UTC epoch (not including NTP fraction) + time = time * 1'000'000; + // Fractions part of the NTP time, in microseconds. + int64_t time_fraction = + DivideRoundToNearest(int64_t{ntp_time.fractions()} * 1'000'000, + NtpTime::kFractionsPerSecond); + return Timestamp::Micros(time + time_fraction); + } + + // Returns an instance of the real-time system clock implementation. + static Clock* GetRealTimeClock(); +}; + +class SimulatedClock : public Clock { + public: + // The constructors assume an epoch of Jan 1, 1970. + explicit SimulatedClock(int64_t initial_time_us); + explicit SimulatedClock(Timestamp initial_time); + ~SimulatedClock() override; + + // Return a timestamp with an epoch of Jan 1, 1970. + Timestamp CurrentTime() override; + + NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) override; + + // Advance the simulated clock with a given number of milliseconds or + // microseconds. + void AdvanceTimeMilliseconds(int64_t milliseconds); + void AdvanceTimeMicroseconds(int64_t microseconds); + void AdvanceTime(TimeDelta delta); + + private: + // The time is read and incremented with relaxed order. Each thread will see + // monotonically increasing time, and when threads post tasks or messages to + // one another, the synchronization done as part of the message passing should + // ensure that any causual chain of events on multiple threads also + // corresponds to monotonically increasing time. + std::atomic time_us_; +}; + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_ diff --git a/pkg/apm/webrtc/system_wrappers/include/cpu_features_wrapper.h b/pkg/apm/webrtc/system_wrappers/include/cpu_features_wrapper.h new file mode 100644 index 00000000..254e2d8d --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/include/cpu_features_wrapper.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SYSTEM_WRAPPERS_INCLUDE_CPU_FEATURES_WRAPPER_H_ +#define SYSTEM_WRAPPERS_INCLUDE_CPU_FEATURES_WRAPPER_H_ + +#include + +namespace webrtc { + +// List of features in x86. +typedef enum { kSSE2, kSSE3, kAVX2, kFMA3 } CPUFeature; + +// List of features in ARM. +enum { + kCPUFeatureARMv7 = (1 << 0), + kCPUFeatureVFPv3 = (1 << 1), + kCPUFeatureNEON = (1 << 2), + kCPUFeatureLDREXSTREX = (1 << 3) +}; + +// Returns true if the CPU supports the feature. +int GetCPUInfo(CPUFeature feature); + +// No CPU feature is available => straight C path. +int GetCPUInfoNoASM(CPUFeature feature); + +// Return the features in an ARM device. +// It detects the features in the hardware platform, and returns supported +// values in the above enum definition as a bitmask. +uint64_t GetCPUFeaturesARM(void); + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INCLUDE_CPU_FEATURES_WRAPPER_H_ diff --git a/pkg/apm/webrtc/system_wrappers/include/cpu_info.h b/pkg/apm/webrtc/system_wrappers/include/cpu_info.h new file mode 100644 index 00000000..ab546c72 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/include/cpu_info.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SYSTEM_WRAPPERS_INCLUDE_CPU_INFO_H_ +#define SYSTEM_WRAPPERS_INCLUDE_CPU_INFO_H_ + +#include + +namespace webrtc { + +class CpuInfo { + public: + static uint32_t DetectNumberOfCores(); + + private: + CpuInfo() {} +}; + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INCLUDE_CPU_INFO_H_ diff --git a/pkg/apm/webrtc/system_wrappers/include/denormal_disabler.h b/pkg/apm/webrtc/system_wrappers/include/denormal_disabler.h new file mode 100644 index 00000000..bd3d4013 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/include/denormal_disabler.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SYSTEM_WRAPPERS_INCLUDE_DENORMAL_DISABLER_H_ +#define SYSTEM_WRAPPERS_INCLUDE_DENORMAL_DISABLER_H_ + +#include "rtc_base/system/arch.h" + +namespace webrtc { + +// Activates the hardware (HW) way to flush denormals (see [1]) to zero as they +// can very seriously impact performance. At destruction time restores the +// denormals handling state read by the ctor; hence, supports nested calls. +// Equals a no-op if the architecture is not x86 or ARM or if the compiler is +// not CLANG. +// [1] https://en.wikipedia.org/wiki/Denormal_number +// +// Example usage: +// +// void Foo() { +// DenormalDisabler d; +// ... +// } +class DenormalDisabler { + public: + // Ctor. If architecture and compiler are supported, stores the HW settings + // for denormals, disables denormals and sets `disabling_activated_` to true. + // Otherwise, only sets `disabling_activated_` to false. + DenormalDisabler(); + // Ctor. Same as above, but also requires `enabled` to be true to disable + // denormals. + explicit DenormalDisabler(bool enabled); + DenormalDisabler(const DenormalDisabler&) = delete; + DenormalDisabler& operator=(const DenormalDisabler&) = delete; + // Dtor. If `disabling_activated_` is true, restores the denormals HW settings + // read by the ctor before denormals were disabled. Otherwise it's a no-op. + ~DenormalDisabler(); + + // Returns true if architecture and compiler are supported. + static bool IsSupported(); + + private: + const int status_word_; + const bool disabling_activated_; +}; + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INCLUDE_DENORMAL_DISABLER_H_ diff --git a/pkg/apm/webrtc/system_wrappers/include/field_trial.h b/pkg/apm/webrtc/system_wrappers/include/field_trial.h new file mode 100644 index 00000000..8d0ad258 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/include/field_trial.h @@ -0,0 +1,116 @@ +// +// 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 SYSTEM_WRAPPERS_INCLUDE_FIELD_TRIAL_H_ +#define SYSTEM_WRAPPERS_INCLUDE_FIELD_TRIAL_H_ + +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/containers/flat_set.h" + +// Field trials allow webrtc clients (such as Chrome) to turn on feature code +// in binaries out in the field and gather information with that. +// +// By default WebRTC provides an implementation of field trials that can be +// found in system_wrappers/source/field_trial.cc. If clients want to provide +// a custom version, they will have to: +// +// 1. Compile WebRTC defining the preprocessor macro +// WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT (if GN is used this can be achieved +// by setting the GN arg rtc_exclude_field_trial_default to true). +// 2. Provide an implementation of: +// std::string webrtc::field_trial::FindFullName(absl::string_view trial). +// +// They are designed to wire up directly to chrome field trials and to speed up +// developers by reducing the need to wire APIs to control whether a feature is +// on/off. E.g. to experiment with a new method that could lead to a different +// trade-off between CPU/bandwidth: +// +// 1 - Develop the feature with default behaviour off: +// +// if (FieldTrial::FindFullName("WebRTCExperimentMethod2") == "Enabled") +// method2(); +// else +// method1(); +// +// 2 - Once the changes are rolled to chrome, the new code path can be +// controlled as normal chrome field trials. +// +// 3 - Evaluate the new feature and clean the code paths. +// +// Notes: +// - NOT every feature is a candidate to be controlled by this mechanism as +// it may require negotiation between involved parties (e.g. SDP). +// +// TODO(andresp): since chrome --force-fieldtrials does not marks the trial +// as active it does not get propagated to the renderer process. For now one +// needs to push a config with start_active:true or run a local finch +// server. +// +// TODO(andresp): find out how to get bots to run tests with trials enabled. + +namespace webrtc { +namespace field_trial { + +// Returns the group name chosen for the named trial, or the empty string +// if the trial does not exists. +// +// Note: To keep things tidy append all the trial names with WebRTC. +std::string FindFullName(absl::string_view name); + +// Convenience method, returns true iff FindFullName(name) return a string that +// starts with "Enabled". +// TODO(tommi): Make sure all implementations support this. +inline bool IsEnabled(absl::string_view name) { + return FindFullName(name).find("Enabled") == 0; +} + +// Convenience method, returns true iff FindFullName(name) return a string that +// starts with "Disabled". +inline bool IsDisabled(absl::string_view name) { + return FindFullName(name).find("Disabled") == 0; +} + +// Optionally initialize field trial from a string. +// This method can be called at most once before any other call into webrtc. +// E.g. before the peer connection factory is constructed. +// Note: trials_string must never be destroyed. +void InitFieldTrialsFromString(const char* trials_string); + +const char* GetFieldTrialString(); + +// Validates the given field trial string. +bool FieldTrialsStringIsValid(absl::string_view trials_string); + +// Merges two field trial strings. +// +// If a key (trial) exists twice with conflicting values (groups), the value +// in 'second' takes precedence. +// Shall only be called with valid FieldTrial strings. +std::string MergeFieldTrialsStrings(absl::string_view first, + absl::string_view second); + +// This helper allows to temporary "register" a field trial within the current +// scope. This is only useful for tests that use the global field trial string, +// otherwise you can use `webrtc::FieldTrialsRegistry`. +// +// If you want to isolate changes to the global field trial string itself within +// the current scope you should use `webrtc::test::ScopedFieldTrials`. +class FieldTrialsAllowedInScopeForTesting { + public: + explicit FieldTrialsAllowedInScopeForTesting(flat_set keys); + ~FieldTrialsAllowedInScopeForTesting(); +}; + +} // namespace field_trial +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INCLUDE_FIELD_TRIAL_H_ diff --git a/pkg/apm/webrtc/system_wrappers/include/metrics.h b/pkg/apm/webrtc/system_wrappers/include/metrics.h new file mode 100644 index 00000000..a447cce4 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/include/metrics.h @@ -0,0 +1,468 @@ +// +// 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 SYSTEM_WRAPPERS_INCLUDE_METRICS_H_ +#define SYSTEM_WRAPPERS_INCLUDE_METRICS_H_ + +#include + +#include // IWYU pragma: keep +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/string_utils.h" + +#if defined(RTC_DISABLE_METRICS) +#define RTC_METRICS_ENABLED 0 +#else +#define RTC_METRICS_ENABLED 1 +#endif + +namespace webrtc { +namespace metrics_impl { +template +void NoOp(const Ts&...) {} +} // namespace metrics_impl +} // namespace webrtc + +#if RTC_METRICS_ENABLED +#define EXPECT_METRIC_EQ(val1, val2) EXPECT_EQ(val1, val2) +#define EXPECT_METRIC_GT(val1, val2) EXPECT_GT(val1, val2) +#define EXPECT_METRIC_LE(val1, val2) EXPECT_LE(val1, val2) +#define EXPECT_METRIC_TRUE(conditon) EXPECT_TRUE(conditon) +#define EXPECT_METRIC_FALSE(conditon) EXPECT_FALSE(conditon) +#define EXPECT_METRIC_THAT(value, matcher) EXPECT_THAT(value, matcher) +#else +#define EXPECT_METRIC_EQ(val1, val2) webrtc::metrics_impl::NoOp(val1, val2) +#define EXPECT_METRIC_GT(val1, val2) webrtc::metrics_impl::NoOp(val1, val2) +#define EXPECT_METRIC_LE(val1, val2) webrtc::metrics_impl::NoOp(val1, val2) +#define EXPECT_METRIC_TRUE(condition) \ + webrtc::metrics_impl::NoOp(condition || true) +#define EXPECT_METRIC_FALSE(condition) \ + webrtc::metrics_impl::NoOp(condition && false) +#define EXPECT_METRIC_THAT(value, matcher) \ + webrtc::metrics_impl::NoOp(value, testing::_) +#endif + +#if RTC_METRICS_ENABLED +// Macros for allowing WebRTC clients (e.g. Chrome) to gather and aggregate +// statistics. +// +// Histogram for counters. +// RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count); +// +// Histogram for enumerators. +// The boundary should be above the max enumerator sample. +// RTC_HISTOGRAM_ENUMERATION(name, sample, boundary); +// +// +// The macros use the methods HistogramFactoryGetCounts, +// HistogramFactoryGetEnumeration and HistogramAdd. +// +// By default WebRTC provides implementations of the aforementioned methods +// that can be found in system_wrappers/source/metrics.cc. If clients want to +// provide a custom version, they will have to: +// +// 1. Compile WebRTC defining the preprocessor macro +// WEBRTC_EXCLUDE_METRICS_DEFAULT (if GN is used this can be achieved +// by setting the GN arg rtc_exclude_metrics_default to true). +// 2. Provide implementations of: +// Histogram* webrtc::metrics::HistogramFactoryGetCounts( +// absl::string_view name, int sample, int min, int max, +// int bucket_count); +// Histogram* webrtc::metrics::HistogramFactoryGetEnumeration( +// absl::string_view name, int sample, int boundary); +// void webrtc::metrics::HistogramAdd( +// Histogram* histogram_pointer, absl::string_view name, int sample); +// +// Example usage: +// +// RTC_HISTOGRAM_COUNTS("WebRTC.Video.NacksSent", nacks_sent, 1, 100000, 100); +// +// enum Types { +// kTypeX, +// kTypeY, +// kBoundary, +// }; +// +// RTC_HISTOGRAM_ENUMERATION("WebRTC.Types", kTypeX, kBoundary); +// +// NOTE: It is recommended to do the Chromium review for modifications to +// histograms.xml before new metrics are committed to WebRTC. + +// Macros for adding samples to a named histogram. + +// Histogram for counters (exponentially spaced buckets). +#define RTC_HISTOGRAM_COUNTS_100(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 100, 50) + +#define RTC_HISTOGRAM_COUNTS_200(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 200, 50) + +#define RTC_HISTOGRAM_COUNTS_500(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 500, 50) + +#define RTC_HISTOGRAM_COUNTS_1000(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 1000, 50) + +#define RTC_HISTOGRAM_COUNTS_10000(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 10000, 50) + +#define RTC_HISTOGRAM_COUNTS_100000(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 100000, 50) + +#define RTC_HISTOGRAM_COUNTS_1M(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 1'000'000, 50) + +#define RTC_HISTOGRAM_COUNTS_1G(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 1'000'000'000, 50) + +#define RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count) \ + RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \ + webrtc::metrics::HistogramFactoryGetCounts( \ + name, min, max, bucket_count)) + +#define RTC_HISTOGRAM_COUNTS_LINEAR(name, sample, min, max, bucket_count) \ + RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \ + webrtc::metrics::HistogramFactoryGetCountsLinear( \ + name, min, max, bucket_count)) + +// Slow metrics: pointer to metric is acquired at each call and is not cached. +// +#define RTC_HISTOGRAM_COUNTS_SPARSE_100(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 100, 50) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_200(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 200, 50) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_500(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 500, 50) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_1000(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 1000, 50) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_10000(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 10000, 50) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_100000(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 100000, 50) + +#define RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, min, max, bucket_count) \ + RTC_HISTOGRAM_COMMON_BLOCK_SLOW(name, sample, \ + webrtc::metrics::HistogramFactoryGetCounts( \ + name, min, max, bucket_count)) + +// Histogram for percentage (evenly spaced buckets). +#define RTC_HISTOGRAM_PERCENTAGE_SPARSE(name, sample) \ + RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, 101) + +// Histogram for booleans. +#define RTC_HISTOGRAM_BOOLEAN_SPARSE(name, sample) \ + RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, 2) + +// Histogram for enumerators (evenly spaced buckets). +// `boundary` should be above the max enumerator sample. +// +// TODO(qingsi): Refactor the default implementation given by RtcHistogram, +// which is already sparse, and remove the boundary argument from the macro. +#define RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, boundary) \ + RTC_HISTOGRAM_COMMON_BLOCK_SLOW( \ + name, sample, \ + webrtc::metrics::SparseHistogramFactoryGetEnumeration(name, boundary)) + +// Histogram for percentage (evenly spaced buckets). +#define RTC_HISTOGRAM_PERCENTAGE(name, sample) \ + RTC_HISTOGRAM_ENUMERATION(name, sample, 101) + +// Histogram for booleans. +#define RTC_HISTOGRAM_BOOLEAN(name, sample) \ + RTC_HISTOGRAM_ENUMERATION(name, sample, 2) + +// Histogram for enumerators (evenly spaced buckets). +// `boundary` should be above the max enumerator sample. +#define RTC_HISTOGRAM_ENUMERATION(name, sample, boundary) \ + RTC_HISTOGRAM_COMMON_BLOCK_SLOW( \ + name, sample, \ + webrtc::metrics::HistogramFactoryGetEnumeration(name, boundary)) + +// The name of the histogram should not vary. +#define RTC_HISTOGRAM_COMMON_BLOCK(constant_name, sample, \ + factory_get_invocation) \ + do { \ + static std::atomic atomic_histogram_pointer( \ + nullptr); \ + webrtc::metrics::Histogram* histogram_pointer = \ + atomic_histogram_pointer.load(std::memory_order_acquire); \ + if (!histogram_pointer) { \ + histogram_pointer = factory_get_invocation; \ + webrtc::metrics::Histogram* null_histogram = nullptr; \ + atomic_histogram_pointer.compare_exchange_strong(null_histogram, \ + histogram_pointer); \ + } \ + if (histogram_pointer) { \ + webrtc::metrics::HistogramAdd(histogram_pointer, sample); \ + } \ + } while (0) + +// The histogram is constructed/found for each call. +// May be used for histograms with infrequent updates.` +#define RTC_HISTOGRAM_COMMON_BLOCK_SLOW(name, sample, factory_get_invocation) \ + do { \ + webrtc::metrics::Histogram* histogram_pointer = factory_get_invocation; \ + if (histogram_pointer) { \ + webrtc::metrics::HistogramAdd(histogram_pointer, sample); \ + } \ + } while (0) + +// Helper macros. +// Macros for calling a histogram with varying name (e.g. when using a metric +// in different modes such as real-time vs screenshare). Fast, because pointer +// is cached. `index` should be different for different names. Allowed `index` +// values are 0, 1, and 2. +#define RTC_HISTOGRAMS_COUNTS_100(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 100, 50)) + +#define RTC_HISTOGRAMS_COUNTS_200(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 200, 50)) + +#define RTC_HISTOGRAMS_COUNTS_500(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 500, 50)) + +#define RTC_HISTOGRAMS_COUNTS_1000(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 1000, 50)) + +#define RTC_HISTOGRAMS_COUNTS_10000(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 10000, 50)) + +#define RTC_HISTOGRAMS_COUNTS_100000(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 100000, 50)) + +#define RTC_HISTOGRAMS_ENUMERATION(index, name, sample, boundary) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_ENUMERATION(name, sample, boundary)) + +#define RTC_HISTOGRAMS_PERCENTAGE(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_PERCENTAGE(name, sample)) + +#define RTC_HISTOGRAMS_COMMON(index, name, sample, macro_invocation) \ + do { \ + switch (index) { \ + case 0: \ + macro_invocation; \ + break; \ + case 1: \ + macro_invocation; \ + break; \ + case 2: \ + macro_invocation; \ + break; \ + default: \ + RTC_DCHECK_NOTREACHED(); \ + } \ + } while (0) + +#else + +//////////////////////////////////////////////////////////////////////////////// +// This section defines no-op alternatives to the metrics macros when +// RTC_METRICS_ENABLED is defined. + +#define RTC_HISTOGRAM_COUNTS_100(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_200(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_500(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_1000(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_10000(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_100000(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_1M(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_1G(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count) \ + webrtc::metrics_impl::NoOp(name, sample, min, max, bucket_count) + +#define RTC_HISTOGRAM_COUNTS_LINEAR(name, sample, min, max, bucket_count) \ + webrtc::metrics_impl::NoOp(name, sample, min, max, bucket_count) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_100(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_200(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_500(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_1000(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_10000(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_100000(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, min, max, bucket_count) \ + webrtc::metrics_impl::NoOp(name, sample, min, max, bucket_count) + +#define RTC_HISTOGRAM_PERCENTAGE_SPARSE(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_BOOLEAN_SPARSE(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, boundary) \ + webrtc::metrics_impl::NoOp(name, sample, boundary) + +#define RTC_HISTOGRAM_PERCENTAGE(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_BOOLEAN(name, sample) \ + webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_ENUMERATION(name, sample, boundary) \ + webrtc::metrics_impl::NoOp(name, sample, boundary) + +#define RTC_HISTOGRAM_COMMON_BLOCK(constant_name, sample, \ + factory_get_invocation) \ + webrtc::metrics_impl::NoOp(constant_name, sample, factory_get_invocation) + +#define RTC_HISTOGRAM_COMMON_BLOCK_SLOW(name, sample, factory_get_invocation) \ + webrtc::metrics_impl::NoOp(name, sample, factory_get_invocation) + +#define RTC_HISTOGRAMS_COUNTS_100(index, name, sample) \ + webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COUNTS_200(index, name, sample) \ + webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COUNTS_500(index, name, sample) \ + webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COUNTS_1000(index, name, sample) \ + webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COUNTS_10000(index, name, sample) \ + webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COUNTS_100000(index, name, sample) \ + webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_ENUMERATION(index, name, sample, boundary) \ + webrtc::metrics_impl::NoOp(index, name, sample, boundary) + +#define RTC_HISTOGRAMS_PERCENTAGE(index, name, sample) \ + webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COMMON(index, name, sample, macro_invocation) \ + webrtc::metrics_impl::NoOp(index, name, sample, macro_invocation) + +#endif // RTC_METRICS_ENABLED + +namespace webrtc { +namespace metrics { + +// Time that should have elapsed for stats that are gathered once per call. +constexpr int kMinRunTimeInSeconds = 10; + +class Histogram; + +// Functions for getting pointer to histogram (constructs or finds the named +// histogram). + +// Get histogram for counters. +Histogram* HistogramFactoryGetCounts(absl::string_view name, + int min, + int max, + int bucket_count); + +// Get histogram for counters with linear bucket spacing. +Histogram* HistogramFactoryGetCountsLinear(absl::string_view name, + int min, + int max, + int bucket_count); + +// Get histogram for enumerators. +// `boundary` should be above the max enumerator sample. +Histogram* HistogramFactoryGetEnumeration(absl::string_view name, int boundary); + +// Get sparse histogram for enumerators. +// `boundary` should be above the max enumerator sample. +Histogram* SparseHistogramFactoryGetEnumeration(absl::string_view name, + int boundary); + +// Function for adding a `sample` to a histogram. +void HistogramAdd(Histogram* histogram_pointer, int sample); + +struct SampleInfo { + SampleInfo(absl::string_view name, int min, int max, size_t bucket_count); + ~SampleInfo(); + + const std::string name; + const int min; + const int max; + const size_t bucket_count; + std::map samples; // +}; + +// Enables collection of samples. +// This method should be called before any other call into webrtc. +void Enable(); + +// Gets histograms and clears all samples. +void GetAndReset( + std::map, AbslStringViewCmp>* + histograms); + +// Functions below are mainly for testing. + +// Clears all samples. +void Reset(); + +// Returns the number of times the `sample` has been added to the histogram. +int NumEvents(absl::string_view name, int sample); + +// Returns the total number of added samples to the histogram. +int NumSamples(absl::string_view name); + +// Returns the minimum sample value (or -1 if the histogram has no samples). +int MinSample(absl::string_view name); + +// Returns a map with keys the samples with at least one event and values the +// number of events for that sample. +std::map Samples(absl::string_view name); + +} // namespace metrics +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INCLUDE_METRICS_H_ diff --git a/pkg/apm/webrtc/system_wrappers/include/ntp_time.h b/pkg/apm/webrtc/system_wrappers/include/ntp_time.h new file mode 100644 index 00000000..c1990362 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/include/ntp_time.h @@ -0,0 +1,136 @@ +/* + * 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 SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_ +#define SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_ + +#include +#include +#include + +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { + +class NtpTime { + public: + static constexpr uint64_t kFractionsPerSecond = 0x100000000; + NtpTime() : value_(0) {} + explicit NtpTime(uint64_t value) : value_(value) {} + NtpTime(uint32_t seconds, uint32_t fractions) + : value_(seconds * kFractionsPerSecond + fractions) {} + + NtpTime(const NtpTime&) = default; + NtpTime& operator=(const NtpTime&) = default; + explicit operator uint64_t() const { return value_; } + + void Set(uint32_t seconds, uint32_t fractions) { + value_ = seconds * kFractionsPerSecond + fractions; + } + void Reset() { value_ = 0; } + + int64_t ToMs() const { + static constexpr double kNtpFracPerMs = 4.294967296E6; // 2^32 / 1000. + const double frac_ms = static_cast(fractions()) / kNtpFracPerMs; + return 1000 * static_cast(seconds()) + + static_cast(frac_ms + 0.5); + } + // NTP standard (RFC1305, section 3.1) explicitly state value 0 is invalid. + bool Valid() const { return value_ != 0; } + + uint32_t seconds() const { + return dchecked_cast(value_ / kFractionsPerSecond); + } + uint32_t fractions() const { + return dchecked_cast(value_ % kFractionsPerSecond); + } + + private: + uint64_t value_; +}; + +inline bool operator==(const NtpTime& n1, const NtpTime& n2) { + return static_cast(n1) == static_cast(n2); +} +inline bool operator!=(const NtpTime& n1, const NtpTime& n2) { + return !(n1 == n2); +} + +// Converts `int64_t` milliseconds to Q32.32-formatted fixed-point seconds. +// Performs clamping if the result overflows or underflows. +inline int64_t Int64MsToQ32x32(int64_t milliseconds) { + // TODO(bugs.webrtc.org/10893): Change to use `webrtc::saturated_cast` once + // the bug has been fixed. + double result = + std::round(milliseconds * (NtpTime::kFractionsPerSecond / 1000.0)); + + // Explicitly cast values to double to avoid implicit conversion warnings + // The conversion of the std::numeric_limits::max() triggers + // -Wimplicit-int-float-conversion warning in clang 10.0.0 without explicit + // cast + if (result <= static_cast(std::numeric_limits::min())) { + return std::numeric_limits::min(); + } + + if (result >= static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + + return dchecked_cast(result); +} + +// Converts `int64_t` milliseconds to UQ32.32-formatted fixed-point seconds. +// Performs clamping if the result overflows or underflows. +inline uint64_t Int64MsToUQ32x32(int64_t milliseconds) { + // TODO(bugs.webrtc.org/10893): Change to use `webrtc::saturated_cast` once + // the bug has been fixed. + double result = + std::round(milliseconds * (NtpTime::kFractionsPerSecond / 1000.0)); + + // Explicitly cast values to double to avoid implicit conversion warnings + // The conversion of the std::numeric_limits::max() triggers + // -Wimplicit-int-float-conversion warning in clang 10.0.0 without explicit + // cast + if (result <= static_cast(std::numeric_limits::min())) { + return std::numeric_limits::min(); + } + + if (result >= static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + + return dchecked_cast(result); +} + +// Converts Q32.32-formatted fixed-point seconds to `int64_t` milliseconds. +inline int64_t Q32x32ToInt64Ms(int64_t q32x32) { + return dchecked_cast( + std::round(q32x32 * (1000.0 / NtpTime::kFractionsPerSecond))); +} + +// Converts UQ32.32-formatted fixed-point seconds to `int64_t` milliseconds. +inline int64_t UQ32x32ToInt64Ms(uint64_t q32x32) { + return dchecked_cast( + std::round(q32x32 * (1000.0 / NtpTime::kFractionsPerSecond))); +} + +// Converts UQ32.32-formatted fixed-point seconds to `int64_t` microseconds. +inline int64_t UQ32x32ToInt64Us(uint64_t q32x32) { + return dchecked_cast( + std::round(q32x32 * (1'000'000.0 / NtpTime::kFractionsPerSecond))); +} + +// Converts Q32.32-formatted fixed-point seconds to `int64_t` microseconds. +inline int64_t Q32x32ToInt64Us(int64_t q32x32) { + return dchecked_cast( + std::round(q32x32 * (1'000'000.0 / NtpTime::kFractionsPerSecond))); +} + +} // namespace webrtc +#endif // SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_ diff --git a/pkg/apm/webrtc/system_wrappers/include/rtp_to_ntp_estimator.h b/pkg/apm/webrtc/system_wrappers/include/rtp_to_ntp_estimator.h new file mode 100644 index 00000000..2f369817 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/include/rtp_to_ntp_estimator.h @@ -0,0 +1,72 @@ +/* + * 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 SYSTEM_WRAPPERS_INCLUDE_RTP_TO_NTP_ESTIMATOR_H_ +#define SYSTEM_WRAPPERS_INCLUDE_RTP_TO_NTP_ESTIMATOR_H_ + +#include + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/sequence_number_unwrapper.h" +#include "system_wrappers/include/ntp_time.h" + +namespace webrtc { + +// Converts an RTP timestamp to the NTP domain. +// The class needs to be trained with (at least 2) RTP/NTP timestamp pairs from +// RTCP sender reports before the convertion can be done. +class RtpToNtpEstimator { + public: + static constexpr int kMaxInvalidSamples = 3; + + RtpToNtpEstimator() = default; + RtpToNtpEstimator(const RtpToNtpEstimator&) = delete; + RtpToNtpEstimator& operator=(const RtpToNtpEstimator&) = delete; + ~RtpToNtpEstimator() = default; + + enum UpdateResult { kInvalidMeasurement, kSameMeasurement, kNewMeasurement }; + // Updates measurements with RTP/NTP timestamp pair from a RTCP sender report. + UpdateResult UpdateMeasurements(NtpTime ntp, uint32_t rtp_timestamp); + + // Converts an RTP timestamp to the NTP domain. + // Returns invalid NtpTime (i.e. NtpTime(0)) on failure. + NtpTime Estimate(uint32_t rtp_timestamp) const; + + // Returns estimated rtp_timestamp frequency, or 0 on failure. + double EstimatedFrequencyKhz() const; + + private: + // Estimated parameters from RTP and NTP timestamp pairs in `measurements_`. + // Defines linear estimation: NtpTime (in units of 1s/2^32) = + // `Parameters::slope` * rtp_timestamp + `Parameters::offset`. + struct Parameters { + double slope; + double offset; + }; + + // RTP and NTP timestamp pair from a RTCP SR report. + struct RtcpMeasurement { + NtpTime ntp_time; + int64_t unwrapped_rtp_timestamp; + }; + + void UpdateParameters(); + + int consecutive_invalid_samples_ = 0; + std::list measurements_; + std::optional params_; + mutable RtpTimestampUnwrapper unwrapper_; +}; +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INCLUDE_RTP_TO_NTP_ESTIMATOR_H_ diff --git a/pkg/apm/webrtc/system_wrappers/include/sleep.h b/pkg/apm/webrtc/system_wrappers/include/sleep.h new file mode 100644 index 00000000..3bf8df21 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/include/sleep.h @@ -0,0 +1,24 @@ +/* + * 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. + */ +// An OS-independent sleep function. + +#ifndef SYSTEM_WRAPPERS_INCLUDE_SLEEP_H_ +#define SYSTEM_WRAPPERS_INCLUDE_SLEEP_H_ + +namespace webrtc { + +// This function sleeps for the specified number of milliseconds. +// It may return early if the thread is woken by some other event, +// such as the delivery of a signal on Unix. +void SleepMs(int msecs); + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INCLUDE_SLEEP_H_ diff --git a/pkg/apm/webrtc/system_wrappers/source/clock.cc b/pkg/apm/webrtc/system_wrappers/source/clock.cc new file mode 100644 index 00000000..0fe5abc7 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/source/clock.cc @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/clock.h" + +#include "rtc_base/time_utils.h" + +namespace webrtc { +namespace { + +int64_t NtpOffsetUsCalledOnce() { + constexpr int64_t kNtpJan1970Sec = 2208988800; + int64_t clock_time = TimeMicros(); + int64_t utc_time = TimeUTCMicros(); + return utc_time - clock_time + kNtpJan1970Sec * kNumMicrosecsPerSec; +} + +NtpTime TimeMicrosToNtp(int64_t time_us) { + static int64_t ntp_offset_us = NtpOffsetUsCalledOnce(); + + int64_t time_ntp_us = time_us + ntp_offset_us; + RTC_DCHECK_GE(time_ntp_us, 0); // Time before year 1900 is unsupported. + + // Convert seconds to uint32 through uint64 for a well-defined cast. + // A wrap around, which will happen in 2036, is expected for NTP time. + uint32_t ntp_seconds = + static_cast(time_ntp_us / kNumMicrosecsPerSec); + + // Scale fractions of the second to NTP resolution. + constexpr int64_t kNtpFractionsInSecond = 1LL << 32; + int64_t us_fractions = time_ntp_us % kNumMicrosecsPerSec; + uint32_t ntp_fractions = + us_fractions * kNtpFractionsInSecond / kNumMicrosecsPerSec; + + return NtpTime(ntp_seconds, ntp_fractions); +} + +} // namespace + +class RealTimeClock : public Clock { + public: + RealTimeClock() = default; + + Timestamp CurrentTime() override { return Timestamp::Micros(TimeMicros()); } + + NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) override { + return TimeMicrosToNtp(timestamp.us()); + } +}; + +Clock* Clock::GetRealTimeClock() { + static Clock* const clock = new RealTimeClock(); + return clock; +} + +SimulatedClock::SimulatedClock(int64_t initial_time_us) + : time_us_(initial_time_us) {} + +SimulatedClock::SimulatedClock(Timestamp initial_time) + : SimulatedClock(initial_time.us()) {} + +SimulatedClock::~SimulatedClock() {} + +Timestamp SimulatedClock::CurrentTime() { + return Timestamp::Micros(time_us_.load(std::memory_order_relaxed)); +} + +NtpTime SimulatedClock::ConvertTimestampToNtpTime(Timestamp timestamp) { + int64_t now_us = timestamp.us(); + uint32_t seconds = (now_us / 1'000'000) + kNtpJan1970; + uint32_t fractions = static_cast( + (now_us % 1'000'000) * kMagicNtpFractionalUnit / 1'000'000); + return NtpTime(seconds, fractions); +} + +void SimulatedClock::AdvanceTimeMilliseconds(int64_t milliseconds) { + AdvanceTime(TimeDelta::Millis(milliseconds)); +} + +void SimulatedClock::AdvanceTimeMicroseconds(int64_t microseconds) { + AdvanceTime(TimeDelta::Micros(microseconds)); +} + +// TODO(bugs.webrtc.org(12102): It's desirable to let a single thread own +// advancement of the clock. We could then replace this read-modify-write +// operation with just a thread checker. But currently, that breaks a couple of +// tests, in particular, RepeatingTaskTest.ClockIntegration and +// CallStatsTest.LastProcessedRtt. +void SimulatedClock::AdvanceTime(TimeDelta delta) { + time_us_.fetch_add(delta.us(), std::memory_order_relaxed); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/system_wrappers/source/cpu_features.cc b/pkg/apm/webrtc/system_wrappers/source/cpu_features.cc new file mode 100644 index 00000000..4a6170c5 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/source/cpu_features.cc @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Parts of this file derived from Chromium's base/cpu.cc. + +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" + +#if defined(WEBRTC_ARCH_X86_FAMILY) && defined(_MSC_VER) +#include +#endif + +namespace webrtc { + +// No CPU feature is available => straight C path. +int GetCPUInfoNoASM(CPUFeature feature) { + (void)feature; + return 0; +} + +#if defined(WEBRTC_ARCH_X86_FAMILY) + +#if defined(WEBRTC_ENABLE_AVX2) +// xgetbv returns the value of an Intel Extended Control Register (XCR). +// Currently only XCR0 is defined by Intel so `xcr` should always be zero. +static uint64_t xgetbv(uint32_t xcr) { +#if defined(_MSC_VER) + return _xgetbv(xcr); +#else + uint32_t eax, edx; + + __asm__ volatile("xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr)); + return (static_cast(edx) << 32) | eax; +#endif // _MSC_VER +} +#endif // WEBRTC_ENABLE_AVX2 + +#ifndef _MSC_VER +// Intrinsic for "cpuid". +#if defined(__pic__) && defined(__i386__) +static inline void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile( + "mov %%ebx, %%edi\n" + "cpuid\n" + "xchg %%edi, %%ebx\n" + : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), + "=d"(cpu_info[3]) + : "a"(info_type)); +} +#else +static inline void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile("cpuid\n" + : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), + "=d"(cpu_info[3]) + : "a"(info_type), "c"(0)); +} +#endif +#endif // _MSC_VER +#endif // WEBRTC_ARCH_X86_FAMILY + +#if defined(WEBRTC_ARCH_X86_FAMILY) +// Actual feature detection for x86. +int GetCPUInfo(CPUFeature feature) { + int cpu_info[4]; + __cpuid(cpu_info, 1); + if (feature == kSSE2) { + return 0 != (cpu_info[3] & 0x04000000); + } + if (feature == kSSE3) { + return 0 != (cpu_info[2] & 0x00000001); + } +#if defined(WEBRTC_ENABLE_AVX2) + if (feature == kAVX2) { + int cpu_info7[4]; + __cpuid(cpu_info7, 0); + int num_ids = cpu_info7[0]; + if (num_ids < 7) { + return 0; + } + // Interpret CPU feature information. + __cpuid(cpu_info7, 7); + + // AVX instructions can be used when + // a) AVX are supported by the CPU, + // b) XSAVE is supported by the CPU, + // c) XSAVE is enabled by the kernel. + // Compiling with MSVC and /arch:AVX2 surprisingly generates BMI2 + // instructions (see crbug.com/1315519). + return (cpu_info[2] & 0x10000000) != 0 /* AVX */ && + (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ && + (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ && + (xgetbv(0) & 0x00000006) == 6 /* XSAVE enabled by kernel */ && + (cpu_info7[1] & 0x00000020) != 0 /* AVX2 */ && + (cpu_info7[1] & 0x00000100) != 0 /* BMI2 */; + } +#endif // WEBRTC_ENABLE_AVX2 + if (feature == kFMA3) { + return 0 != (cpu_info[2] & 0x00001000); + } + return 0; +} +#else +// Default to straight C for other platforms. +int GetCPUInfo(CPUFeature feature) { + (void)feature; + return 0; +} +#endif + +} // namespace webrtc diff --git a/pkg/apm/webrtc/system_wrappers/source/cpu_features_android.cc b/pkg/apm/webrtc/system_wrappers/source/cpu_features_android.cc new file mode 100644 index 00000000..95cc609b --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/source/cpu_features_android.cc @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +namespace webrtc { + +uint64_t GetCPUFeaturesARM(void) { + return android_getCpuFeatures(); +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/system_wrappers/source/cpu_features_linux.cc b/pkg/apm/webrtc/system_wrappers/source/cpu_features_linux.cc new file mode 100644 index 00000000..2d79dde1 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/source/cpu_features_linux.cc @@ -0,0 +1,97 @@ +/* + * 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 +#include +#include + +#ifdef __GLIBC_PREREQ +#define WEBRTC_GLIBC_PREREQ(a, b) __GLIBC_PREREQ(a, b) +#else +#define WEBRTC_GLIBC_PREREQ(a, b) 0 +#endif + +#if WEBRTC_GLIBC_PREREQ(2, 16) +#include +#else +#include +#include +#include +#include +#endif + +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" + +#if defined(WEBRTC_ARCH_ARM_FAMILY) +#include + +namespace webrtc { + +uint64_t GetCPUFeaturesARM(void) { + uint64_t result = 0; + int architecture = 0; + uint64_t hwcap = 0; + const char* platform = NULL; +#if WEBRTC_GLIBC_PREREQ(2, 16) + hwcap = getauxval(AT_HWCAP); + platform = (const char*)getauxval(AT_PLATFORM); +#else + ElfW(auxv_t) auxv; + int fd = open("/proc/self/auxv", O_RDONLY); + if (fd >= 0) { + while (hwcap == 0 || platform == NULL) { + if (read(fd, &auxv, sizeof(auxv)) < (ssize_t)sizeof(auxv)) { + if (errno == EINTR) + continue; + break; + } + switch (auxv.a_type) { + case AT_HWCAP: + hwcap = auxv.a_un.a_val; + break; + case AT_PLATFORM: + platform = (const char*)auxv.a_un.a_val; + break; + } + } + close(fd); + } +#endif // WEBRTC_GLIBC_PREREQ(2, 16) +#if defined(__aarch64__) + (void)platform; + architecture = 8; + if ((hwcap & HWCAP_FP) != 0) + result |= kCPUFeatureVFPv3; + if ((hwcap & HWCAP_ASIMD) != 0) + result |= kCPUFeatureNEON; +#else + if (platform != NULL) { + /* expect a string in the form "v6l" or "v7l", etc. + */ + if (platform[0] == 'v' && '0' <= platform[1] && platform[1] <= '9' && + (platform[2] == 'l' || platform[2] == 'b')) { + architecture = platform[1] - '0'; + } + } + if ((hwcap & HWCAP_VFPv3) != 0) + result |= kCPUFeatureVFPv3; + if ((hwcap & HWCAP_NEON) != 0) + result |= kCPUFeatureNEON; +#endif + if (architecture >= 7) + result |= kCPUFeatureARMv7; + if (architecture >= 6) + result |= kCPUFeatureLDREXSTREX; + return result; +} + +} // namespace webrtc +#endif // WEBRTC_ARCH_ARM_FAMILY diff --git a/pkg/apm/webrtc/system_wrappers/source/cpu_info.cc b/pkg/apm/webrtc/system_wrappers/source/cpu_info.cc new file mode 100644 index 00000000..8f726532 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/source/cpu_info.cc @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/cpu_info.h" + +#include + +#if defined(WEBRTC_WIN) +#include +#elif defined(WEBRTC_LINUX) +#include +#elif defined(WEBRTC_MAC) +#include +#elif defined(WEBRTC_FUCHSIA) +#include +#endif + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace internal { +static int DetectNumberOfCores() { + int number_of_cores; + +#if defined(WEBRTC_WIN) + SYSTEM_INFO si; + GetNativeSystemInfo(&si); + number_of_cores = static_cast(si.dwNumberOfProcessors); +#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID) + number_of_cores = static_cast(sysconf(_SC_NPROCESSORS_ONLN)); + if (number_of_cores <= 0) { + RTC_LOG(LS_ERROR) << "Failed to get number of cores"; + number_of_cores = 1; + } +#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS) + int name[] = {CTL_HW, HW_AVAILCPU}; + size_t size = sizeof(number_of_cores); + if (0 != sysctl(name, 2, &number_of_cores, &size, NULL, 0)) { + RTC_LOG(LS_ERROR) << "Failed to get number of cores"; + number_of_cores = 1; + } +#elif defined(WEBRTC_FUCHSIA) + number_of_cores = zx_system_get_num_cpus(); +#else + RTC_LOG(LS_ERROR) << "No function to get number of cores"; + number_of_cores = 1; +#endif + + RTC_LOG(LS_INFO) << "Available number of cores: " << number_of_cores; + + RTC_CHECK_GT(number_of_cores, 0); + return number_of_cores; +} +} // namespace internal + +namespace webrtc { + +uint32_t CpuInfo::DetectNumberOfCores() { + // Statically cache the number of system cores available since if the process + // is running in a sandbox, we may only be able to read the value once (before + // the sandbox is initialized) and not thereafter. + // For more information see crbug.com/176522. + static const uint32_t logical_cpus = + static_cast(::internal::DetectNumberOfCores()); + return logical_cpus; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/system_wrappers/source/denormal_disabler.cc b/pkg/apm/webrtc/system_wrappers/source/denormal_disabler.cc new file mode 100644 index 00000000..bb9c0564 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/source/denormal_disabler.cc @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/denormal_disabler.h" + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +#if defined(WEBRTC_ARCH_X86_FAMILY) && defined(__clang__) +#define WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED +#endif + +#if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) || \ + defined(WEBRTC_ARCH_ARM_FAMILY) +#define WEBRTC_DENORMAL_DISABLER_SUPPORTED +#endif + +constexpr int kUnspecifiedStatusWord = -1; + +#if defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED) + +// Control register bit mask to disable denormals on the hardware. +#if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) +// On x86 two bits are used: flush-to-zero (FTZ) and denormals-are-zero (DAZ). +constexpr int kDenormalBitMask = 0x8040; +#elif defined(WEBRTC_ARCH_ARM_FAMILY) +// On ARM one bit is used: flush-to-zero (FTZ). +constexpr int kDenormalBitMask = 1 << 24; +#endif + +// Reads the relevant CPU control register and returns its value for supported +// architectures and compilers. Otherwise returns `kUnspecifiedStatusWord`. +int ReadStatusWord() { + int result = kUnspecifiedStatusWord; +#if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) + asm volatile("stmxcsr %0" : "=m"(result)); +#elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_32_BITS) + asm volatile("vmrs %[result], FPSCR" : [result] "=r"(result)); +#elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS) + asm volatile("mrs %x[result], FPCR" : [result] "=r"(result)); +#endif + return result; +} + +// Writes `status_word` in the relevant CPU control register if the architecture +// and the compiler are supported. +void SetStatusWord(int status_word) { +#if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) + asm volatile("ldmxcsr %0" : : "m"(status_word)); +#elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_32_BITS) + asm volatile("vmsr FPSCR, %[src]" : : [src] "r"(status_word)); +#elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS) + asm volatile("msr FPCR, %x[src]" : : [src] "r"(status_word)); +#endif +} + +// Returns true if the status word indicates that denormals are enabled. +constexpr bool DenormalsEnabled(int status_word) { + return (status_word & kDenormalBitMask) != kDenormalBitMask; +} + +#endif // defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED) + +} // namespace + +#if defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED) +DenormalDisabler::DenormalDisabler() : DenormalDisabler(/*enabled=*/true) {} + +DenormalDisabler::DenormalDisabler(bool enabled) + : status_word_(enabled ? ReadStatusWord() : kUnspecifiedStatusWord), + disabling_activated_(enabled && DenormalsEnabled(status_word_)) { + if (disabling_activated_) { + RTC_DCHECK_NE(status_word_, kUnspecifiedStatusWord); + SetStatusWord(status_word_ | kDenormalBitMask); + RTC_DCHECK(!DenormalsEnabled(ReadStatusWord())); + } +} + +bool DenormalDisabler::IsSupported() { + return true; +} + +DenormalDisabler::~DenormalDisabler() { + if (disabling_activated_) { + RTC_DCHECK_NE(status_word_, kUnspecifiedStatusWord); + SetStatusWord(status_word_); + } +} +#else +DenormalDisabler::DenormalDisabler() : DenormalDisabler(/*enabled=*/false) {} + +DenormalDisabler::DenormalDisabler(bool enabled) + : status_word_(kUnspecifiedStatusWord), disabling_activated_(false) {} + +bool DenormalDisabler::IsSupported() { + return false; +} + +DenormalDisabler::~DenormalDisabler() = default; +#endif + +} // namespace webrtc diff --git a/pkg/apm/webrtc/system_wrappers/source/field_trial.cc b/pkg/apm/webrtc/system_wrappers/source/field_trial.cc new file mode 100644 index 00000000..cb4544e5 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/source/field_trial.cc @@ -0,0 +1,208 @@ +// 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 + +#include +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "experiments/registered_field_trials.h" +#include "rtc_base/checks.h" +#include "rtc_base/containers/flat_set.h" +#include "rtc_base/logging.h" +#include "rtc_base/string_encode.h" +#include "system_wrappers/include/field_trial.h" + +// Simple field trial implementation, which allows client to +// specify desired flags in InitFieldTrialsFromString. +namespace webrtc { +namespace field_trial { + +namespace { + +constexpr char kPersistentStringSeparator = '/'; + +struct TrialsState { + absl::Mutex mutex; + std::vector> storage ABSL_GUARDED_BY(mutex); + const char* trials_init_string ABSL_GUARDED_BY(mutex) = NULL; +}; + +TrialsState& GetTrialsState() { + static auto* state = new TrialsState(); + return *state; +} + +flat_set& TestKeys() { + static auto* test_keys = new flat_set(); + return *test_keys; +} + +// Validates the given field trial string. +// E.g.: +// "WebRTC-experimentFoo/Enabled/WebRTC-experimentBar/Enabled100kbps/" +// Assigns the process to group "Enabled" on WebRTCExperimentFoo trial +// and to group "Enabled100kbps" on WebRTCExperimentBar. +// +// E.g. invalid config: +// "WebRTC-experiment1/Enabled" (note missing / separator at the end). +bool FieldTrialsStringIsValidInternal(const absl::string_view trials) { + if (trials.empty()) return true; + + size_t next_item = 0; + std::map field_trials; + while (next_item < trials.length()) { + size_t name_end = trials.find(kPersistentStringSeparator, next_item); + if (name_end == trials.npos || next_item == name_end) return false; + size_t group_name_end = + trials.find(kPersistentStringSeparator, name_end + 1); + if (group_name_end == trials.npos || name_end + 1 == group_name_end) + return false; + absl::string_view name = trials.substr(next_item, name_end - next_item); + absl::string_view group_name = + trials.substr(name_end + 1, group_name_end - name_end - 1); + + next_item = group_name_end + 1; + + // Fail if duplicate with different group name. + if (field_trials.find(name) != field_trials.end() && + field_trials.find(name)->second != group_name) { + return false; + } + + field_trials[name] = group_name; + } + + return true; +} + +} // namespace + +bool FieldTrialsStringIsValid(absl::string_view trials_string) { + return FieldTrialsStringIsValidInternal(trials_string); +} + +void InsertOrReplaceFieldTrialStringsInMap( + std::map* fieldtrial_map, + const absl::string_view trials_string) { + if (FieldTrialsStringIsValidInternal(trials_string)) { + std::vector tokens = split(trials_string, '/'); + // Skip last token which is empty due to trailing '/'. + for (size_t idx = 0; idx < tokens.size() - 1; idx += 2) { + (*fieldtrial_map)[std::string(tokens[idx])] = + std::string(tokens[idx + 1]); + } + } else { + RTC_DCHECK_NOTREACHED() << "Invalid field trials string:" << trials_string; + } +} + +std::string MergeFieldTrialsStrings(absl::string_view first, + absl::string_view second) { + std::map fieldtrial_map; + InsertOrReplaceFieldTrialStringsInMap(&fieldtrial_map, first); + InsertOrReplaceFieldTrialStringsInMap(&fieldtrial_map, second); + + // Merge into fieldtrial string. + std::string merged = ""; + for (auto const& fieldtrial : fieldtrial_map) { + merged += fieldtrial.first + '/' + fieldtrial.second + '/'; + } + return merged; +} + +#ifndef WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT +std::string FindFullName(absl::string_view name) { +#if WEBRTC_STRICT_FIELD_TRIALS == 1 + RTC_DCHECK(absl::c_linear_search(kRegisteredFieldTrials, name) || + TestKeys().contains(name)) + << name << " is not registered, see g3doc/field-trials.md."; +#elif WEBRTC_STRICT_FIELD_TRIALS == 2 + RTC_LOG_IF(LS_WARNING, + !(absl::c_linear_search(kRegisteredFieldTrials, name) || + TestKeys().contains(name))) + << name << " is not registered, see g3doc/field-trials.md."; +#endif + + const char* trials_string_ptr = NULL; + { + TrialsState& state = GetTrialsState(); + absl::MutexLock lock(&state.mutex); + trials_string_ptr = state.trials_init_string; + } + if (trials_string_ptr == NULL) return std::string(); + + absl::string_view trials_string(trials_string_ptr); + if (trials_string.empty()) return std::string(); + + size_t next_item = 0; + while (next_item < trials_string.length()) { + // Find next name/value pair in field trial configuration string. + size_t field_name_end = + trials_string.find(kPersistentStringSeparator, next_item); + if (field_name_end == trials_string.npos || field_name_end == next_item) + break; + size_t field_value_end = + trials_string.find(kPersistentStringSeparator, field_name_end + 1); + if (field_value_end == trials_string.npos || + field_value_end == field_name_end + 1) + break; + absl::string_view field_name = + trials_string.substr(next_item, field_name_end - next_item); + absl::string_view field_value = trials_string.substr( + field_name_end + 1, field_value_end - field_name_end - 1); + next_item = field_value_end + 1; + + if (name == field_name) return std::string(field_value); + } + return std::string(); +} +#endif // WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT + +// Optionally initialize field trial from a string. +void InitFieldTrialsFromString(const char* trials_string) { + RTC_LOG(LS_INFO) << "Setting field trial string:" << trials_string; + TrialsState& state = GetTrialsState(); + absl::MutexLock lock(&state.mutex); + if (trials_string) { + RTC_DCHECK(FieldTrialsStringIsValidInternal(trials_string)) + << "Invalid field trials string:" << trials_string; + + // Persistent storage to ensure pointers remain valid for concurrent + // readers. We never remove strings from here to avoid use-after-free races. + state.storage.push_back(std::make_unique(trials_string)); + state.trials_init_string = state.storage.back()->c_str(); + } else { + state.trials_init_string = NULL; + } +} + +const char* GetFieldTrialString() { + TrialsState& state = GetTrialsState(); + absl::MutexLock lock(&state.mutex); + return state.trials_init_string; +} + +FieldTrialsAllowedInScopeForTesting::FieldTrialsAllowedInScopeForTesting( + flat_set keys) { + TestKeys() = std::move(keys); +} + +FieldTrialsAllowedInScopeForTesting::~FieldTrialsAllowedInScopeForTesting() { + TestKeys().clear(); +} + +} // namespace field_trial +} // namespace webrtc diff --git a/pkg/apm/webrtc/system_wrappers/source/metrics.cc b/pkg/apm/webrtc/system_wrappers/source/metrics.cc new file mode 100644 index 00000000..bf39770f --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/source/metrics.cc @@ -0,0 +1,338 @@ +// 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 "system_wrappers/include/metrics.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/string_utils.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" + +// Default implementation of histogram methods for WebRTC clients that do not +// want to provide their own implementation. + +namespace webrtc { +namespace metrics { +class Histogram; + +namespace { +// Limit for the maximum number of sample values that can be stored. +// TODO(asapersson): Consider using bucket count (and set up +// linearly/exponentially spaced buckets) if samples are logged more frequently. +const int kMaxSampleMapSize = 300; + +class RtcHistogram { + public: + RtcHistogram(absl::string_view name, int min, int max, int bucket_count) + : min_(min), max_(max), info_(name, min, max, bucket_count) { + RTC_DCHECK_GT(bucket_count, 0); + } + + RtcHistogram(const RtcHistogram&) = delete; + RtcHistogram& operator=(const RtcHistogram&) = delete; + + void Add(int sample) { + sample = std::min(sample, max_); + sample = std::max(sample, min_ - 1); // Underflow bucket. + + MutexLock lock(&mutex_); + if (info_.samples.size() == kMaxSampleMapSize && + info_.samples.find(sample) == info_.samples.end()) { + return; + } + ++info_.samples[sample]; + } + + // Returns a copy (or nullptr if there are no samples) and clears samples. + std::unique_ptr GetAndReset() { + MutexLock lock(&mutex_); + if (info_.samples.empty()) + return nullptr; + + SampleInfo* copy = + new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count); + + std::swap(info_.samples, copy->samples); + + return std::unique_ptr(copy); + } + + const std::string& name() const { return info_.name; } + + // Functions only for testing. + void Reset() { + MutexLock lock(&mutex_); + info_.samples.clear(); + } + + int NumEvents(int sample) const { + MutexLock lock(&mutex_); + const auto it = info_.samples.find(sample); + return (it == info_.samples.end()) ? 0 : it->second; + } + + int NumSamples() const { + int num_samples = 0; + MutexLock lock(&mutex_); + for (const auto& sample : info_.samples) { + num_samples += sample.second; + } + return num_samples; + } + + int MinSample() const { + MutexLock lock(&mutex_); + return (info_.samples.empty()) ? -1 : info_.samples.begin()->first; + } + + std::map Samples() const { + MutexLock lock(&mutex_); + return info_.samples; + } + + private: + mutable Mutex mutex_; + const int min_; + const int max_; + SampleInfo info_ RTC_GUARDED_BY(mutex_); +}; + +class RtcHistogramMap { + public: + RtcHistogramMap() {} + ~RtcHistogramMap() {} + + RtcHistogramMap(const RtcHistogramMap&) = delete; + RtcHistogramMap& operator=(const RtcHistogramMap&) = delete; + + Histogram* GetCountsHistogram(absl::string_view name, + int min, + int max, + int bucket_count) { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + if (it != map_.end()) + return reinterpret_cast(it->second.get()); + + RtcHistogram* hist = new RtcHistogram(name, min, max, bucket_count); + map_.emplace(name, hist); + return reinterpret_cast(hist); + } + + Histogram* GetEnumerationHistogram(absl::string_view name, int boundary) { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + if (it != map_.end()) + return reinterpret_cast(it->second.get()); + + RtcHistogram* hist = new RtcHistogram(name, 1, boundary, boundary + 1); + map_.emplace(name, hist); + return reinterpret_cast(hist); + } + + void GetAndReset( + std::map, AbslStringViewCmp>* + histograms) { + MutexLock lock(&mutex_); + for (const auto& kv : map_) { + std::unique_ptr info = kv.second->GetAndReset(); + if (info) + histograms->insert(std::make_pair(kv.first, std::move(info))); + } + } + + // Functions only for testing. + void Reset() { + MutexLock lock(&mutex_); + for (const auto& kv : map_) + kv.second->Reset(); + } + + int NumEvents(absl::string_view name, int sample) const { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + return (it == map_.end()) ? 0 : it->second->NumEvents(sample); + } + + int NumSamples(absl::string_view name) const { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + return (it == map_.end()) ? 0 : it->second->NumSamples(); + } + + int MinSample(absl::string_view name) const { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + return (it == map_.end()) ? -1 : it->second->MinSample(); + } + + std::map Samples(absl::string_view name) const { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + return (it == map_.end()) ? std::map() : it->second->Samples(); + } + + private: + mutable Mutex mutex_; + std::map, AbslStringViewCmp> map_ + RTC_GUARDED_BY(mutex_); +}; + +// RtcHistogramMap is allocated upon call to Enable(). +// The histogram getter functions, which return pointer values to the histograms +// in the map, are cached in WebRTC. Therefore, this memory is not freed by the +// application (the memory will be reclaimed by the OS). +static std::atomic g_rtc_histogram_map(nullptr); + +void CreateMap() { + RtcHistogramMap* map = g_rtc_histogram_map.load(std::memory_order_acquire); + if (map == nullptr) { + RtcHistogramMap* new_map = new RtcHistogramMap(); + if (!g_rtc_histogram_map.compare_exchange_strong(map, new_map)) + delete new_map; + } +} + +// Set the first time we start using histograms. Used to make sure Enable() is +// not called thereafter. +#if RTC_DCHECK_IS_ON +static std::atomic g_rtc_histogram_called(0); +#endif + +// Gets the map (or nullptr). +RtcHistogramMap* GetMap() { +#if RTC_DCHECK_IS_ON + g_rtc_histogram_called.store(1, std::memory_order_release); +#endif + return g_rtc_histogram_map.load(); +} +} // namespace + +#ifndef WEBRTC_EXCLUDE_METRICS_DEFAULT +// Implementation of histogram methods in +// webrtc/system_wrappers/interface/metrics.h. + +// Histogram with exponentially spaced buckets. +// Creates (or finds) histogram. +// The returned histogram pointer is cached (and used for adding samples in +// subsequent calls). +Histogram* HistogramFactoryGetCounts(absl::string_view name, + int min, + int max, + int bucket_count) { + // TODO(asapersson): Alternative implementation will be needed if this + // histogram type should be truly exponential. + return HistogramFactoryGetCountsLinear(name, min, max, bucket_count); +} + +// Histogram with linearly spaced buckets. +// Creates (or finds) histogram. +// The returned histogram pointer is cached (and used for adding samples in +// subsequent calls). +Histogram* HistogramFactoryGetCountsLinear(absl::string_view name, + int min, + int max, + int bucket_count) { + RtcHistogramMap* map = GetMap(); + if (!map) + return nullptr; + + return map->GetCountsHistogram(name, min, max, bucket_count); +} + +// Histogram with linearly spaced buckets. +// Creates (or finds) histogram. +// The returned histogram pointer is cached (and used for adding samples in +// subsequent calls). +Histogram* HistogramFactoryGetEnumeration(absl::string_view name, + int boundary) { + RtcHistogramMap* map = GetMap(); + if (!map) + return nullptr; + + return map->GetEnumerationHistogram(name, boundary); +} + +// Our default implementation reuses the non-sparse histogram. +Histogram* SparseHistogramFactoryGetEnumeration(absl::string_view name, + int boundary) { + return HistogramFactoryGetEnumeration(name, boundary); +} + +// Fast path. Adds `sample` to cached `histogram_pointer`. +void HistogramAdd(Histogram* histogram_pointer, int sample) { + RtcHistogram* ptr = reinterpret_cast(histogram_pointer); + ptr->Add(sample); +} + +#endif // WEBRTC_EXCLUDE_METRICS_DEFAULT + +SampleInfo::SampleInfo(absl::string_view name, + int min, + int max, + size_t bucket_count) + : name(name), min(min), max(max), bucket_count(bucket_count) {} + +SampleInfo::~SampleInfo() {} + +// Implementation of global functions in metrics.h. +void Enable() { + RTC_DCHECK(g_rtc_histogram_map.load() == nullptr); +#if RTC_DCHECK_IS_ON + RTC_DCHECK_EQ(0, g_rtc_histogram_called.load(std::memory_order_acquire)); +#endif + CreateMap(); +} + +void GetAndReset( + std::map, AbslStringViewCmp>* + histograms) { + histograms->clear(); + RtcHistogramMap* map = GetMap(); + if (map) + map->GetAndReset(histograms); +} + +void Reset() { + RtcHistogramMap* map = GetMap(); + if (map) + map->Reset(); +} + +int NumEvents(absl::string_view name, int sample) { + RtcHistogramMap* map = GetMap(); + return map ? map->NumEvents(name, sample) : 0; +} + +int NumSamples(absl::string_view name) { + RtcHistogramMap* map = GetMap(); + return map ? map->NumSamples(name) : 0; +} + +int MinSample(absl::string_view name) { + RtcHistogramMap* map = GetMap(); + return map ? map->MinSample(name) : -1; +} + +std::map Samples(absl::string_view name) { + RtcHistogramMap* map = GetMap(); + return map ? map->Samples(name) : std::map(); +} + +} // namespace metrics +} // namespace webrtc diff --git a/pkg/apm/webrtc/system_wrappers/source/rtp_to_ntp_estimator.cc b/pkg/apm/webrtc/system_wrappers/source/rtp_to_ntp_estimator.cc new file mode 100644 index 00000000..549967c0 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/source/rtp_to_ntp_estimator.cc @@ -0,0 +1,153 @@ +/* + * 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 "system_wrappers/include/rtp_to_ntp_estimator.h" + +#include + +#include +#include + +#include "api/array_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { +namespace { +// Maximum number of RTCP SR reports to use to map between RTP and NTP. +constexpr size_t kNumRtcpReportsToUse = 20; +// Don't allow NTP timestamps to jump more than 1 hour. Chosen arbitrary as big +// enough to not affect normal use-cases. Yet it is smaller than RTP wrap-around +// half-period (90khz RTP clock wrap-arounds every 13.25 hours). After half of +// wrap-around period it is impossible to unwrap RTP timestamps correctly. +constexpr uint64_t kMaxAllowedRtcpNtpInterval = uint64_t{60 * 60} << 32; +} // namespace + +void RtpToNtpEstimator::UpdateParameters() { + size_t n = measurements_.size(); + if (n < 2) + return; + + // Run linear regression: + // Given x[] and y[] writes out such k and b that line y=k*x+b approximates + // given points in the best way (Least Squares Method). + auto x = [](const RtcpMeasurement& m) { + return static_cast(m.unwrapped_rtp_timestamp); + }; + auto y = [](const RtcpMeasurement& m) { + return static_cast(static_cast(m.ntp_time)); + }; + + double avg_x = 0; + double avg_y = 0; + for (const RtcpMeasurement& m : measurements_) { + avg_x += x(m); + avg_y += y(m); + } + avg_x /= n; + avg_y /= n; + + double variance_x = 0; + double covariance_xy = 0; + for (const RtcpMeasurement& m : measurements_) { + double normalized_x = x(m) - avg_x; + double normalized_y = y(m) - avg_y; + variance_x += normalized_x * normalized_x; + covariance_xy += normalized_x * normalized_y; + } + + if (std::fabs(variance_x) < 1e-8) + return; + + double k = covariance_xy / variance_x; + double b = avg_y - k * avg_x; + params_ = {{.slope = k, .offset = b}}; +} + +RtpToNtpEstimator::UpdateResult RtpToNtpEstimator::UpdateMeasurements( + NtpTime ntp, + uint32_t rtp_timestamp) { + int64_t unwrapped_rtp_timestamp = unwrapper_.Unwrap(rtp_timestamp); + + RtcpMeasurement new_measurement = { + .ntp_time = ntp, .unwrapped_rtp_timestamp = unwrapped_rtp_timestamp}; + + for (const RtcpMeasurement& measurement : measurements_) { + // Use || since two equal timestamps will result in zero frequency. + if (measurement.ntp_time == ntp || + measurement.unwrapped_rtp_timestamp == unwrapped_rtp_timestamp) { + return kSameMeasurement; + } + } + + if (!new_measurement.ntp_time.Valid()) + return kInvalidMeasurement; + + uint64_t ntp_new = static_cast(new_measurement.ntp_time); + bool invalid_sample = false; + if (!measurements_.empty()) { + int64_t old_rtp_timestamp = measurements_.front().unwrapped_rtp_timestamp; + uint64_t old_ntp = static_cast(measurements_.front().ntp_time); + if (ntp_new <= old_ntp || ntp_new > old_ntp + kMaxAllowedRtcpNtpInterval) { + invalid_sample = true; + } else if (unwrapped_rtp_timestamp <= old_rtp_timestamp) { + RTC_LOG(LS_WARNING) + << "Newer RTCP SR report with older RTP timestamp, dropping"; + invalid_sample = true; + } else if (unwrapped_rtp_timestamp - old_rtp_timestamp > (1 << 25)) { + // Sanity check. No jumps too far into the future in rtp. + invalid_sample = true; + } + } + + if (invalid_sample) { + ++consecutive_invalid_samples_; + if (consecutive_invalid_samples_ < kMaxInvalidSamples) { + return kInvalidMeasurement; + } + RTC_LOG(LS_WARNING) << "Multiple consecutively invalid RTCP SR reports, " + "clearing measurements."; + measurements_.clear(); + params_ = std::nullopt; + } + consecutive_invalid_samples_ = 0; + + // Insert new RTCP SR report. + if (measurements_.size() == kNumRtcpReportsToUse) + measurements_.pop_back(); + + measurements_.push_front(new_measurement); + + // List updated, calculate new parameters. + UpdateParameters(); + return kNewMeasurement; +} + +NtpTime RtpToNtpEstimator::Estimate(uint32_t rtp_timestamp) const { + if (!params_) + return NtpTime(); + + double estimated = + static_cast(unwrapper_.Unwrap(rtp_timestamp)) * params_->slope + + params_->offset + 0.5f; + + return NtpTime(saturated_cast(estimated)); +} + +double RtpToNtpEstimator::EstimatedFrequencyKhz() const { + if (!params_.has_value()) { + return 0.0; + } + static constexpr double kNtpUnitPerMs = 4.294967296E6; // 2^32 / 1000. + return kNtpUnitPerMs / params_->slope; +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/system_wrappers/source/sleep.cc b/pkg/apm/webrtc/system_wrappers/source/sleep.cc new file mode 100644 index 00000000..e2fa4861 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/source/sleep.cc @@ -0,0 +1,36 @@ +/* + * 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. + */ +// An OS-independent sleep function. + +#include "system_wrappers/include/sleep.h" + +#ifdef _WIN32 +// For Sleep() +#include +#else +// For nanosleep() +#include +#endif + +namespace webrtc { + +void SleepMs(int msecs) { +#ifdef _WIN32 + Sleep(msecs); +#else + struct timespec short_wait; + struct timespec remainder; + short_wait.tv_sec = msecs / 1000; + short_wait.tv_nsec = (msecs % 1000) * 1000 * 1000; + nanosleep(&short_wait, &remainder); +#endif +} + +} // namespace webrtc diff --git a/pkg/apm/webrtc/system_wrappers/source/system.go b/pkg/apm/webrtc/system_wrappers/source/system.go new file mode 100644 index 00000000..d4a53283 --- /dev/null +++ b/pkg/apm/webrtc/system_wrappers/source/system.go @@ -0,0 +1,10 @@ +//go:build console + +package source + +// #cgo CXXFLAGS: -I${SRCDIR}/../.. -I${SRCDIR}/../../third_party/abseil-cpp -std=c++17 -fno-rtti -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -DNDEBUG -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-deprecated-declarations -Wno-nullability-completeness -Wno-shorten-64-to-32 +// #cgo darwin CXXFLAGS: -DWEBRTC_MAC -DWEBRTC_POSIX +// #cgo linux CXXFLAGS: -DWEBRTC_LINUX -DWEBRTC_POSIX +// #cgo windows CXXFLAGS: -DWEBRTC_WIN +// #cgo arm64 CXXFLAGS: -DWEBRTC_HAS_NEON -DWEBRTC_ARCH_ARM64 +import "C" diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/algorithm/algorithm.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/algorithm/algorithm.h new file mode 100644 index 00000000..48f59504 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/algorithm/algorithm.h @@ -0,0 +1,64 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: algorithm.h +// ----------------------------------------------------------------------------- +// +// This header file contains Google extensions to the standard C++ +// header. + +#ifndef ABSL_ALGORITHM_ALGORITHM_H_ +#define ABSL_ALGORITHM_ALGORITHM_H_ + +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// equal() +// rotate() +// +// Historical note: Abseil once provided implementations of these algorithms +// prior to their adoption in C++14. New code should prefer to use the std +// variants. +// +// See the documentation for the STL header for more information: +// https://en.cppreference.com/w/cpp/header/algorithm +using std::equal; +using std::rotate; + +// linear_search() +// +// Performs a linear search for `value` using the iterator `first` up to +// but not including `last`, returning true if [`first`, `last`) contains an +// element equal to `value`. +// +// A linear search is of O(n) complexity which is guaranteed to make at most +// n = (`last` - `first`) comparisons. A linear search over short containers +// may be faster than a binary search, even when the container is sorted. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool linear_search( + InputIterator first, InputIterator last, const EqualityComparable& value) { + return std::find(first, last, value) != last; +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_ALGORITHM_ALGORITHM_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/algorithm/container.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/algorithm/container.h new file mode 100644 index 00000000..3193656f --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/algorithm/container.h @@ -0,0 +1,1830 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: container.h +// ----------------------------------------------------------------------------- +// +// This header file provides Container-based versions of algorithmic functions +// within the C++ standard library. The following standard library sets of +// functions are covered within this file: +// +// * Algorithmic functions +// * Algorithmic functions +// * functions +// +// The standard library functions operate on iterator ranges; the functions +// within this API operate on containers, though many return iterator ranges. +// +// All functions within this API are named with a `c_` prefix. Calls such as +// `absl::c_xx(container, ...) are equivalent to std:: functions such as +// `std::xx(std::begin(cont), std::end(cont), ...)`. Functions that act on +// iterators but not conceptually on iterator ranges (e.g. `std::iter_swap`) +// have no equivalent here. +// +// For template parameter and variable naming, `C` indicates the container type +// to which the function is applied, `Pred` indicates the predicate object type +// to be used by the function and `T` indicates the applicable element type. + +#ifndef ABSL_ALGORITHM_CONTAINER_H_ +#define ABSL_ALGORITHM_CONTAINER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/algorithm.h" +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/base/nullability.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_algorithm_internal { + +// NOTE: it is important to defer to ADL lookup for building with C++ modules, +// especially for headers like which are not visible from this file +// but specialize std::begin and std::end. +using std::begin; +using std::end; + +// The type of the iterator given by begin(c) (possibly std::begin(c)). +// ContainerIter> gives vector::const_iterator, +// while ContainerIter> gives vector::iterator. +template +using ContainerIter = decltype(begin(std::declval())); + +// An MSVC bug involving template parameter substitution requires us to use +// decltype() here instead of just std::pair. +template +using ContainerIterPairType = + decltype(std::make_pair(ContainerIter(), ContainerIter())); + +template +using ContainerDifferenceType = decltype(std::distance( + std::declval>(), std::declval>())); + +template +using ContainerPointerType = + typename std::iterator_traits>::pointer; + +// container_algorithm_internal::c_begin and +// container_algorithm_internal::c_end are abbreviations for proper ADL +// lookup of std::begin and std::end, i.e. +// using std::begin; +// using std::end; +// std::foo(begin(c), end(c)); +// becomes +// std::foo(container_algorithm_internal::c_begin(c), +// container_algorithm_internal::c_end(c)); +// These are meant for internal use only. + +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 ContainerIter c_begin(C& c) { + return begin(c); +} + +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 ContainerIter c_end(C& c) { + return end(c); +} + +template +struct IsUnorderedContainer : std::false_type {}; + +template +struct IsUnorderedContainer< + std::unordered_map> : std::true_type {}; + +template +struct IsUnorderedContainer> + : std::true_type {}; + +} // namespace container_algorithm_internal + +// PUBLIC API + +//------------------------------------------------------------------------------ +// Abseil algorithm.h functions +//------------------------------------------------------------------------------ + +// c_linear_search() +// +// Container-based version of absl::linear_search() for performing a linear +// search within a container. +// +// For a generalization that uses a predicate, see absl::c_any_of(). +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_linear_search( + const C& c, EqualityComparable&& value) { + return absl::linear_search(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(value)); +} + +//------------------------------------------------------------------------------ +// algorithms +//------------------------------------------------------------------------------ + +// c_distance() +// +// Container-based version of the `std::distance()` function to +// return the number of elements within a container. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 + container_algorithm_internal::ContainerDifferenceType + c_distance(const C& c) { + return std::distance(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +//------------------------------------------------------------------------------ +// Non-modifying sequence operations +//------------------------------------------------------------------------------ + +// c_all_of() +// +// Container-based version of the `std::all_of()` function to +// test if all elements within a container satisfy a condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_all_of(const C& c, Pred&& pred) { + return std::all_of(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_any_of() +// +// Container-based version of the `std::any_of()` function to +// test if any element in a container fulfills a condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_any_of(const C& c, Pred&& pred) { + return std::any_of(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_none_of() +// +// Container-based version of the `std::none_of()` function to +// test if no elements in a container fulfill a condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_none_of(const C& c, Pred&& pred) { + return std::none_of(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_for_each() +// +// Container-based version of the `std::for_each()` function to +// apply a function to a container's elements. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 decay_t c_for_each(C&& c, + Function&& f) { + return std::for_each(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(f)); +} + +// c_find() +// +// Container-based version of the `std::find()` function to find +// the first element containing the passed value within a container value. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_find(C& c, T&& value) { + return std::find(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(value)); +} + +// c_contains() +// +// Container-based version of the `std::ranges::contains()` C++23 +// function to search a container for a value. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_contains(const Sequence& sequence, + T&& value) { + return absl::c_find(sequence, std::forward(value)) != + container_algorithm_internal::c_end(sequence); +} + +// c_find_if() +// +// Container-based version of the `std::find_if()` function to find +// the first element in a container matching the given condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_find_if(C& c, Pred&& pred) { + return std::find_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_find_if_not() +// +// Container-based version of the `std::find_if_not()` function to +// find the first element in a container not matching the given condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_find_if_not(C& c, Pred&& pred) { + return std::find_if_not(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_find_end() +// +// Container-based version of the `std::find_end()` function to +// find the last subsequence within a container. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_find_end(Sequence1& sequence, Sequence2& subsequence) { + return std::find_end(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence)); +} + +// Overload of c_find_end() for using a predicate evaluation other than `==` as +// the function's test condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_find_end(Sequence1& sequence, Sequence2& subsequence, + BinaryPredicate&& pred) { + return std::find_end(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence), + std::forward(pred)); +} + +// c_find_first_of() +// +// Container-based version of the `std::find_first_of()` function to +// find the first element within the container that is also within the options +// container. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_find_first_of(C1& container, const C2& options) { + return std::find_first_of(container_algorithm_internal::c_begin(container), + container_algorithm_internal::c_end(container), + container_algorithm_internal::c_begin(options), + container_algorithm_internal::c_end(options)); +} + +// Overload of c_find_first_of() for using a predicate evaluation other than +// `==` as the function's test condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_find_first_of(C1& container, const C2& options, BinaryPredicate&& pred) { + return std::find_first_of(container_algorithm_internal::c_begin(container), + container_algorithm_internal::c_end(container), + container_algorithm_internal::c_begin(options), + container_algorithm_internal::c_end(options), + std::forward(pred)); +} + +// c_adjacent_find() +// +// Container-based version of the `std::adjacent_find()` function to +// find equal adjacent elements within a container. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_adjacent_find(Sequence& sequence) { + return std::adjacent_find(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_adjacent_find() for using a predicate evaluation other than +// `==` as the function's test condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_adjacent_find(Sequence& sequence, BinaryPredicate&& pred) { + return std::adjacent_find(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(pred)); +} + +// c_count() +// +// Container-based version of the `std::count()` function to count +// values that match within a container. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerDifferenceType + c_count(const C& c, T&& value) { + return std::count(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(value)); +} + +// c_count_if() +// +// Container-based version of the `std::count_if()` function to +// count values matching a condition within a container. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerDifferenceType + c_count_if(const C& c, Pred&& pred) { + return std::count_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_mismatch() +// +// Container-based version of the `std::mismatch()` function to +// return the first element where two ordered containers differ. Applies `==` to +// the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIterPairType + c_mismatch(C1& c1, C2& c2) { + return std::mismatch(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2)); +} + +// Overload of c_mismatch() for using a predicate evaluation other than `==` as +// the function's test condition. Applies `pred`to the first N elements of `c1` +// and `c2`, where N = min(size(c1), size(c2)). +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIterPairType + c_mismatch(C1& c1, C2& c2, BinaryPredicate pred) { + return std::mismatch(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), pred); +} + +// c_equal() +// +// Container-based version of the `std::equal()` function to +// test whether two containers are equal. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_equal(const C1& c1, const C2& c2) { + return std::equal(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2)); +} + +// Overload of c_equal() for using a predicate evaluation other than `==` as +// the function's test condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_equal(const C1& c1, const C2& c2, + BinaryPredicate&& pred) { + return std::equal(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), + std::forward(pred)); +} + +// c_is_permutation() +// +// Container-based version of the `std::is_permutation()` function +// to test whether a container is a permutation of another. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_is_permutation(const C1& c1, + const C2& c2) { + return std::is_permutation(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2)); +} + +// Overload of c_is_permutation() for using a predicate evaluation other than +// `==` as the function's test condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_is_permutation( + const C1& c1, const C2& c2, BinaryPredicate&& pred) { + return std::is_permutation(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), + std::forward(pred)); +} + +// c_search() +// +// Container-based version of the `std::search()` function to search +// a container for a subsequence. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_search(Sequence1& sequence, Sequence2& subsequence) { + return std::search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence)); +} + +// Overload of c_search() for using a predicate evaluation other than +// `==` as the function's test condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_search(Sequence1& sequence, Sequence2& subsequence, + BinaryPredicate&& pred) { + return std::search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence), + std::forward(pred)); +} + +// c_contains_subrange() +// +// Container-based version of the `std::ranges::contains_subrange()` +// C++23 function to search a container for a subsequence. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_contains_subrange( + Sequence1& sequence, Sequence2& subsequence) { + return absl::c_search(sequence, subsequence) != + container_algorithm_internal::c_end(sequence); +} + +// Overload of c_contains_subrange() for using a predicate evaluation other than +// `==` as the function's test condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 bool c_contains_subrange( + Sequence1& sequence, Sequence2& subsequence, BinaryPredicate&& pred) { + return absl::c_search(sequence, subsequence, + std::forward(pred)) != + container_algorithm_internal::c_end(sequence); +} + +// c_search_n() +// +// Container-based version of the `std::search_n()` function to +// search a container for the first sequence of N elements. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_search_n(Sequence& sequence, Size count, T&& value) { + return std::search_n(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), count, + std::forward(value)); +} + +// Overload of c_search_n() for using a predicate evaluation other than +// `==` as the function's test condition. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 + container_algorithm_internal::ContainerIter + c_search_n(Sequence& sequence, Size count, T&& value, + BinaryPredicate&& pred) { + return std::search_n(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), count, + std::forward(value), + std::forward(pred)); +} + +//------------------------------------------------------------------------------ +// Modifying sequence operations +//------------------------------------------------------------------------------ + +// c_copy() +// +// Container-based version of the `std::copy()` function to copy a +// container's elements into an iterator. +template +OutputIterator c_copy(const InputSequence& input, OutputIterator output) { + return std::copy(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), output); +} + +// c_copy_n() +// +// Container-based version of the `std::copy_n()` function to copy a +// container's first N elements into an iterator. +template +OutputIterator c_copy_n(const C& input, Size n, OutputIterator output) { + return std::copy_n(container_algorithm_internal::c_begin(input), n, output); +} + +// c_copy_if() +// +// Container-based version of the `std::copy_if()` function to copy +// a container's elements satisfying some condition into an iterator. +template +OutputIterator c_copy_if(const InputSequence& input, OutputIterator output, + Pred&& pred) { + return std::copy_if(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), output, + std::forward(pred)); +} + +// c_copy_backward() +// +// Container-based version of the `std::copy_backward()` function to +// copy a container's elements in reverse order into an iterator. +template +BidirectionalIterator c_copy_backward(const C& src, + BidirectionalIterator dest) { + return std::copy_backward(container_algorithm_internal::c_begin(src), + container_algorithm_internal::c_end(src), dest); +} + +// c_move() +// +// Container-based version of the `std::move()` function to move +// a container's elements into an iterator. +template +OutputIterator c_move(C&& src, OutputIterator dest) { + return std::move(container_algorithm_internal::c_begin(src), + container_algorithm_internal::c_end(src), dest); +} + +// c_move_backward() +// +// Container-based version of the `std::move_backward()` function to +// move a container's elements into an iterator in reverse order. +template +BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) { + return std::move_backward(container_algorithm_internal::c_begin(src), + container_algorithm_internal::c_end(src), dest); +} + +// c_swap_ranges() +// +// Container-based version of the `std::swap_ranges()` function to +// swap a container's elements with another container's elements. Swaps the +// first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). +template +container_algorithm_internal::ContainerIter c_swap_ranges(C1& c1, C2& c2) { + auto first1 = container_algorithm_internal::c_begin(c1); + auto last1 = container_algorithm_internal::c_end(c1); + auto first2 = container_algorithm_internal::c_begin(c2); + auto last2 = container_algorithm_internal::c_end(c2); + + using std::swap; + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) { + swap(*first1, *first2); + } + return first2; +} + +// c_transform() +// +// Container-based version of the `std::transform()` function to +// transform a container's elements using the unary operation, storing the +// result in an iterator pointing to the last transformed element in the output +// range. +template +OutputIterator c_transform(const InputSequence& input, OutputIterator output, + UnaryOp&& unary_op) { + return std::transform(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), output, + std::forward(unary_op)); +} + +// Overload of c_transform() for performing a transformation using a binary +// predicate. Applies `binary_op` to the first N elements of `c1` and `c2`, +// where N = min(size(c1), size(c2)). +template +OutputIterator c_transform(const InputSequence1& input1, + const InputSequence2& input2, OutputIterator output, + BinaryOp&& binary_op) { + auto first1 = container_algorithm_internal::c_begin(input1); + auto last1 = container_algorithm_internal::c_end(input1); + auto first2 = container_algorithm_internal::c_begin(input2); + auto last2 = container_algorithm_internal::c_end(input2); + for (; first1 != last1 && first2 != last2; + ++first1, (void)++first2, ++output) { + *output = binary_op(*first1, *first2); + } + + return output; +} + +// c_replace() +// +// Container-based version of the `std::replace()` function to +// replace a container's elements of some value with a new value. The container +// is modified in place. +template +void c_replace(Sequence& sequence, const T& old_value, const T& new_value) { + std::replace(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), old_value, + new_value); +} + +// c_replace_if() +// +// Container-based version of the `std::replace_if()` function to +// replace a container's elements of some value with a new value based on some +// condition. The container is modified in place. +template +void c_replace_if(C& c, Pred&& pred, T&& new_value) { + std::replace_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred), std::forward(new_value)); +} + +// c_replace_copy() +// +// Container-based version of the `std::replace_copy()` function to +// replace a container's elements of some value with a new value and return the +// results within an iterator. +template +OutputIterator c_replace_copy(const C& c, OutputIterator result, T&& old_value, + T&& new_value) { + return std::replace_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(old_value), + std::forward(new_value)); +} + +// c_replace_copy_if() +// +// Container-based version of the `std::replace_copy_if()` function +// to replace a container's elements of some value with a new value based on +// some condition, and return the results within an iterator. +template +OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred, + const T& new_value) { + return std::replace_copy_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(pred), new_value); +} + +// c_fill() +// +// Container-based version of the `std::fill()` function to fill a +// container with some value. +template +void c_fill(C& c, const T& value) { + std::fill(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), value); +} + +// c_fill_n() +// +// Container-based version of the `std::fill_n()` function to fill +// the first N elements in a container with some value. +template +void c_fill_n(C& c, Size n, const T& value) { + std::fill_n(container_algorithm_internal::c_begin(c), n, value); +} + +// c_generate() +// +// Container-based version of the `std::generate()` function to +// assign a container's elements to the values provided by the given generator. +template +void c_generate(C& c, Generator&& gen) { + std::generate(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(gen)); +} + +// c_generate_n() +// +// Container-based version of the `std::generate_n()` function to +// assign a container's first N elements to the values provided by the given +// generator. +template +container_algorithm_internal::ContainerIter c_generate_n(C& c, Size n, + Generator&& gen) { + return std::generate_n(container_algorithm_internal::c_begin(c), n, + std::forward(gen)); +} + +// Note: `c_xx()` container versions for `remove()`, `remove_if()`, +// and `unique()` are omitted, because it's not clear whether or not such +// functions should call erase on their supplied sequences afterwards. Either +// behavior would be surprising for a different set of users. + +// c_remove_copy() +// +// Container-based version of the `std::remove_copy()` function to +// copy a container's elements while removing any elements matching the given +// `value`. +template +OutputIterator c_remove_copy(const C& c, OutputIterator result, + const T& value) { + return std::remove_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + value); +} + +// c_remove_copy_if() +// +// Container-based version of the `std::remove_copy_if()` function +// to copy a container's elements while removing any elements matching the given +// condition. +template +OutputIterator c_remove_copy_if(const C& c, OutputIterator result, + Pred&& pred) { + return std::remove_copy_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(pred)); +} + +// c_unique_copy() +// +// Container-based version of the `std::unique_copy()` function to +// copy a container's elements while removing any elements containing duplicate +// values. +template +OutputIterator c_unique_copy(const C& c, OutputIterator result) { + return std::unique_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result); +} + +// Overload of c_unique_copy() for using a predicate evaluation other than +// `==` for comparing uniqueness of the element values. +template +OutputIterator c_unique_copy(const C& c, OutputIterator result, + BinaryPredicate&& pred) { + return std::unique_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(pred)); +} + +// c_reverse() +// +// Container-based version of the `std::reverse()` function to +// reverse a container's elements. +template +void c_reverse(Sequence& sequence) { + std::reverse(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// c_reverse_copy() +// +// Container-based version of the `std::reverse()` function to +// reverse a container's elements and write them to an iterator range. +template +OutputIterator c_reverse_copy(const C& sequence, OutputIterator result) { + return std::reverse_copy(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + result); +} + +// c_rotate() +// +// Container-based version of the `std::rotate()` function to +// shift a container's elements leftward such that the `middle` element becomes +// the first element in the container. +template > +Iterator c_rotate(C& sequence, Iterator middle) { + return absl::rotate(container_algorithm_internal::c_begin(sequence), middle, + container_algorithm_internal::c_end(sequence)); +} + +// c_rotate_copy() +// +// Container-based version of the `std::rotate_copy()` function to +// shift a container's elements leftward such that the `middle` element becomes +// the first element in a new iterator range. +template +OutputIterator c_rotate_copy( + const C& sequence, + container_algorithm_internal::ContainerIter middle, + OutputIterator result) { + return std::rotate_copy(container_algorithm_internal::c_begin(sequence), + middle, container_algorithm_internal::c_end(sequence), + result); +} + +// c_shuffle() +// +// Container-based version of the `std::shuffle()` function to +// randomly shuffle elements within the container using a `gen()` uniform random +// number generator. +template +void c_shuffle(RandomAccessContainer& c, UniformRandomBitGenerator&& gen) { + std::shuffle(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(gen)); +} + +// c_sample() +// +// Container-based version of the `std::sample()` function to +// randomly sample elements from the container without replacement using a +// `gen()` uniform random number generator and write them to an iterator range. +template +OutputIterator c_sample(const C& c, OutputIterator result, Distance n, + UniformRandomBitGenerator&& gen) { +#if defined(__cpp_lib_sample) && __cpp_lib_sample >= 201603L + return std::sample(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, n, + std::forward(gen)); +#else + // Fall back to a stable selection-sampling implementation. + auto first = container_algorithm_internal::c_begin(c); + Distance unsampled_elements = c_distance(c); + n = (std::min)(n, unsampled_elements); + for (; n != 0; ++first) { + Distance r = + std::uniform_int_distribution(0, --unsampled_elements)(gen); + if (r < n) { + *result++ = *first; + --n; + } + } + return result; +#endif +} + +//------------------------------------------------------------------------------ +// Partition functions +//------------------------------------------------------------------------------ + +// c_is_partitioned() +// +// Container-based version of the `std::is_partitioned()` function +// to test whether all elements in the container for which `pred` returns `true` +// precede those for which `pred` is `false`. +template +bool c_is_partitioned(const C& c, Pred&& pred) { + return std::is_partitioned(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_partition() +// +// Container-based version of the `std::partition()` function +// to rearrange all elements in a container in such a way that all elements for +// which `pred` returns `true` precede all those for which it returns `false`, +// returning an iterator to the first element of the second group. +template +container_algorithm_internal::ContainerIter c_partition(C& c, Pred&& pred) { + return std::partition(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_stable_partition() +// +// Container-based version of the `std::stable_partition()` function +// to rearrange all elements in a container in such a way that all elements for +// which `pred` returns `true` precede all those for which it returns `false`, +// preserving the relative ordering between the two groups. The function returns +// an iterator to the first element of the second group. +template +container_algorithm_internal::ContainerIter c_stable_partition(C& c, + Pred&& pred) { + return std::stable_partition(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_partition_copy() +// +// Container-based version of the `std::partition_copy()` function +// to partition a container's elements and return them into two iterators: one +// for which `pred` returns `true`, and one for which `pred` returns `false.` + +template +std::pair c_partition_copy( + const C& c, OutputIterator1 out_true, OutputIterator2 out_false, + Pred&& pred) { + return std::partition_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), out_true, + out_false, std::forward(pred)); +} + +// c_partition_point() +// +// Container-based version of the `std::partition_point()` function +// to return the first element of an already partitioned container for which +// the given `pred` is not `true`. +template +container_algorithm_internal::ContainerIter c_partition_point(C& c, + Pred&& pred) { + return std::partition_point(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +//------------------------------------------------------------------------------ +// Sorting functions +//------------------------------------------------------------------------------ + +// c_sort() +// +// Container-based version of the `std::sort()` function +// to sort elements in ascending order of their values. +template +void c_sort(C& c) { + std::sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_sort() for performing a `comp` comparison other than the +// default `operator<`. +template +void c_sort(C& c, LessThan&& comp) { + std::sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_stable_sort() +// +// Container-based version of the `std::stable_sort()` function +// to sort elements in ascending order of their values, preserving the order +// of equivalents. +template +void c_stable_sort(C& c) { + std::stable_sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_stable_sort() for performing a `comp` comparison other than the +// default `operator<`. +template +void c_stable_sort(C& c, LessThan&& comp) { + std::stable_sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_is_sorted() +// +// Container-based version of the `std::is_sorted()` function +// to evaluate whether the given container is sorted in ascending order. +template +bool c_is_sorted(const C& c) { + return std::is_sorted(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// c_is_sorted() overload for performing a `comp` comparison other than the +// default `operator<`. +template +bool c_is_sorted(const C& c, LessThan&& comp) { + return std::is_sorted(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_partial_sort() +// +// Container-based version of the `std::partial_sort()` function +// to rearrange elements within a container such that elements before `middle` +// are sorted in ascending order. +template +void c_partial_sort( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter middle) { + std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_partial_sort() for performing a `comp` comparison other than +// the default `operator<`. +template +void c_partial_sort( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter middle, + LessThan&& comp) { + std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_partial_sort_copy() +// +// Container-based version of the `std::partial_sort_copy()` +// function to sort the elements in the given range `result` within the larger +// `sequence` in ascending order (and using `result` as the output parameter). +// At most min(result.last - result.first, sequence.last - sequence.first) +// elements from the sequence will be stored in the result. +template +container_algorithm_internal::ContainerIter +c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { + return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(result), + container_algorithm_internal::c_end(result)); +} + +// Overload of c_partial_sort_copy() for performing a `comp` comparison other +// than the default `operator<`. +template +container_algorithm_internal::ContainerIter +c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, + LessThan&& comp) { + return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(result), + container_algorithm_internal::c_end(result), + std::forward(comp)); +} + +// c_is_sorted_until() +// +// Container-based version of the `std::is_sorted_until()` function +// to return the first element within a container that is not sorted in +// ascending order as an iterator. +template +container_algorithm_internal::ContainerIter c_is_sorted_until(C& c) { + return std::is_sorted_until(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_is_sorted_until() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIter c_is_sorted_until( + C& c, LessThan&& comp) { + return std::is_sorted_until(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_nth_element() +// +// Container-based version of the `std::nth_element()` function +// to rearrange the elements within a container such that the `nth` element +// would be in that position in an ordered sequence; other elements may be in +// any order, except that all preceding `nth` will be less than that element, +// and all following `nth` will be greater than that element. +template +void c_nth_element( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter nth) { + std::nth_element(container_algorithm_internal::c_begin(sequence), nth, + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_nth_element() for performing a `comp` comparison other than +// the default `operator<`. +template +void c_nth_element( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter nth, + LessThan&& comp) { + std::nth_element(container_algorithm_internal::c_begin(sequence), nth, + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Binary Search +//------------------------------------------------------------------------------ + +// c_lower_bound() +// +// Container-based version of the `std::lower_bound()` function +// to return an iterator pointing to the first element in a sorted container +// which does not compare less than `value`. +template +container_algorithm_internal::ContainerIter c_lower_bound( + Sequence& sequence, const T& value) { + return std::lower_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), value); +} + +// Overload of c_lower_bound() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIter c_lower_bound( + Sequence& sequence, const T& value, LessThan&& comp) { + return std::lower_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), value, + std::forward(comp)); +} + +// c_upper_bound() +// +// Container-based version of the `std::upper_bound()` function +// to return an iterator pointing to the first element in a sorted container +// which is greater than `value`. +template +container_algorithm_internal::ContainerIter c_upper_bound( + Sequence& sequence, const T& value) { + return std::upper_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), value); +} + +// Overload of c_upper_bound() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIter c_upper_bound( + Sequence& sequence, const T& value, LessThan&& comp) { + return std::upper_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), value, + std::forward(comp)); +} + +// c_equal_range() +// +// Container-based version of the `std::equal_range()` function +// to return an iterator pair pointing to the first and last elements in a +// sorted container which compare equal to `value`. +template +container_algorithm_internal::ContainerIterPairType +c_equal_range(Sequence& sequence, const T& value) { + return std::equal_range(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), value); +} + +// Overload of c_equal_range() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIterPairType +c_equal_range(Sequence& sequence, const T& value, LessThan&& comp) { + return std::equal_range(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), value, + std::forward(comp)); +} + +// c_binary_search() +// +// Container-based version of the `std::binary_search()` function +// to test if any element in the sorted container contains a value equivalent to +// 'value'. +template +bool c_binary_search(const Sequence& sequence, const T& value) { + return std::binary_search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + value); +} + +// Overload of c_binary_search() for performing a `comp` comparison other than +// the default `operator<`. +template +bool c_binary_search(const Sequence& sequence, const T& value, + LessThan&& comp) { + return std::binary_search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + value, std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Merge functions +//------------------------------------------------------------------------------ + +// c_merge() +// +// Container-based version of the `std::merge()` function +// to merge two sorted containers into a single sorted iterator. +template +OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) { + return std::merge(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), result); +} + +// Overload of c_merge() for performing a `comp` comparison other than +// the default `operator<`. +template +OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result, + LessThan&& comp) { + return std::merge(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), result, + std::forward(comp)); +} + +// c_inplace_merge() +// +// Container-based version of the `std::inplace_merge()` function +// to merge a supplied iterator `middle` into a container. +template +void c_inplace_merge(C& c, + container_algorithm_internal::ContainerIter middle) { + std::inplace_merge(container_algorithm_internal::c_begin(c), middle, + container_algorithm_internal::c_end(c)); +} + +// Overload of c_inplace_merge() for performing a merge using a `comp` other +// than `operator<`. +template +void c_inplace_merge(C& c, + container_algorithm_internal::ContainerIter middle, + LessThan&& comp) { + std::inplace_merge(container_algorithm_internal::c_begin(c), middle, + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_includes() +// +// Container-based version of the `std::includes()` function +// to test whether a sorted container `c1` entirely contains another sorted +// container `c2`. +template +bool c_includes(const C1& c1, const C2& c2) { + return std::includes(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2)); +} + +// Overload of c_includes() for performing a merge using a `comp` other than +// `operator<`. +template +bool c_includes(const C1& c1, const C2& c2, LessThan&& comp) { + return std::includes(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), + std::forward(comp)); +} + +// c_set_union() +// +// Container-based version of the `std::set_union()` function +// to return an iterator containing the union of two containers; duplicate +// values are not copied into the output. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) { + return std::set_union(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_union() for performing a merge using a `comp` other than +// `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, + LessThan&& comp) { + return std::set_union(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +// c_set_intersection() +// +// Container-based version of the `std::set_intersection()` function +// to return an iterator containing the intersection of two sorted containers. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_intersection(const C1& c1, const C2& c2, + OutputIterator output) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using operator<. + assert(absl::c_is_sorted(c1)); + assert(absl::c_is_sorted(c2)); + return std::set_intersection(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_intersection() for performing a merge using a `comp` other +// than `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_intersection(const C1& c1, const C2& c2, + OutputIterator output, LessThan&& comp) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using the same comparator. + assert(absl::c_is_sorted(c1, comp)); + assert(absl::c_is_sorted(c2, comp)); + return std::set_intersection(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +// c_set_difference() +// +// Container-based version of the `std::set_difference()` function +// to return an iterator containing elements present in the first container but +// not in the second. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_difference(const C1& c1, const C2& c2, + OutputIterator output) { + return std::set_difference(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_difference() for performing a merge using a `comp` other +// than `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_difference(const C1& c1, const C2& c2, + OutputIterator output, LessThan&& comp) { + return std::set_difference(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +// c_set_symmetric_difference() +// +// Container-based version of the `std::set_symmetric_difference()` +// function to return an iterator containing elements present in either one +// container or the other, but not both. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, + OutputIterator output) { + return std::set_symmetric_difference( + container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_symmetric_difference() for performing a merge using a +// `comp` other than `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, + OutputIterator output, + LessThan&& comp) { + return std::set_symmetric_difference( + container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Heap functions +//------------------------------------------------------------------------------ + +// c_push_heap() +// +// Container-based version of the `std::push_heap()` function +// to push a value onto a container heap. +template +void c_push_heap(RandomAccessContainer& sequence) { + std::push_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_push_heap() for performing a push operation on a heap using a +// `comp` other than `operator<`. +template +void c_push_heap(RandomAccessContainer& sequence, LessThan&& comp) { + std::push_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_pop_heap() +// +// Container-based version of the `std::pop_heap()` function +// to pop a value from a heap container. +template +void c_pop_heap(RandomAccessContainer& sequence) { + std::pop_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_pop_heap() for performing a pop operation on a heap using a +// `comp` other than `operator<`. +template +void c_pop_heap(RandomAccessContainer& sequence, LessThan&& comp) { + std::pop_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_make_heap() +// +// Container-based version of the `std::make_heap()` function +// to make a container a heap. +template +void c_make_heap(RandomAccessContainer& sequence) { + std::make_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_make_heap() for performing heap comparisons using a +// `comp` other than `operator<` +template +void c_make_heap(RandomAccessContainer& sequence, LessThan&& comp) { + std::make_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_sort_heap() +// +// Container-based version of the `std::sort_heap()` function +// to sort a heap into ascending order (after which it is no longer a heap). +template +void c_sort_heap(RandomAccessContainer& sequence) { + std::sort_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_sort_heap() for performing heap comparisons using a +// `comp` other than `operator<` +template +void c_sort_heap(RandomAccessContainer& sequence, LessThan&& comp) { + std::sort_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_is_heap() +// +// Container-based version of the `std::is_heap()` function +// to check whether the given container is a heap. +template +bool c_is_heap(const RandomAccessContainer& sequence) { + return std::is_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_is_heap() for performing heap comparisons using a +// `comp` other than `operator<` +template +bool c_is_heap(const RandomAccessContainer& sequence, LessThan&& comp) { + return std::is_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_is_heap_until() +// +// Container-based version of the `std::is_heap_until()` function +// to find the first element in a given container which is not in heap order. +template +container_algorithm_internal::ContainerIter +c_is_heap_until(RandomAccessContainer& sequence) { + return std::is_heap_until(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_is_heap_until() for performing heap comparisons using a +// `comp` other than `operator<` +template +container_algorithm_internal::ContainerIter +c_is_heap_until(RandomAccessContainer& sequence, LessThan&& comp) { + return std::is_heap_until(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Min/max +//------------------------------------------------------------------------------ + +// c_min_element() +// +// Container-based version of the `std::min_element()` function +// to return an iterator pointing to the element with the smallest value, using +// `operator<` to make the comparisons. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 + container_algorithm_internal::ContainerIter + c_min_element(Sequence& sequence) { + return std::min_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_min_element() for performing a `comp` comparison other than +// `operator<`. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 + container_algorithm_internal::ContainerIter + c_min_element(Sequence& sequence, LessThan&& comp) { + return std::min_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_max_element() +// +// Container-based version of the `std::max_element()` function +// to return an iterator pointing to the element with the largest value, using +// `operator<` to make the comparisons. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 + container_algorithm_internal::ContainerIter + c_max_element(Sequence& sequence) { + return std::max_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_max_element() for performing a `comp` comparison other than +// `operator<`. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 + container_algorithm_internal::ContainerIter + c_max_element(Sequence& sequence, LessThan&& comp) { + return std::max_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_minmax_element() +// +// Container-based version of the `std::minmax_element()` function +// to return a pair of iterators pointing to the elements containing the +// smallest and largest values, respectively, using `operator<` to make the +// comparisons. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 + container_algorithm_internal::ContainerIterPairType + c_minmax_element(C& c) { + return std::minmax_element(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_minmax_element() for performing `comp` comparisons other than +// `operator<`. +template +ABSL_INTERNAL_CONSTEXPR_SINCE_CXX17 + container_algorithm_internal::ContainerIterPairType + c_minmax_element(C& c, LessThan&& comp) { + return std::minmax_element(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Lexicographical Comparisons +//------------------------------------------------------------------------------ + +// c_lexicographical_compare() +// +// Container-based version of the `std::lexicographical_compare()` +// function to lexicographically compare (e.g. sort words alphabetically) two +// container sequences. The comparison is performed using `operator<`. Note +// that capital letters ("A-Z") have ASCII values less than lowercase letters +// ("a-z"). +template +bool c_lexicographical_compare(const Sequence1& sequence1, + const Sequence2& sequence2) { + return std::lexicographical_compare( + container_algorithm_internal::c_begin(sequence1), + container_algorithm_internal::c_end(sequence1), + container_algorithm_internal::c_begin(sequence2), + container_algorithm_internal::c_end(sequence2)); +} + +// Overload of c_lexicographical_compare() for performing a lexicographical +// comparison using a `comp` operator instead of `operator<`. +template +bool c_lexicographical_compare(const Sequence1& sequence1, + const Sequence2& sequence2, LessThan&& comp) { + return std::lexicographical_compare( + container_algorithm_internal::c_begin(sequence1), + container_algorithm_internal::c_end(sequence1), + container_algorithm_internal::c_begin(sequence2), + container_algorithm_internal::c_end(sequence2), + std::forward(comp)); +} + +// c_next_permutation() +// +// Container-based version of the `std::next_permutation()` function +// to rearrange a container's elements into the next lexicographically greater +// permutation. +template +bool c_next_permutation(C& c) { + return std::next_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_next_permutation() for performing a lexicographical +// comparison using a `comp` operator instead of `operator<`. +template +bool c_next_permutation(C& c, LessThan&& comp) { + return std::next_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_prev_permutation() +// +// Container-based version of the `std::prev_permutation()` function +// to rearrange a container's elements into the next lexicographically lesser +// permutation. +template +bool c_prev_permutation(C& c) { + return std::prev_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_prev_permutation() for performing a lexicographical +// comparison using a `comp` operator instead of `operator<`. +template +bool c_prev_permutation(C& c, LessThan&& comp) { + return std::prev_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// algorithms +//------------------------------------------------------------------------------ + +// c_iota() +// +// Container-based version of the `std::iota()` function +// to compute successive values of `value`, as if incremented with `++value` +// after each element is written, and write them to the container. +template +void c_iota(Sequence& sequence, const T& value) { + std::iota(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), value); +} + +// c_accumulate() +// +// Container-based version of the `std::accumulate()` function +// to accumulate the element values of a container to `init` and return that +// accumulation by value. +// +// Note: Due to a language technicality this function has return type +// absl::decay_t. As a user of this function you can casually read +// this as "returns T by value" and assume it does the right thing. +template +decay_t c_accumulate(const Sequence& sequence, T&& init) { + return std::accumulate(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(init)); +} + +// Overload of c_accumulate() for using a binary operations other than +// addition for computing the accumulation. +template +decay_t c_accumulate(const Sequence& sequence, T&& init, + BinaryOp&& binary_op) { + return std::accumulate(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(init), + std::forward(binary_op)); +} + +// c_inner_product() +// +// Container-based version of the `std::inner_product()` function +// to compute the cumulative inner product of container element pairs. +// +// Note: Due to a language technicality this function has return type +// absl::decay_t. As a user of this function you can casually read +// this as "returns T by value" and assume it does the right thing. +template +decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, + T&& sum) { + return std::inner_product(container_algorithm_internal::c_begin(factors1), + container_algorithm_internal::c_end(factors1), + container_algorithm_internal::c_begin(factors2), + std::forward(sum)); +} + +// Overload of c_inner_product() for using binary operations other than +// `operator+` (for computing the accumulation) and `operator*` (for computing +// the product between the two container's element pair). +template +decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, + T&& sum, BinaryOp1&& op1, BinaryOp2&& op2) { + return std::inner_product(container_algorithm_internal::c_begin(factors1), + container_algorithm_internal::c_end(factors1), + container_algorithm_internal::c_begin(factors2), + std::forward(sum), std::forward(op1), + std::forward(op2)); +} + +// c_adjacent_difference() +// +// Container-based version of the `std::adjacent_difference()` +// function to compute the difference between each element and the one preceding +// it and write it to an iterator. +template +OutputIt c_adjacent_difference(const InputSequence& input, + OutputIt output_first) { + return std::adjacent_difference(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first); +} + +// Overload of c_adjacent_difference() for using a binary operation other than +// subtraction to compute the adjacent difference. +template +OutputIt c_adjacent_difference(const InputSequence& input, + OutputIt output_first, BinaryOp&& op) { + return std::adjacent_difference(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first, std::forward(op)); +} + +// c_partial_sum() +// +// Container-based version of the `std::partial_sum()` function +// to compute the partial sum of the elements in a sequence and write them +// to an iterator. The partial sum is the sum of all element values so far in +// the sequence. +template +OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first) { + return std::partial_sum(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first); +} + +// Overload of c_partial_sum() for using a binary operation other than addition +// to compute the "partial sum". +template +OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first, + BinaryOp&& op) { + return std::partial_sum(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first, std::forward(op)); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_ALGORITHM_CONTAINER_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/base/attributes.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/base/attributes.h new file mode 100644 index 00000000..d009f6d4 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/base/attributes.h @@ -0,0 +1,1077 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This header file defines macros for declaring attributes for functions, +// types, and variables. +// +// These macros are used within Abseil and allow the compiler to optimize, where +// applicable, certain function calls. +// +// Most macros here are exposing GCC or Clang features, and are stubbed out for +// other compilers. +// +// GCC attributes documentation: +// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html +// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Variable-Attributes.html +// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Type-Attributes.html +// +// Most attributes in this file are already supported by GCC 4.7. However, some +// of them are not supported in older version of Clang. Thus, we check +// `__has_attribute()` first. If the check fails, we check if we are on GCC and +// assume the attribute exists on GCC (which is verified on GCC 4.7). + +// SKIP_ABSL_INLINE_NAMESPACE_CHECK + +#ifndef ABSL_BASE_ATTRIBUTES_H_ +#define ABSL_BASE_ATTRIBUTES_H_ + +#include "absl/base/config.h" + +// ABSL_HAVE_ATTRIBUTE +// +// A function-like feature checking macro that is a wrapper around +// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a +// nonzero constant integer if the attribute is supported or 0 if not. +// +// It evaluates to zero if `__has_attribute` is not defined by the compiler. +// +// GCC: https://gcc.gnu.org/gcc-5/changes.html +// Clang: https://clang.llvm.org/docs/LanguageExtensions.html +#ifdef __has_attribute +#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x) +#else +#define ABSL_HAVE_ATTRIBUTE(x) 0 +#endif + +// ABSL_HAVE_CPP_ATTRIBUTE +// +// A function-like feature checking macro that accepts C++11 style attributes. +// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6 +// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't +// find `__has_cpp_attribute`, will evaluate to 0. +#if defined(__cplusplus) && defined(__has_cpp_attribute) +// NOTE: requiring __cplusplus above should not be necessary, but +// works around https://bugs.llvm.org/show_bug.cgi?id=23435. +#define ABSL_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +#define ABSL_HAVE_CPP_ATTRIBUTE(x) 0 +#endif + +// ----------------------------------------------------------------------------- +// Function Attributes +// ----------------------------------------------------------------------------- +// +// GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html +// Clang: https://clang.llvm.org/docs/AttributeReference.html + +// ABSL_PRINTF_ATTRIBUTE +// ABSL_SCANF_ATTRIBUTE +// +// Tells the compiler to perform `printf` format string checking if the +// compiler supports it; see the 'format' attribute in +// . +// +// Note: As the GCC manual states, "[s]ince non-static C++ methods +// have an implicit 'this' argument, the arguments of such methods +// should be counted from two, not one." +#if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \ + __attribute__((__format__(__printf__, string_index, first_to_check))) +#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \ + __attribute__((__format__(__scanf__, string_index, first_to_check))) +#else +#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) +#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) +#endif + +// ABSL_ATTRIBUTE_ALWAYS_INLINE +// ABSL_ATTRIBUTE_NOINLINE +// +// Forces functions to either inline or not inline. Introduced in gcc 3.1. +#if ABSL_HAVE_ATTRIBUTE(always_inline) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) +#define ABSL_HAVE_ATTRIBUTE_ALWAYS_INLINE 1 +#else +#define ABSL_ATTRIBUTE_ALWAYS_INLINE +#endif + +#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_NOINLINE __attribute__((noinline)) +#define ABSL_HAVE_ATTRIBUTE_NOINLINE 1 +#else +#define ABSL_ATTRIBUTE_NOINLINE +#endif + +// ABSL_ATTRIBUTE_NO_TAIL_CALL +// +// Prevents the compiler from optimizing away stack frames for functions which +// end in a call to another function. +#if ABSL_HAVE_ATTRIBUTE(disable_tail_calls) +#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 +#define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls)) +#elif defined(__GNUC__) && !defined(__clang__) && !defined(__e2k__) +#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 +#define ABSL_ATTRIBUTE_NO_TAIL_CALL \ + __attribute__((optimize("no-optimize-sibling-calls"))) +#else +#define ABSL_ATTRIBUTE_NO_TAIL_CALL +#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 0 +#endif + +// ABSL_ATTRIBUTE_WEAK +// +// Tags a function as weak for the purposes of compilation and linking. +// Weak attributes did not work properly in LLVM's Windows backend before +// 9.0.0, so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 +// for further information. Weak attributes do not work across DLL boundary. +// The MinGW compiler doesn't complain about the weak attribute until the link +// step, presumably because Windows doesn't use ELF binaries. +#if (ABSL_HAVE_ATTRIBUTE(weak) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + (!defined(_WIN32) || \ + (defined(__clang__) && __clang_major__ >= 9 && \ + !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL))) && \ + !defined(__MINGW32__) +#undef ABSL_ATTRIBUTE_WEAK +#define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) +#define ABSL_HAVE_ATTRIBUTE_WEAK 1 +#else +#define ABSL_ATTRIBUTE_WEAK +#define ABSL_HAVE_ATTRIBUTE_WEAK 0 +#endif + +// ABSL_ATTRIBUTE_NONNULL +// +// Tells the compiler either (a) that a particular function parameter +// should be a non-null pointer, or (b) that all pointer arguments should +// be non-null. +// +// Note: As the GCC manual states, "[s]ince non-static C++ methods +// have an implicit 'this' argument, the arguments of such methods +// should be counted from two, not one." +// +// Args are indexed starting at 1. +// +// For non-static class member functions, the implicit `this` argument +// is arg 1, and the first explicit argument is arg 2. For static class member +// functions, there is no implicit `this`, and the first explicit argument is +// arg 1. +// +// Example: +// +// /* arg_a cannot be null, but arg_b can */ +// void Function(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(1); +// +// class C { +// /* arg_a cannot be null, but arg_b can */ +// void Method(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(2); +// +// /* arg_a cannot be null, but arg_b can */ +// static void StaticMethod(void* arg_a, void* arg_b) +// ABSL_ATTRIBUTE_NONNULL(1); +// }; +// +// If no arguments are provided, then all pointer arguments should be non-null. +// +// /* No pointer arguments may be null. */ +// void Function(void* arg_a, void* arg_b, int arg_c) ABSL_ATTRIBUTE_NONNULL(); +// +// NOTE: The GCC nonnull attribute actually accepts a list of arguments, but +// ABSL_ATTRIBUTE_NONNULL does not. +#if ABSL_HAVE_ATTRIBUTE(nonnull) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_NONNULL(arg_index) __attribute__((nonnull(arg_index))) +#else +#define ABSL_ATTRIBUTE_NONNULL(...) +#endif + +// ABSL_ATTRIBUTE_NORETURN +// +// Tells the compiler that a given function never returns. +// +// Deprecated: Prefer the `[[noreturn]]` attribute standardized by C++11 over +// this macro. +#if ABSL_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define ABSL_ATTRIBUTE_NORETURN __declspec(noreturn) +#else +#define ABSL_ATTRIBUTE_NORETURN +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS +// +// Tells the AddressSanitizer (or other memory testing tools) to ignore a given +// function. Useful for cases when a function reads random locations on stack, +// calls _exit from a cloned subprocess, deliberately accesses buffer +// out of bounds or does other scary things with memory. +// NOTE: GCC supports AddressSanitizer(asan) since 4.8. +// https://gcc.gnu.org/gcc-4.8/changes.html +#if defined(ABSL_HAVE_ADDRESS_SANITIZER) && \ + ABSL_HAVE_ATTRIBUTE(no_sanitize_address) +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) && defined(_MSC_VER) && \ + _MSC_VER >= 1928 +// https://docs.microsoft.com/en-us/cpp/cpp/no-sanitize-address +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __declspec(no_sanitize_address) +#elif defined(ABSL_HAVE_HWADDRESS_SANITIZER) && ABSL_HAVE_ATTRIBUTE(no_sanitize) +// HWAddressSanitizer is a sanitizer similar to AddressSanitizer, which uses CPU +// features to detect similar bugs with less CPU and memory overhead. +// NOTE: GCC supports HWAddressSanitizer(hwasan) since 11. +// https://gcc.gnu.org/gcc-11/changes.html +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS \ + __attribute__((no_sanitize("hwaddress"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY +// +// Tells the MemorySanitizer to relax the handling of a given function. All "Use +// of uninitialized value" warnings from such functions will be suppressed, and +// all values loaded from memory will be considered fully initialized. This +// attribute is similar to the ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS attribute +// above, but deals with initialized-ness rather than addressability issues. +// NOTE: MemorySanitizer(msan) is supported by Clang but not GCC. +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_memory) +#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_THREAD +// +// Tells the ThreadSanitizer to not instrument a given function. +// NOTE: GCC supports ThreadSanitizer(tsan) since 4.8. +// https://gcc.gnu.org/gcc-4.8/changes.html +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_thread) +#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED +// +// Tells the UndefinedSanitizer to ignore a given function. Useful for cases +// where certain behavior (eg. division by zero) is being used intentionally. +// NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9. +// https://gcc.gnu.org/gcc-4.9/changes.html +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_undefined) +#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ + __attribute__((no_sanitize_undefined)) +#elif ABSL_HAVE_ATTRIBUTE(no_sanitize) +#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ + __attribute__((no_sanitize("undefined"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_CFI +// +// Tells the ControlFlowIntegrity sanitizer to not instrument a given function. +// See https://clang.llvm.org/docs/ControlFlowIntegrity.html for details. +#if ABSL_HAVE_ATTRIBUTE(no_sanitize) && defined(__llvm__) +#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK +// +// Tells the SafeStack to not instrument a given function. +// See https://clang.llvm.org/docs/SafeStack.html for details. +#if ABSL_HAVE_ATTRIBUTE(no_sanitize) +#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \ + __attribute__((no_sanitize("safe-stack"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK +#endif + +// ABSL_ATTRIBUTE_RETURNS_NONNULL +// +// Tells the compiler that a particular function never returns a null pointer. +#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) +#define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) +#else +#define ABSL_ATTRIBUTE_RETURNS_NONNULL +#endif + +// ABSL_HAVE_ATTRIBUTE_SECTION +// +// Indicates whether labeled sections are supported. Weak symbol support is +// a prerequisite. Labeled sections are not supported on Darwin/iOS. +#ifdef ABSL_HAVE_ATTRIBUTE_SECTION +#error ABSL_HAVE_ATTRIBUTE_SECTION cannot be directly set +#elif (ABSL_HAVE_ATTRIBUTE(section) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + !defined(__APPLE__) && ABSL_HAVE_ATTRIBUTE_WEAK +#define ABSL_HAVE_ATTRIBUTE_SECTION 1 + +// ABSL_ATTRIBUTE_SECTION +// +// Tells the compiler/linker to put a given function into a section and define +// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. +// This functionality is supported by GNU linker. Any function annotated with +// `ABSL_ATTRIBUTE_SECTION` must not be inlined, or it will be placed into +// whatever section its caller is placed into. +// +#ifndef ABSL_ATTRIBUTE_SECTION +#define ABSL_ATTRIBUTE_SECTION(name) \ + __attribute__((section(#name))) __attribute__((noinline)) +#endif + +// ABSL_ATTRIBUTE_SECTION_VARIABLE +// +// Tells the compiler/linker to put a given variable into a section and define +// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. +// This functionality is supported by GNU linker. +#ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE +#ifdef _AIX +// __attribute__((section(#name))) on AIX is achieved by using the `.csect` +// pseudo op which includes an additional integer as part of its syntax +// indicating alignment. If data fall under different alignments then you might +// get a compilation error indicating a `Section type conflict`. +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) +#else +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name))) +#endif +#endif + +// ABSL_DECLARE_ATTRIBUTE_SECTION_VARS +// +// A weak section declaration to be used as a global declaration +// for ABSL_ATTRIBUTE_SECTION_START|STOP(name) to compile and link +// even without functions with ABSL_ATTRIBUTE_SECTION(name). +// ABSL_DEFINE_ATTRIBUTE_SECTION should be in the exactly one file; it's +// a no-op on ELF but not on Mach-O. +// +#ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS +#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ + extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ + extern char __stop_##name[] ABSL_ATTRIBUTE_WEAK +#endif +#ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS +#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name) +#endif + +// ABSL_ATTRIBUTE_SECTION_START +// +// Returns `void*` pointers to start/end of a section of code with +// functions having ABSL_ATTRIBUTE_SECTION(name). +// Returns 0 if no such functions exist. +// One must ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) for this to compile and +// link. +// +#define ABSL_ATTRIBUTE_SECTION_START(name) \ + (reinterpret_cast(__start_##name)) +#define ABSL_ATTRIBUTE_SECTION_STOP(name) \ + (reinterpret_cast(__stop_##name)) + +#else // !ABSL_HAVE_ATTRIBUTE_SECTION + +#define ABSL_HAVE_ATTRIBUTE_SECTION 0 + +// provide dummy definitions +#define ABSL_ATTRIBUTE_SECTION(name) +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) +#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_ATTRIBUTE_SECTION_START(name) (reinterpret_cast(0)) +#define ABSL_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast(0)) + +#endif // ABSL_ATTRIBUTE_SECTION + +// ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +// +// Support for aligning the stack on 32-bit x86. +#if ABSL_HAVE_ATTRIBUTE(force_align_arg_pointer) || \ + (defined(__GNUC__) && !defined(__clang__)) +#if defined(__i386__) +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC \ + __attribute__((force_align_arg_pointer)) +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) +#elif defined(__x86_64__) +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (1) +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +#else // !__i386__ && !__x86_64 +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +#endif // __i386__ +#else +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) +#endif + +// ABSL_MUST_USE_RESULT +// +// Tells the compiler to warn about unused results. +// +// For code or headers that are assured to only build with C++17 and up, prefer +// just using the standard `[[nodiscard]]` directly over this macro. +// +// When annotating a function, it must appear as the first part of the +// declaration or definition. The compiler will warn if the return value from +// such a function is unused: +// +// ABSL_MUST_USE_RESULT Sprocket* AllocateSprocket(); +// AllocateSprocket(); // Triggers a warning. +// +// When annotating a class, it is equivalent to annotating every function which +// returns an instance. +// +// class ABSL_MUST_USE_RESULT Sprocket {}; +// Sprocket(); // Triggers a warning. +// +// Sprocket MakeSprocket(); +// MakeSprocket(); // Triggers a warning. +// +// Note that references and pointers are not instances: +// +// Sprocket* SprocketPointer(); +// SprocketPointer(); // Does *not* trigger a warning. +// +// ABSL_MUST_USE_RESULT allows using cast-to-void to suppress the unused result +// warning. For that, warn_unused_result is used only for clang but not for gcc. +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 +// +// Note: past advice was to place the macro after the argument list. +// +// TODO(b/176172494): Use ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) when all code is +// compliant with the stricter [[nodiscard]]. +#if defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) +#define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result)) +#else +#define ABSL_MUST_USE_RESULT +#endif + +// ABSL_ATTRIBUTE_HOT, ABSL_ATTRIBUTE_COLD +// +// Tells GCC that a function is hot or cold. GCC can use this information to +// improve static analysis, i.e. a conditional branch to a cold function +// is likely to be not-taken. +// This annotation is used for function declarations. +// +// Example: +// +// int foo() ABSL_ATTRIBUTE_HOT; +#if ABSL_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_HOT __attribute__((hot)) +#else +#define ABSL_ATTRIBUTE_HOT +#endif + +#if ABSL_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_COLD __attribute__((cold)) +#else +#define ABSL_ATTRIBUTE_COLD +#endif + +// ABSL_XRAY_ALWAYS_INSTRUMENT, ABSL_XRAY_NEVER_INSTRUMENT, ABSL_XRAY_LOG_ARGS +// +// We define the ABSL_XRAY_ALWAYS_INSTRUMENT and ABSL_XRAY_NEVER_INSTRUMENT +// macro used as an attribute to mark functions that must always or never be +// instrumented by XRay. Currently, this is only supported in Clang/LLVM. +// +// For reference on the LLVM XRay instrumentation, see +// http://llvm.org/docs/XRay.html. +// +// A function with the XRAY_ALWAYS_INSTRUMENT macro attribute in its declaration +// will always get the XRay instrumentation sleds. These sleds may introduce +// some binary size and runtime overhead and must be used sparingly. +// +// These attributes only take effect when the following conditions are met: +// +// * The file/target is built in at least C++11 mode, with a Clang compiler +// that supports XRay attributes. +// * The file/target is built with the -fxray-instrument flag set for the +// Clang/LLVM compiler. +// * The function is defined in the translation unit (the compiler honors the +// attribute in either the definition or the declaration, and must match). +// +// There are cases when, even when building with XRay instrumentation, users +// might want to control specifically which functions are instrumented for a +// particular build using special-case lists provided to the compiler. These +// special case lists are provided to Clang via the +// -fxray-always-instrument=... and -fxray-never-instrument=... flags. The +// attributes in source take precedence over these special-case lists. +// +// To disable the XRay attributes at build-time, users may define +// ABSL_NO_XRAY_ATTRIBUTES. Do NOT define ABSL_NO_XRAY_ATTRIBUTES on specific +// packages/targets, as this may lead to conflicting definitions of functions at +// link-time. +// +// XRay isn't currently supported on Android: +// https://github.com/android/ndk/issues/368 +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_always_instrument) && \ + !defined(ABSL_NO_XRAY_ATTRIBUTES) && !defined(__ANDROID__) +#define ABSL_XRAY_ALWAYS_INSTRUMENT [[clang::xray_always_instrument]] +#define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]] +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args) +#define ABSL_XRAY_LOG_ARGS(N) \ + [[clang::xray_always_instrument, clang::xray_log_args(N)]] +#else +#define ABSL_XRAY_LOG_ARGS(N) [[clang::xray_always_instrument]] +#endif +#else +#define ABSL_XRAY_ALWAYS_INSTRUMENT +#define ABSL_XRAY_NEVER_INSTRUMENT +#define ABSL_XRAY_LOG_ARGS(N) +#endif + +// ABSL_ATTRIBUTE_REINITIALIZES +// +// Indicates that a member function reinitializes the entire object to a known +// state, independent of the previous state of the object. +// +// The clang-tidy check bugprone-use-after-move allows member functions marked +// with this attribute to be called on objects that have been moved from; +// without the attribute, this would result in a use-after-move warning. +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::reinitializes) +#define ABSL_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]] +#else +#define ABSL_ATTRIBUTE_REINITIALIZES +#endif + +// ----------------------------------------------------------------------------- +// Variable Attributes +// ----------------------------------------------------------------------------- + +// ABSL_ATTRIBUTE_UNUSED +// +// Prevents the compiler from complaining about variables that appear unused. +// +// Deprecated: Use the standard C++17 `[[maybe_unused]` instead. +// +// Due to differences in positioning requirements between the old, compiler +// specific __attribute__ syntax and the now standard `[[maybe_unused]]`, this +// macro does not attempt to take advantage of `[[maybe_unused]]`. +#if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__)) +#undef ABSL_ATTRIBUTE_UNUSED +#define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__)) +#else +#define ABSL_ATTRIBUTE_UNUSED +#endif + +// ABSL_ATTRIBUTE_INITIAL_EXEC +// +// Tells the compiler to use "initial-exec" mode for a thread-local variable. +// See http://people.redhat.com/drepper/tls.pdf for the gory details. +#if ABSL_HAVE_ATTRIBUTE(tls_model) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_INITIAL_EXEC __attribute__((tls_model("initial-exec"))) +#else +#define ABSL_ATTRIBUTE_INITIAL_EXEC +#endif + +// ABSL_ATTRIBUTE_PACKED +// +// Instructs the compiler not to use natural alignment for a tagged data +// structure, but instead to reduce its alignment to 1. +// +// Therefore, DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing +// so can cause atomic variables to be mis-aligned and silently violate +// atomicity on x86. +// +// This attribute can either be applied to members of a structure or to a +// structure in its entirety. Applying this attribute (judiciously) to a +// structure in its entirety to optimize the memory footprint of very +// commonly-used structs is fine. Do not apply this attribute to a structure in +// its entirety if the purpose is to control the offsets of the members in the +// structure. Instead, apply this attribute only to structure members that need +// it. +// +// When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the +// natural alignment of structure members not annotated is preserved. Aligned +// member accesses are faster than non-aligned member accesses even if the +// targeted microprocessor supports non-aligned accesses. +#if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__)) +#else +#define ABSL_ATTRIBUTE_PACKED +#endif + +// ABSL_ATTRIBUTE_FUNC_ALIGN +// +// Tells the compiler to align the function start at least to certain +// alignment boundary +#if ABSL_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) __attribute__((aligned(bytes))) +#else +#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) +#endif + +// ABSL_FALLTHROUGH_INTENDED +// +// Annotates implicit fall-through between switch labels, allowing a case to +// indicate intentional fallthrough and turn off warnings about any lack of a +// `break` statement. The ABSL_FALLTHROUGH_INTENDED macro should be followed by +// a semicolon and can be used in most places where `break` can, provided that +// no statements exist between it and the next switch label. +// +// Example: +// +// switch (x) { +// case 40: +// case 41: +// if (truth_is_out_there) { +// ++x; +// ABSL_FALLTHROUGH_INTENDED; // Use instead of/along with annotations +// // in comments +// } else { +// return x; +// } +// case 42: +// ... +// +// Notes: When supported, GCC and Clang can issue a warning on switch labels +// with unannotated fallthrough using the warning `-Wimplicit-fallthrough`. See +// clang documentation on language extensions for details: +// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough +// +// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro has +// no effect on diagnostics. In any case this macro has no effect on runtime +// behavior and performance of code. + +#ifdef ABSL_FALLTHROUGH_INTENDED +#error "ABSL_FALLTHROUGH_INTENDED should not be defined." +#elif ABSL_HAVE_CPP_ATTRIBUTE(fallthrough) +#define ABSL_FALLTHROUGH_INTENDED [[fallthrough]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::fallthrough) +#define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::fallthrough) +#define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] +#else +#define ABSL_FALLTHROUGH_INTENDED \ + do { \ + } while (0) +#endif + +// ABSL_DEPRECATED() +// +// Marks a deprecated class, struct, enum, function, method and variable +// declarations. The macro argument is used as a custom diagnostic message (e.g. +// suggestion of a better alternative). +// +// For code or headers that are assured to only build with C++14 and up, prefer +// just using the standard `[[deprecated("message")]]` directly over this macro. +// +// Examples: +// +// class ABSL_DEPRECATED("Use Bar instead") Foo {...}; +// +// ABSL_DEPRECATED("Use Baz() instead") void Bar() {...} +// +// template +// ABSL_DEPRECATED("Use DoThat() instead") +// void DoThis(); +// +// enum FooEnum { +// kBar ABSL_DEPRECATED("Use kBaz instead"), +// }; +// +// Every usage of a deprecated entity will trigger a warning when compiled with +// GCC/Clang's `-Wdeprecated-declarations` option. Google's production toolchain +// turns this warning off by default, instead relying on clang-tidy to report +// new uses of deprecated code. +#if ABSL_HAVE_ATTRIBUTE(deprecated) +#define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) +#else +#define ABSL_DEPRECATED(message) +#endif + +// When deprecating Abseil code, it is sometimes necessary to turn off the +// warning within Abseil, until the deprecated code is actually removed. The +// deprecated code can be surrounded with these directives to achieve that +// result. +// +// class ABSL_DEPRECATED("Use Bar instead") Foo; +// +// ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING +// Baz ComputeBazFromFoo(Foo f); +// ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING +#if defined(__GNUC__) || defined(__clang__) +// Clang also supports these GCC pragmas. +#define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING \ + _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) +#define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING \ + _Pragma("warning(push)") _Pragma("warning(disable: 4996)") +#define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING \ + _Pragma("warning(pop)") +#else +#define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING +#define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING +#endif // defined(__GNUC__) || defined(__clang__) + +// ABSL_CONST_INIT +// +// A variable declaration annotated with the `ABSL_CONST_INIT` attribute will +// not compile (on supported platforms) unless the variable has a constant +// initializer. This is useful for variables with static and thread storage +// duration, because it guarantees that they will not suffer from the so-called +// "static init order fiasco". +// +// This attribute must be placed on the initializing declaration of the +// variable. Some compilers will give a -Wmissing-constinit warning when this +// attribute is placed on some other declaration but missing from the +// initializing declaration. +// +// In some cases (notably with thread_local variables), `ABSL_CONST_INIT` can +// also be used in a non-initializing declaration to tell the compiler that a +// variable is already initialized, reducing overhead that would otherwise be +// incurred by a hidden guard variable. Thus annotating all declarations with +// this attribute is recommended to potentially enhance optimization. +// +// Example: +// +// class MyClass { +// public: +// ABSL_CONST_INIT static MyType my_var; +// }; +// +// ABSL_CONST_INIT MyType MyClass::my_var = MakeMyType(...); +// +// For code or headers that are assured to only build with C++20 and up, prefer +// just using the standard `constinit` keyword directly over this macro. +// +// Note that this attribute is redundant if the variable is declared constexpr. +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +#define ABSL_CONST_INIT constinit +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +#define ABSL_CONST_INIT [[clang::require_constant_initialization]] +#else +#define ABSL_CONST_INIT +#endif + +// ABSL_REQUIRE_EXPLICIT_INIT +// +// ABSL_REQUIRE_EXPLICIT_INIT is placed *after* the data members of an aggregate +// type to indicate that the annotated member must be explicitly initialized by +// the user whenever the aggregate is constructed. For example: +// +// struct Coord { +// int x ABSL_REQUIRE_EXPLICIT_INIT; +// int y ABSL_REQUIRE_EXPLICIT_INIT; +// }; +// Coord coord = {1}; // warning: field 'y' is not explicitly initialized +// +// Note that usage on C arrays is not supported in C++. +// Use a struct (such as std::array) to wrap the array member instead. +// +// Avoid applying this attribute to the members of non-aggregate types. +// The behavior within non-aggregates is unspecified and subject to change. +// +// Do NOT attempt to suppress or demote the error generated by this attribute. +// Just like with a missing function argument, it is a hard error by design. +// +// See the upstream documentation for more details: +// https://clang.llvm.org/docs/AttributeReference.html#require-explicit-initialization +#ifdef __cplusplus +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_explicit_initialization) +// clang-format off +#define ABSL_REQUIRE_EXPLICIT_INIT \ + [[clang::require_explicit_initialization]] = \ + AbslInternal_YouForgotToExplicitlyInitializeAField::v +#else +#define ABSL_REQUIRE_EXPLICIT_INIT \ + = AbslInternal_YouForgotToExplicitlyInitializeAField::v +#endif +// clang-format on +#else +// clang-format off +#if ABSL_HAVE_ATTRIBUTE(require_explicit_initialization) +#define ABSL_REQUIRE_EXPLICIT_INIT \ + __attribute__((require_explicit_initialization)) +#else +#define ABSL_REQUIRE_EXPLICIT_INIT \ + /* No portable fallback for C is available */ +#endif +// clang-format on +#endif + +#ifdef __cplusplus +struct AbslInternal_YouForgotToExplicitlyInitializeAField { + // A portable version of [[clang::require_explicit_initialization]] that + // never builds, as a last resort for all toolchains. + // The error messages are poor, so we don't rely on this unless we have to. + template +#if !defined(SWIG) + constexpr +#endif + operator T() const /* NOLINT */ { + const void *volatile deliberately_volatile_ptr = nullptr; + // Infinite loop to prevent constexpr compilation + for (;;) { + // This assignment ensures the 'this' pointer is not optimized away, so + // that linking always fails. + deliberately_volatile_ptr = this; // Deliberately not constexpr + (void)deliberately_volatile_ptr; + } + } + // This is deliberately left undefined to prevent linking + static AbslInternal_YouForgotToExplicitlyInitializeAField v; +}; +#endif + +// ABSL_ATTRIBUTE_PURE_FUNCTION +// +// ABSL_ATTRIBUTE_PURE_FUNCTION is used to annotate declarations of "pure" +// functions. A function is pure if its return value is only a function of its +// arguments. The pure attribute prohibits a function from modifying the state +// of the program that is observable by means other than inspecting the +// function's return value. Declaring such functions with the pure attribute +// allows the compiler to avoid emitting some calls in repeated invocations of +// the function with the same argument values. +// +// Example: +// +// ABSL_ATTRIBUTE_PURE_FUNCTION std::string FormatTime(Time t); +#if ABSL_HAVE_CPP_ATTRIBUTE(gnu::pure) +#define ABSL_ATTRIBUTE_PURE_FUNCTION [[gnu::pure]] +#elif ABSL_HAVE_ATTRIBUTE(pure) +#define ABSL_ATTRIBUTE_PURE_FUNCTION __attribute__((pure)) +#else +// If the attribute isn't defined, we'll fallback to ABSL_MUST_USE_RESULT since +// pure functions are useless if its return is ignored. +#define ABSL_ATTRIBUTE_PURE_FUNCTION ABSL_MUST_USE_RESULT +#endif + +// ABSL_ATTRIBUTE_CONST_FUNCTION +// +// ABSL_ATTRIBUTE_CONST_FUNCTION is used to annotate declarations of "const" +// functions. A const function is similar to a pure function, with one +// exception: Pure functions may return value that depend on a non-volatile +// object that isn't provided as a function argument, while the const function +// is guaranteed to return the same result given the same arguments. +// +// Example: +// +// ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Milliseconds(Duration d); +#if defined(_MSC_VER) && !defined(__clang__) +// Put the MSVC case first since MSVC seems to parse const as a C++ keyword. +#define ABSL_ATTRIBUTE_CONST_FUNCTION ABSL_ATTRIBUTE_PURE_FUNCTION +#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::const) +#define ABSL_ATTRIBUTE_CONST_FUNCTION [[gnu::const]] +#elif ABSL_HAVE_ATTRIBUTE(const) +#define ABSL_ATTRIBUTE_CONST_FUNCTION __attribute__((const)) +#else +// Since const functions are more restrictive pure function, we'll fallback to a +// pure function if the const attribute is not handled. +#define ABSL_ATTRIBUTE_CONST_FUNCTION ABSL_ATTRIBUTE_PURE_FUNCTION +#endif + +// ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function +// parameter or implicit object parameter is retained by the return value of the +// annotated function (or, for a parameter of a constructor, in the value of the +// constructed object). This attribute causes warnings to be produced if a +// temporary object does not live long enough. +// +// When applied to a reference parameter, the referenced object is assumed to be +// retained by the return value of the function. When applied to a non-reference +// parameter (for example, a pointer or a class type), all temporaries +// referenced by the parameter are assumed to be retained by the return value of +// the function. +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +// https://learn.microsoft.com/en-us/cpp/code-quality/c26816?view=msvc-170 +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(msvc::lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[msvc::lifetimebound]] +#elif ABSL_HAVE_ATTRIBUTE(lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound)) +#else +#define ABSL_ATTRIBUTE_LIFETIME_BOUND +#endif + +// Internal attribute; name and documentation TBD. +// +// See the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#lifetime_capture_by +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetime_capture_by) +#define ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(Owner) \ + [[clang::lifetime_capture_by(Owner)]] +#else +#define ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(Owner) +#endif + +// ABSL_ATTRIBUTE_VIEW indicates that a type is solely a "view" of data that it +// points to, similarly to a span, string_view, or other non-owning reference +// type. +// This enables diagnosing certain lifetime issues similar to those enabled by +// ABSL_ATTRIBUTE_LIFETIME_BOUND, such as: +// +// struct ABSL_ATTRIBUTE_VIEW StringView { +// template +// StringView(const R&); +// }; +// +// StringView f(std::string s) { +// return s; // warning: address of stack memory returned +// } +// +// We disable this on Clang versions < 13 because of the following +// false-positive: +// +// absl::string_view f(absl::optional sv) { return *sv; } +// +// See the following links for details: +// https://reviews.llvm.org/D64448 +// https://lists.llvm.org/pipermail/cfe-dev/2018-November/060355.html +#if ABSL_HAVE_CPP_ATTRIBUTE(gsl::Pointer) && \ + (!defined(__clang_major__) || __clang_major__ >= 13) +#define ABSL_ATTRIBUTE_VIEW [[gsl::Pointer]] +#else +#define ABSL_ATTRIBUTE_VIEW +#endif + +// ABSL_ATTRIBUTE_OWNER indicates that a type is a container, smart pointer, or +// similar class that owns all the data that it points to. +// This enables diagnosing certain lifetime issues similar to those enabled by +// ABSL_ATTRIBUTE_LIFETIME_BOUND, such as: +// +// struct ABSL_ATTRIBUTE_VIEW StringView { +// template +// StringView(const R&); +// }; +// +// struct ABSL_ATTRIBUTE_OWNER String {}; +// +// StringView f(String s) { +// return s; // warning: address of stack memory returned +// } +// +// We disable this on Clang versions < 13 because of the following +// false-positive: +// +// absl::string_view f(absl::optional sv) { return *sv; } +// +// See the following links for details: +// https://reviews.llvm.org/D64448 +// https://lists.llvm.org/pipermail/cfe-dev/2018-November/060355.html +#if ABSL_HAVE_CPP_ATTRIBUTE(gsl::Owner) && \ + (!defined(__clang_major__) || __clang_major__ >= 13) +#define ABSL_ATTRIBUTE_OWNER [[gsl::Owner]] +#else +#define ABSL_ATTRIBUTE_OWNER +#endif + +// ABSL_ATTRIBUTE_TRIVIAL_ABI +// Indicates that a type is "trivially relocatable" -- meaning it can be +// relocated without invoking the constructor/destructor, using a form of move +// elision. +// +// From a memory safety point of view, putting aside destructor ordering, it's +// safe to apply ABSL_ATTRIBUTE_TRIVIAL_ABI if an object's location +// can change over the course of its lifetime: if a constructor can be run one +// place, and then the object magically teleports to another place where some +// methods are run, and then the object teleports to yet another place where it +// is destroyed. This is notably not true for self-referential types, where the +// move-constructor must keep the self-reference up to date. If the type changed +// location without invoking the move constructor, it would have a dangling +// self-reference. +// +// The use of this teleporting machinery means that the number of paired +// move/destroy operations can change, and so it is a bad idea to apply this to +// a type meant to count the number of moves. +// +// Warning: applying this can, rarely, break callers. Objects passed by value +// will be destroyed at the end of the call, instead of the end of the +// full-expression containing the call. In addition, it changes the ABI +// of functions accepting this type by value (e.g. to pass in registers). +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi +// +// b/321691395 - This is currently disabled in open-source builds since +// compiler support differs. If system libraries compiled with GCC are mixed +// with libraries compiled with Clang, types will have different ideas about +// their ABI, leading to hard to debug crashes. +#define ABSL_ATTRIBUTE_TRIVIAL_ABI + +// ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS +// +// Indicates a data member can be optimized to occupy no space (if it is empty) +// and/or its tail padding can be used for other members. +// +// For code that is assured to only build with C++20 or later, prefer using +// the standard attribute `[[no_unique_address]]` directly instead of this +// macro. +// +// https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#c20-no_unique_address +// Current versions of MSVC have disabled `[[no_unique_address]]` since it +// breaks ABI compatibility, but offers `[[msvc::no_unique_address]]` for +// situations when it can be assured that it is desired. Since Abseil does not +// claim ABI compatibility in mixed builds, we can offer it unconditionally. +#if defined(_MSC_VER) && _MSC_VER >= 1929 +#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(no_unique_address) +#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS +#endif + +// ABSL_ATTRIBUTE_UNINITIALIZED +// +// GCC and Clang support a flag `-ftrivial-auto-var-init=; + // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17, + // but this seems to be mostly pedantic. + template + using EnableIfForwardIterator = std::enable_if_t< + base_internal::IsAtLeastForwardIterator::value>; + static constexpr bool NoexceptCopyable() { + return std::is_nothrow_copy_constructible::value && + absl::allocator_is_nothrow::value; + } + static constexpr bool NoexceptMovable() { + return std::is_nothrow_move_constructible::value && + absl::allocator_is_nothrow::value; + } + static constexpr bool DefaultConstructorIsNonTrivial() { + return !absl::is_trivially_default_constructible::value; + } + + public: + using allocator_type = typename AllocatorTraits::allocator_type; + using value_type = typename AllocatorTraits::value_type; + using pointer = typename AllocatorTraits::pointer; + using const_pointer = typename AllocatorTraits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = typename AllocatorTraits::size_type; + using difference_type = typename AllocatorTraits::difference_type; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + static constexpr size_type inline_elements = + (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type) + : static_cast(N)); + + FixedArray(const FixedArray& other) noexcept(NoexceptCopyable()) + : FixedArray(other, + AllocatorTraits::select_on_container_copy_construction( + other.storage_.alloc())) {} + + FixedArray(const FixedArray& other, + const allocator_type& a) noexcept(NoexceptCopyable()) + : FixedArray(other.begin(), other.end(), a) {} + + FixedArray(FixedArray&& other) noexcept(NoexceptMovable()) + : FixedArray(std::move(other), other.storage_.alloc()) {} + + FixedArray(FixedArray&& other, + const allocator_type& a) noexcept(NoexceptMovable()) + : FixedArray(std::make_move_iterator(other.begin()), + std::make_move_iterator(other.end()), a) {} + + // Creates an array object that can store `n` elements. + // Note that trivially constructible elements will be uninitialized. + explicit FixedArray(size_type n, const allocator_type& a = allocator_type()) + : storage_(n, a) { + if (DefaultConstructorIsNonTrivial()) { + memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), + storage_.end()); + } + } + + // Creates an array initialized with `n` copies of `val`. + FixedArray(size_type n, const value_type& val, + const allocator_type& a = allocator_type()) + : storage_(n, a) { + memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), + storage_.end(), val); + } + + // Creates an array initialized with the size and contents of `init_list`. + FixedArray(std::initializer_list init_list, + const allocator_type& a = allocator_type()) + : FixedArray(init_list.begin(), init_list.end(), a) {} + + // Creates an array initialized with the elements from the input + // range. The array's size will always be `std::distance(first, last)`. + // REQUIRES: Iterator must be a forward_iterator or better. + template * = nullptr> + FixedArray(Iterator first, Iterator last, + const allocator_type& a = allocator_type()) + : storage_(std::distance(first, last), a) { + memory_internal::CopyRange(storage_.alloc(), storage_.begin(), first, last); + } + + ~FixedArray() noexcept { + for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) { + AllocatorTraits::destroy(storage_.alloc(), cur); + } + } + + // Assignments are deleted because they break the invariant that the size of a + // `FixedArray` never changes. + void operator=(FixedArray&&) = delete; + void operator=(const FixedArray&) = delete; + + // FixedArray::size() + // + // Returns the length of the fixed array. + size_type size() const { return storage_.size(); } + + // FixedArray::max_size() + // + // Returns the largest possible value of `std::distance(begin(), end())` for a + // `FixedArray`. This is equivalent to the most possible addressable bytes + // over the number of bytes taken by T. + constexpr size_type max_size() const { + return (std::numeric_limits::max)() / sizeof(value_type); + } + + // FixedArray::empty() + // + // Returns whether or not the fixed array is empty. + bool empty() const { return size() == 0; } + + // FixedArray::memsize() + // + // Returns the memory size of the fixed array in bytes. + size_t memsize() const { return size() * sizeof(value_type); } + + // FixedArray::data() + // + // Returns a const T* pointer to elements of the `FixedArray`. This pointer + // can be used to access (but not modify) the contained elements. + const_pointer data() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return AsValueType(storage_.begin()); + } + + // Overload of FixedArray::data() to return a T* pointer to elements of the + // fixed array. This pointer can be used to access and modify the contained + // elements. + pointer data() ABSL_ATTRIBUTE_LIFETIME_BOUND { + return AsValueType(storage_.begin()); + } + + // FixedArray::operator[] + // + // Returns a reference the ith element of the fixed array. + // REQUIRES: 0 <= i < size() + reference operator[](size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(i < size()); + return data()[i]; + } + + // Overload of FixedArray::operator()[] to return a const reference to the + // ith element of the fixed array. + // REQUIRES: 0 <= i < size() + const_reference operator[](size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(i < size()); + return data()[i]; + } + + // FixedArray::at + // + // Bounds-checked access. Returns a reference to the ith element of the fixed + // array, or throws std::out_of_range + reference at(size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); + } + return data()[i]; + } + + // Overload of FixedArray::at() to return a const reference to the ith element + // of the fixed array. + const_reference at(size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); + } + return data()[i]; + } + + // FixedArray::front() + // + // Returns a reference to the first element of the fixed array. + reference front() ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } + + // Overload of FixedArray::front() to return a reference to the first element + // of a fixed array of const values. + const_reference front() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } + + // FixedArray::back() + // + // Returns a reference to the last element of the fixed array. + reference back() ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } + + // Overload of FixedArray::back() to return a reference to the last element + // of a fixed array of const values. + const_reference back() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } + + // FixedArray::begin() + // + // Returns an iterator to the beginning of the fixed array. + iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); } + + // Overload of FixedArray::begin() to return a const iterator to the + // beginning of the fixed array. + const_iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); } + + // FixedArray::cbegin() + // + // Returns a const iterator to the beginning of the fixed array. + const_iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return begin(); + } + + // FixedArray::end() + // + // Returns an iterator to the end of the fixed array. + iterator end() ABSL_ATTRIBUTE_LIFETIME_BOUND { return data() + size(); } + + // Overload of FixedArray::end() to return a const iterator to the end of the + // fixed array. + const_iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return data() + size(); + } + + // FixedArray::cend() + // + // Returns a const iterator to the end of the fixed array. + const_iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return end(); } + + // FixedArray::rbegin() + // + // Returns a reverse iterator from the end of the fixed array. + reverse_iterator rbegin() ABSL_ATTRIBUTE_LIFETIME_BOUND { + return reverse_iterator(end()); + } + + // Overload of FixedArray::rbegin() to return a const reverse iterator from + // the end of the fixed array. + const_reverse_iterator rbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return const_reverse_iterator(end()); + } + + // FixedArray::crbegin() + // + // Returns a const reverse iterator from the end of the fixed array. + const_reverse_iterator crbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return rbegin(); + } + + // FixedArray::rend() + // + // Returns a reverse iterator from the beginning of the fixed array. + reverse_iterator rend() ABSL_ATTRIBUTE_LIFETIME_BOUND { + return reverse_iterator(begin()); + } + + // Overload of FixedArray::rend() for returning a const reverse iterator + // from the beginning of the fixed array. + const_reverse_iterator rend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return const_reverse_iterator(begin()); + } + + // FixedArray::crend() + // + // Returns a reverse iterator from the beginning of the fixed array. + const_reverse_iterator crend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return rend(); + } + + // FixedArray::fill() + // + // Assigns the given `value` to all elements in the fixed array. + void fill(const value_type& val) { std::fill(begin(), end(), val); } + + // Relational operators. Equality operators are elementwise using + // `operator==`, while order operators order FixedArrays lexicographically. + friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) { + return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + + friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) { + return !(lhs == rhs); + } + + friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) { + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), + rhs.end()); + } + + friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) { + return rhs < lhs; + } + + friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) { + return !(rhs < lhs); + } + + friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) { + return !(lhs < rhs); + } + + template + friend H AbslHashValue(H h, const FixedArray& v) { + return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()), + hash_internal::WeaklyMixedInteger{v.size()}); + } + + private: + // StorageElement + // + // For FixedArrays with a C-style-array value_type, StorageElement is a POD + // wrapper struct called StorageElementWrapper that holds the value_type + // instance inside. This is needed for construction and destruction of the + // entire array regardless of how many dimensions it has. For all other cases, + // StorageElement is just an alias of value_type. + // + // Maintainer's Note: The simpler solution would be to simply wrap value_type + // in a struct whether it's an array or not. That causes some paranoid + // diagnostics to misfire, believing that 'data()' returns a pointer to a + // single element, rather than the packed array that it really is. + // e.g.: + // + // FixedArray buf(1); + // sprintf(buf.data(), "foo"); + // + // error: call to int __builtin___sprintf_chk(etc...) + // will always overflow destination buffer [-Werror] + // + template , + size_t InnerN = std::extent::value> + struct StorageElementWrapper { + InnerT array[InnerN]; + }; + + using StorageElement = + absl::conditional_t::value, + StorageElementWrapper, value_type>; + + static pointer AsValueType(pointer ptr) { return ptr; } + static pointer AsValueType(StorageElementWrapper* ptr) { + return std::addressof(ptr->array); + } + + static_assert(sizeof(StorageElement) == sizeof(value_type), ""); + static_assert(alignof(StorageElement) == alignof(value_type), ""); + + class NonEmptyInlinedStorage { + public: + StorageElement* data() { return reinterpret_cast(buff_); } + void AnnotateConstruct(size_type n); + void AnnotateDestruct(size_type n); + +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + void* RedzoneBegin() { return &redzone_begin_; } + void* RedzoneEnd() { return &redzone_end_ + 1; } +#endif // ABSL_HAVE_ADDRESS_SANITIZER + + private: + ABSL_ADDRESS_SANITIZER_REDZONE(redzone_begin_); + alignas(StorageElement) unsigned char buff_[sizeof( + StorageElement[inline_elements])]; + ABSL_ADDRESS_SANITIZER_REDZONE(redzone_end_); + }; + + class EmptyInlinedStorage { + public: + StorageElement* data() { return nullptr; } + void AnnotateConstruct(size_type) {} + void AnnotateDestruct(size_type) {} + }; + + using InlinedStorage = + absl::conditional_t; + + // Storage + // + // An instance of Storage manages the inline and out-of-line memory for + // instances of FixedArray. This guarantees that even when construction of + // individual elements fails in the FixedArray constructor body, the + // destructor for Storage will still be called and out-of-line memory will be + // properly deallocated. + // + class Storage : public InlinedStorage { + public: + Storage(size_type n, const allocator_type& a) + : size_alloc_(n, a), data_(InitializeData()) {} + + ~Storage() noexcept { + if (UsingInlinedStorage(size())) { + InlinedStorage::AnnotateDestruct(size()); + } else { + AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size()); + } + } + + size_type size() const { return size_alloc_.template get<0>(); } + StorageElement* begin() const { return data_; } + StorageElement* end() const { return begin() + size(); } + allocator_type& alloc() { return size_alloc_.template get<1>(); } + const allocator_type& alloc() const { + return size_alloc_.template get<1>(); + } + + private: + static bool UsingInlinedStorage(size_type n) { + return n <= inline_elements; + } + +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + ABSL_ATTRIBUTE_NOINLINE +#endif // ABSL_HAVE_ADDRESS_SANITIZER + StorageElement* InitializeData() { + if (UsingInlinedStorage(size())) { + InlinedStorage::AnnotateConstruct(size()); + return InlinedStorage::data(); + } else { + return reinterpret_cast( + AllocatorTraits::allocate(alloc(), size())); + } + } + + // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s + container_internal::CompressedTuple size_alloc_; + StorageElement* data_; + }; + + Storage storage_; +}; + +template +void FixedArray::NonEmptyInlinedStorage::AnnotateConstruct( + typename FixedArray::size_type n) { +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + if (!n) return; + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), + data() + n); + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), + RedzoneBegin()); +#endif // ABSL_HAVE_ADDRESS_SANITIZER + static_cast(n); // Mark used when not in asan mode +} + +template +void FixedArray::NonEmptyInlinedStorage::AnnotateDestruct( + typename FixedArray::size_type n) { +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + if (!n) return; + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, + RedzoneEnd()); + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), + data()); +#endif // ABSL_HAVE_ADDRESS_SANITIZER + static_cast(n); // Mark used when not in asan mode +} +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_FIXED_ARRAY_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/flat_hash_map.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/flat_hash_map.h new file mode 100644 index 00000000..bc86ced9 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/flat_hash_map.h @@ -0,0 +1,692 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: flat_hash_map.h +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_map` is an unordered associative container of +// unique keys and associated values designed to be a more efficient replacement +// for `std::unordered_map`. Like `unordered_map`, search, insertion, and +// deletion of map elements can be done as an `O(1)` operation. However, +// `flat_hash_map` (and other unordered associative containers known as the +// collection of Abseil "Swiss tables") contain other optimizations that result +// in both memory and computation advantages. +// +// In most cases, your default choice for a hash map should be a map of type +// `flat_hash_map`. +// +// `flat_hash_map` is not exception-safe. + +#ifndef ABSL_CONTAINER_FLAT_HASH_MAP_H_ +#define ABSL_CONTAINER_FLAT_HASH_MAP_H_ + +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/base/attributes.h" +#include "absl/base/macros.h" +#include "absl/container/hash_container_defaults.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +template +struct FlatHashMapPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::flat_hash_map +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_map` is an unordered associative container which +// has been optimized for both speed and memory footprint in most common use +// cases. Its interface is similar to that of `std::unordered_map` with +// the following notable differences: +// +// * Requires keys that are CopyConstructible +// * Requires values that are MoveConstructible +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the map is provided a compatible heterogeneous +// hashing function and equality operator. See below for details. +// * Invalidates any references and pointers to elements within the table after +// `rehash()` and when the table is moved. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash map. +// * Returns `void` from the `erase(iterator)` overload. +// +// By default, `flat_hash_map` uses the `absl::Hash` hashing framework. +// All fundamental and Abseil types that support the `absl::Hash` framework have +// a compatible equality operator for comparing insertions into `flat_hash_map`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// Using `absl::flat_hash_map` at interface boundaries in dynamically loaded +// libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may +// be randomized across dynamically loaded libraries. +// +// To achieve heterogeneous lookup for custom types either `Hash` and `Eq` type +// parameters can be used or `T` should have public inner types +// `absl_container_hash` and (optionally) `absl_container_eq`. In either case, +// `typename Hash::is_transparent` and `typename Eq::is_transparent` should be +// well-formed. Both types are basically functors: +// * `Hash` should support `size_t operator()(U val) const` that returns a hash +// for the given `val`. +// * `Eq` should support `bool operator()(U lhs, V rhs) const` that returns true +// if `lhs` is equal to `rhs`. +// +// In most cases `T` needs only to provide the `absl_container_hash`. In this +// case `std::equal_to` will be used instead of `eq` part. +// +// NOTE: A `flat_hash_map` stores its value types directly inside its +// implementation array to avoid memory indirection. Because a `flat_hash_map` +// is designed to move data when rehashed, map values will not retain pointer +// stability. If you require pointer stability, or if your values are large, +// consider using `absl::flat_hash_map>` instead. +// If your types are not moveable or you require pointer stability for keys, +// consider `absl::node_hash_map`. +// +// PERFORMANCE WARNING: Erasure & sparsity can negatively affect performance: +// * Iteration takes O(capacity) time, not O(size). +// * erase() slows down begin() and ++iterator. +// * Capacity only shrinks on rehash() or clear() -- not on erase(). +// +// Example: +// +// // Create a flat hash map of three strings (that map to strings) +// absl::flat_hash_map ducks = +// {{"a", "huey"}, {"b", "dewey"}, {"c", "louie"}}; +// +// // Insert a new element into the flat hash map +// ducks.insert({"d", "donald"}); +// +// // Force a rehash of the flat hash map +// ducks.rehash(0); +// +// // Find the element with the key "b" +// std::string search_key = "b"; +// auto result = ducks.find(search_key); +// if (result != ducks.end()) { +// std::cout << "Result: " << result->second << std::endl; +// } +template , + class Eq = DefaultHashContainerEq, + class Allocator = std::allocator>> +class ABSL_ATTRIBUTE_OWNER flat_hash_map + : public absl::container_internal::raw_hash_map< + absl::container_internal::FlatHashMapPolicy, Hash, Eq, + Allocator> { + using Base = typename flat_hash_map::raw_hash_map; + + public: + // Constructors and Assignment Operators + // + // A flat_hash_map supports the same overload set as `std::unordered_map` + // for construction and assignment: + // + // * Default constructor + // + // // No allocation for the table's elements is made. + // absl::flat_hash_map map1; + // + // * Initializer List constructor + // + // absl::flat_hash_map map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::flat_hash_map map3(map2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::flat_hash_map map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::flat_hash_map map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::flat_hash_map map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector> v = {{1, "a"}, {2, "b"}}; + // absl::flat_hash_map map7(v.begin(), v.end()); + flat_hash_map() {} + using Base::Base; + + // flat_hash_map::begin() + // + // Returns an iterator to the beginning of the `flat_hash_map`. + using Base::begin; + + // flat_hash_map::cbegin() + // + // Returns a const iterator to the beginning of the `flat_hash_map`. + using Base::cbegin; + + // flat_hash_map::cend() + // + // Returns a const iterator to the end of the `flat_hash_map`. + using Base::cend; + + // flat_hash_map::end() + // + // Returns an iterator to the end of the `flat_hash_map`. + using Base::end; + + // flat_hash_map::capacity() + // + // Returns the number of element slots (assigned, deleted, and empty) + // available within the `flat_hash_map`. + // + // NOTE: this member function is particular to `absl::flat_hash_map` and is + // not provided in the `std::unordered_map` API. + using Base::capacity; + + // flat_hash_map::empty() + // + // Returns whether or not the `flat_hash_map` is empty. + using Base::empty; + + // flat_hash_map::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `flat_hash_map` under current memory constraints. This value can be thought + // of the largest value of `std::distance(begin(), end())` for a + // `flat_hash_map`. + using Base::max_size; + + // flat_hash_map::size() + // + // Returns the number of elements currently within the `flat_hash_map`. + using Base::size; + + // flat_hash_map::clear() + // + // Removes all elements from the `flat_hash_map`. Invalidates any references, + // pointers, or iterators referring to contained elements. + // + // NOTE: this operation may shrink the underlying buffer. To avoid shrinking + // the underlying buffer call `erase(begin(), end())`. + using Base::clear; + + // flat_hash_map::erase() + // + // Erases elements within the `flat_hash_map`. Erasing does not trigger a + // rehash. Overloads are listed below. + // + // void erase(const_iterator pos): + // + // Erases the element at `position` of the `flat_hash_map`, returning + // `void`. + // + // NOTE: returning `void` in this case is different than that of STL + // containers in general and `std::unordered_map` in particular (which + // return an iterator to the element following the erased element). If that + // iterator is needed, simply post increment the iterator: + // + // map.erase(it++); + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. The special case of calling + // `erase(begin(), end())` resets the reserved growth such that if + // `reserve(N)` has previously been called and there has been no intervening + // call to `clear()`, then after calling `erase(begin(), end())`, it is safe + // to assume that inserting N elements will not cause a rehash. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). + using Base::erase; + + // flat_hash_map::insert() + // + // Inserts an element of the specified value into the `flat_hash_map`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If rehashing occurs + // due to the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair insert(const init_type& value): + // + // Inserts a value into the `flat_hash_map`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair insert(T&& value): + // std::pair insert(init_type&& value): + // + // Inserts a moveable value into the `flat_hash_map`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const init_type& value): + // iterator insert(const_iterator hint, T&& value): + // iterator insert(const_iterator hint, init_type&& value); + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently, for `flat_hash_map` we guarantee the + // first match is inserted. + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently within the initializer list, for + // `flat_hash_map` we guarantee the first match is inserted. + using Base::insert; + + // flat_hash_map::insert_or_assign() + // + // Inserts an element of the specified value into the `flat_hash_map` provided + // that a value with the given key does not already exist, or replaces it with + // the element value if a key for that value already exists, returning an + // iterator pointing to the newly inserted element. If rehashing occurs due + // to the insertion, all existing iterators are invalidated. Overloads are + // listed below. + // + // pair insert_or_assign(const init_type& k, T&& obj): + // pair insert_or_assign(init_type&& k, T&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `flat_hash_map`. + // + // iterator insert_or_assign(const_iterator hint, + // const init_type& k, T&& obj): + // iterator insert_or_assign(const_iterator hint, init_type&& k, T&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `flat_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + using Base::insert_or_assign; + + // flat_hash_map::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_map`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace; + + // flat_hash_map::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_map`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace_hint; + + // flat_hash_map::try_emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_map`, provided that no element with the given key + // already exists. Unlike `emplace()`, if an element with the given key + // already exists, we guarantee that no element is constructed. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + // Overloads are listed below. + // + // pair try_emplace(const key_type& k, Args&&... args): + // pair try_emplace(key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `flat_hash_map`. + // + // iterator try_emplace(const_iterator hint, + // const key_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `flat_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + // + // All `try_emplace()` overloads make the same guarantees regarding rvalue + // arguments as `std::unordered_map::try_emplace()`, namely that these + // functions will not move from rvalue arguments if insertions do not happen. + using Base::try_emplace; + + // flat_hash_map::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the key,value pair of the element at the indicated position and + // returns a node handle owning that extracted data. + // + // node_type extract(const key_type& x): + // + // Extracts the key,value pair of the element with a key matching the passed + // key value and returns a node handle owning that extracted data. If the + // `flat_hash_map` does not contain an element with a matching key, this + // function returns an empty node handle. + // + // NOTE: when compiled in an earlier version of C++ than C++17, + // `node_type::key()` returns a const reference to the key instead of a + // mutable reference. We cannot safely return a mutable reference without + // std::launder (which is not available before C++17). + using Base::extract; + + // flat_hash_map::merge() + // + // Extracts elements from a given `source` flat hash map into this + // `flat_hash_map`. If the destination `flat_hash_map` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // flat_hash_map::swap(flat_hash_map& other) + // + // Exchanges the contents of this `flat_hash_map` with those of the `other` + // flat hash map. + // + // All iterators and references on the `flat_hash_map` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the flat hash map's hashing and key equivalence + // functions be Swappable, and are exchanged using unqualified calls to + // non-member `swap()`. If the map's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // flat_hash_map::rehash(count) + // + // Rehashes the `flat_hash_map`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + // + // NOTE: unlike behavior in `std::unordered_map`, references are also + // invalidated upon a `rehash()`. + using Base::rehash; + + // flat_hash_map::reserve(count) + // + // Sets the number of slots in the `flat_hash_map` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // flat_hash_map::at() + // + // Returns a reference to the mapped value of the element with key equivalent + // to the passed key. + using Base::at; + + // flat_hash_map::contains() + // + // Determines whether an element with a key comparing equal to the given `key` + // exists within the `flat_hash_map`, returning `true` if so or `false` + // otherwise. + using Base::contains; + + // flat_hash_map::count(const Key& key) const + // + // Returns the number of elements with a key comparing equal to the given + // `key` within the `flat_hash_map`. note that this function will return + // either `1` or `0` since duplicate keys are not allowed within a + // `flat_hash_map`. + using Base::count; + + // flat_hash_map::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `flat_hash_map`. + using Base::equal_range; + + // flat_hash_map::find() + // + // Finds an element with the passed `key` within the `flat_hash_map`. + using Base::find; + + // flat_hash_map::operator[]() + // + // Returns a reference to the value mapped to the passed key within the + // `flat_hash_map`, performing an `insert()` if the key does not already + // exist. + // + // If an insertion occurs and results in a rehashing of the container, all + // iterators are invalidated. Otherwise iterators are not affected and + // references are not invalidated. Overloads are listed below. + // + // T& operator[](const Key& key): + // + // Inserts an init_type object constructed in-place if the element with the + // given key does not exist. + // + // T& operator[](Key&& key): + // + // Inserts an init_type object constructed in-place provided that an element + // with the given key does not exist. + using Base::operator[]; + + // flat_hash_map::bucket_count() + // + // Returns the number of "buckets" within the `flat_hash_map`. Note that + // because a flat hash map contains all elements within its internal storage, + // this value simply equals the current capacity of the `flat_hash_map`. + using Base::bucket_count; + + // flat_hash_map::load_factor() + // + // Returns the current load factor of the `flat_hash_map` (the average number + // of slots occupied with a value within the hash map). + using Base::load_factor; + + // flat_hash_map::max_load_factor() + // + // Manages the maximum load factor of the `flat_hash_map`. Overloads are + // listed below. + // + // float flat_hash_map::max_load_factor() + // + // Returns the current maximum load factor of the `flat_hash_map`. + // + // void flat_hash_map::max_load_factor(float ml) + // + // Sets the maximum load factor of the `flat_hash_map` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `flat_hash_map` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // flat_hash_map::get_allocator() + // + // Returns the allocator function associated with this `flat_hash_map`. + using Base::get_allocator; + + // flat_hash_map::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `flat_hash_map`. + using Base::hash_function; + + // flat_hash_map::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; +}; + +// erase_if(flat_hash_map<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +// Returns the number of erased elements. +template +typename flat_hash_map::size_type erase_if( + flat_hash_map& c, Predicate pred) { + return container_internal::EraseIf(pred, &c); +} + +// swap(flat_hash_map<>, flat_hash_map<>) +// +// Swaps the contents of two `flat_hash_map` containers. +// +// NOTE: we need to define this function template in order for +// `flat_hash_set::swap` to be called instead of `std::swap`. Even though we +// have `swap(raw_hash_set&, raw_hash_set&)` defined, that function requires a +// derived-to-base conversion, whereas `std::swap` is a function template so +// `std::swap` will be preferred by compiler. +template +void swap(flat_hash_map& x, + flat_hash_map& y) noexcept(noexcept(x.swap(y))) { + x.swap(y); +} + +namespace container_internal { + +// c_for_each_fast(flat_hash_map<>, Function) +// +// Container-based version of the `std::for_each()` function to +// apply a function to a container's elements. +// There is no guarantees on the order of the function calls. +// Erasure and/or insertion of elements in the function is not allowed. +template +decay_t c_for_each_fast(const flat_hash_map& c, + Function&& f) { + container_internal::ForEach(f, &c); + return f; +} +template +decay_t c_for_each_fast(flat_hash_map& c, + Function&& f) { + container_internal::ForEach(f, &c); + return f; +} +template +decay_t c_for_each_fast(flat_hash_map&& c, + Function&& f) { + container_internal::ForEach(f, &c); + return f; +} + +} // namespace container_internal + +namespace container_internal { + +template +struct FlatHashMapPolicy { + using slot_policy = container_internal::map_slot_policy; + using slot_type = typename slot_policy::slot_type; + using key_type = K; + using mapped_type = V; + using init_type = std::pair; + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + slot_policy::construct(alloc, slot, std::forward(args)...); + } + + // Returns std::true_type in case destroy is trivial. + template + static auto destroy(Allocator* alloc, slot_type* slot) { + return slot_policy::destroy(alloc, slot); + } + + template + static auto transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + return slot_policy::transfer(alloc, new_slot, old_slot); + } + + template + static decltype(absl::container_internal::DecomposePair( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposePair(std::forward(f), + std::forward(args)...); + } + + template + static constexpr HashSlotFn get_hash_slot_fn() { + return memory_internal::IsLayoutCompatible::value + ? &TypeErasedApplyToSlotFn + : nullptr; + } + + static size_t space_used(const slot_type*) { return 0; } + + static std::pair& element(slot_type* slot) { return slot->value; } + + static V& value(std::pair* kv) { return kv->second; } + static const V& value(const std::pair* kv) { return kv->second; } +}; + +} // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template +struct IsUnorderedContainer< + absl::flat_hash_map> : std::true_type {}; + +} // namespace container_algorithm_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/flat_hash_set.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/flat_hash_set.h new file mode 100644 index 00000000..bf63eb59 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/flat_hash_set.h @@ -0,0 +1,580 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: flat_hash_set.h +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_set` is an unordered associative container designed to +// be a more efficient replacement for `std::unordered_set`. Like +// `unordered_set`, search, insertion, and deletion of set elements can be done +// as an `O(1)` operation. However, `flat_hash_set` (and other unordered +// associative containers known as the collection of Abseil "Swiss tables") +// contain other optimizations that result in both memory and computation +// advantages. +// +// In most cases, your default choice for a hash set should be a set of type +// `flat_hash_set`. +// +// `flat_hash_set` is not exception-safe. + +#ifndef ABSL_CONTAINER_FLAT_HASH_SET_H_ +#define ABSL_CONTAINER_FLAT_HASH_SET_H_ + +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/base/attributes.h" +#include "absl/base/macros.h" +#include "absl/container/hash_container_defaults.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +template +struct FlatHashSetPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::flat_hash_set +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_set` is an unordered associative container which has +// been optimized for both speed and memory footprint in most common use cases. +// Its interface is similar to that of `std::unordered_set` with the +// following notable differences: +// +// * Requires keys that are MoveConstructible +// * Supports heterogeneous lookup, through `find()` and `insert()`, provided +// that the set is provided a compatible heterogeneous hashing function and +// equality operator. See below for details. +// * Invalidates any references and pointers to elements within the table after +// `rehash()` and when the table is moved. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash set. +// * Returns `void` from the `erase(iterator)` overload. +// +// By default, `flat_hash_set` uses the `absl::Hash` hashing framework. All +// fundamental and Abseil types that support the `absl::Hash` framework have a +// compatible equality operator for comparing insertions into `flat_hash_set`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// Using `absl::flat_hash_set` at interface boundaries in dynamically loaded +// libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may +// be randomized across dynamically loaded libraries. +// +// To achieve heterogeneous lookup for custom types either `Hash` and `Eq` type +// parameters can be used or `T` should have public inner types +// `absl_container_hash` and (optionally) `absl_container_eq`. In either case, +// `typename Hash::is_transparent` and `typename Eq::is_transparent` should be +// well-formed. Both types are basically functors: +// * `Hash` should support `size_t operator()(U val) const` that returns a hash +// for the given `val`. +// * `Eq` should support `bool operator()(U lhs, V rhs) const` that returns true +// if `lhs` is equal to `rhs`. +// +// In most cases `T` needs only to provide the `absl_container_hash`. In this +// case `std::equal_to` will be used instead of `eq` part. +// +// NOTE: A `flat_hash_set` stores its keys directly inside its implementation +// array to avoid memory indirection. Because a `flat_hash_set` is designed to +// move data when rehashed, set keys will not retain pointer stability. If you +// require pointer stability, consider using +// `absl::flat_hash_set>`. If your type is not moveable and +// you require pointer stability, consider `absl::node_hash_set` instead. +// +// PERFORMANCE WARNING: Erasure & sparsity can negatively affect performance: +// * Iteration takes O(capacity) time, not O(size). +// * erase() slows down begin() and ++iterator. +// * Capacity only shrinks on rehash() or clear() -- not on erase(). +// +// Example: +// +// // Create a flat hash set of three strings +// absl::flat_hash_set ducks = +// {"huey", "dewey", "louie"}; +// +// // Insert a new element into the flat hash set +// ducks.insert("donald"); +// +// // Force a rehash of the flat hash set +// ducks.rehash(0); +// +// // See if "dewey" is present +// if (ducks.contains("dewey")) { +// std::cout << "We found dewey!" << std::endl; +// } +template , + class Eq = DefaultHashContainerEq, + class Allocator = std::allocator> +class ABSL_ATTRIBUTE_OWNER flat_hash_set + : public absl::container_internal::raw_hash_set< + absl::container_internal::FlatHashSetPolicy, Hash, Eq, Allocator> { + using Base = typename flat_hash_set::raw_hash_set; + + public: + // Constructors and Assignment Operators + // + // A flat_hash_set supports the same overload set as `std::unordered_set` + // for construction and assignment: + // + // * Default constructor + // + // // No allocation for the table's elements is made. + // absl::flat_hash_set set1; + // + // * Initializer List constructor + // + // absl::flat_hash_set set2 = + // {{"huey"}, {"dewey"}, {"louie"},}; + // + // * Copy constructor + // + // absl::flat_hash_set set3(set2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::flat_hash_set set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::flat_hash_set set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::flat_hash_set set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector v = {"a", "b"}; + // absl::flat_hash_set set7(v.begin(), v.end()); + flat_hash_set() {} + using Base::Base; + + // flat_hash_set::begin() + // + // Returns an iterator to the beginning of the `flat_hash_set`. + using Base::begin; + + // flat_hash_set::cbegin() + // + // Returns a const iterator to the beginning of the `flat_hash_set`. + using Base::cbegin; + + // flat_hash_set::cend() + // + // Returns a const iterator to the end of the `flat_hash_set`. + using Base::cend; + + // flat_hash_set::end() + // + // Returns an iterator to the end of the `flat_hash_set`. + using Base::end; + + // flat_hash_set::capacity() + // + // Returns the number of element slots (assigned, deleted, and empty) + // available within the `flat_hash_set`. + // + // NOTE: this member function is particular to `absl::flat_hash_set` and is + // not provided in the `std::unordered_set` API. + using Base::capacity; + + // flat_hash_set::empty() + // + // Returns whether or not the `flat_hash_set` is empty. + using Base::empty; + + // flat_hash_set::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `flat_hash_set` under current memory constraints. This value can be thought + // of the largest value of `std::distance(begin(), end())` for a + // `flat_hash_set`. + using Base::max_size; + + // flat_hash_set::size() + // + // Returns the number of elements currently within the `flat_hash_set`. + using Base::size; + + // flat_hash_set::clear() + // + // Removes all elements from the `flat_hash_set`. Invalidates any references, + // pointers, or iterators referring to contained elements. + // + // NOTE: this operation may shrink the underlying buffer. To avoid shrinking + // the underlying buffer call `erase(begin(), end())`. + using Base::clear; + + // flat_hash_set::erase() + // + // Erases elements within the `flat_hash_set`. Erasing does not trigger a + // rehash. Overloads are listed below. + // + // void erase(const_iterator pos): + // + // Erases the element at `position` of the `flat_hash_set`, returning + // `void`. + // + // NOTE: returning `void` in this case is different than that of STL + // containers in general and `std::unordered_set` in particular (which + // return an iterator to the element following the erased element). If that + // iterator is needed, simply post increment the iterator: + // + // set.erase(it++); + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. The special case of calling + // `erase(begin(), end())` resets the reserved growth such that if + // `reserve(N)` has previously been called and there has been no intervening + // call to `clear()`, then after calling `erase(begin(), end())`, it is safe + // to assume that inserting N elements will not cause a rehash. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). + using Base::erase; + + // flat_hash_set::insert() + // + // Inserts an element of the specified value into the `flat_hash_set`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If rehashing occurs + // due to the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair insert(const T& value): + // + // Inserts a value into the `flat_hash_set`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair insert(T&& value): + // + // Inserts a moveable value into the `flat_hash_set`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const T& value): + // iterator insert(const_iterator hint, T&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently, for `flat_hash_set` we guarantee the + // first match is inserted. + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently within the initializer list, for + // `flat_hash_set` we guarantee the first match is inserted. + using Base::insert; + + // flat_hash_set::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_set`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace; + + // flat_hash_set::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_set`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace_hint; + + // flat_hash_set::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // node_type extract(const key_type& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `flat_hash_set` + // does not contain an element with a matching key, this function returns an + // empty node handle. + using Base::extract; + + // flat_hash_set::merge() + // + // Extracts elements from a given `source` flat hash set into this + // `flat_hash_set`. If the destination `flat_hash_set` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // flat_hash_set::swap(flat_hash_set& other) + // + // Exchanges the contents of this `flat_hash_set` with those of the `other` + // flat hash set. + // + // All iterators and references on the `flat_hash_set` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the flat hash set's hashing and key equivalence + // functions be Swappable, and are exchanged using unqualified calls to + // non-member `swap()`. If the set's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // flat_hash_set::rehash(count) + // + // Rehashes the `flat_hash_set`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + // + // NOTE: unlike behavior in `std::unordered_set`, references are also + // invalidated upon a `rehash()`. + using Base::rehash; + + // flat_hash_set::reserve(count) + // + // Sets the number of slots in the `flat_hash_set` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // flat_hash_set::contains() + // + // Determines whether an element comparing equal to the given `key` exists + // within the `flat_hash_set`, returning `true` if so or `false` otherwise. + using Base::contains; + + // flat_hash_set::count(const Key& key) const + // + // Returns the number of elements comparing equal to the given `key` within + // the `flat_hash_set`. note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `flat_hash_set`. + using Base::count; + + // flat_hash_set::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `flat_hash_set`. + using Base::equal_range; + + // flat_hash_set::find() + // + // Finds an element with the passed `key` within the `flat_hash_set`. + using Base::find; + + // flat_hash_set::bucket_count() + // + // Returns the number of "buckets" within the `flat_hash_set`. Note that + // because a flat hash set contains all elements within its internal storage, + // this value simply equals the current capacity of the `flat_hash_set`. + using Base::bucket_count; + + // flat_hash_set::load_factor() + // + // Returns the current load factor of the `flat_hash_set` (the average number + // of slots occupied with a value within the hash set). + using Base::load_factor; + + // flat_hash_set::max_load_factor() + // + // Manages the maximum load factor of the `flat_hash_set`. Overloads are + // listed below. + // + // float flat_hash_set::max_load_factor() + // + // Returns the current maximum load factor of the `flat_hash_set`. + // + // void flat_hash_set::max_load_factor(float ml) + // + // Sets the maximum load factor of the `flat_hash_set` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `flat_hash_set` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // flat_hash_set::get_allocator() + // + // Returns the allocator function associated with this `flat_hash_set`. + using Base::get_allocator; + + // flat_hash_set::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `flat_hash_set`. + using Base::hash_function; + + // flat_hash_set::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; +}; + +// erase_if(flat_hash_set<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +// Returns the number of erased elements. +template +typename flat_hash_set::size_type erase_if( + flat_hash_set& c, Predicate pred) { + return container_internal::EraseIf(pred, &c); +} + +// swap(flat_hash_set<>, flat_hash_set<>) +// +// Swaps the contents of two `flat_hash_set` containers. +// +// NOTE: we need to define this function template in order for +// `flat_hash_set::swap` to be called instead of `std::swap`. Even though we +// have `swap(raw_hash_set&, raw_hash_set&)` defined, that function requires a +// derived-to-base conversion, whereas `std::swap` is a function template so +// `std::swap` will be preferred by compiler. +template +void swap(flat_hash_set& x, + flat_hash_set& y) noexcept(noexcept(x.swap(y))) { + return x.swap(y); +} + +namespace container_internal { + +// c_for_each_fast(flat_hash_set<>, Function) +// +// Container-based version of the `std::for_each()` function to +// apply a function to a container's elements. +// There is no guarantees on the order of the function calls. +// Erasure and/or insertion of elements in the function is not allowed. +template +decay_t c_for_each_fast(const flat_hash_set& c, + Function&& f) { + container_internal::ForEach(f, &c); + return f; +} +template +decay_t c_for_each_fast(flat_hash_set& c, Function&& f) { + container_internal::ForEach(f, &c); + return f; +} +template +decay_t c_for_each_fast(flat_hash_set&& c, Function&& f) { + container_internal::ForEach(f, &c); + return f; +} + +} // namespace container_internal + +namespace container_internal { + +template +struct FlatHashSetPolicy { + using slot_type = T; + using key_type = T; + using init_type = T; + using constant_iterators = std::true_type; + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + absl::allocator_traits::construct(*alloc, slot, + std::forward(args)...); + } + + // Return std::true_type in case destroy is trivial. + template + static auto destroy(Allocator* alloc, slot_type* slot) { + absl::allocator_traits::destroy(*alloc, slot); + return IsDestructionTrivial(); + } + + static T& element(slot_type* slot) { return *slot; } + + template + static decltype(absl::container_internal::DecomposeValue( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposeValue( + std::forward(f), std::forward(args)...); + } + + static size_t space_used(const T*) { return 0; } + + template + static constexpr HashSlotFn get_hash_slot_fn() { + return &TypeErasedApplyToSlotFn; + } +}; +} // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template +struct IsUnorderedContainer> + : std::true_type {}; + +} // namespace container_algorithm_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_FLAT_HASH_SET_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hash_container_defaults.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hash_container_defaults.h new file mode 100644 index 00000000..eb944a7c --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hash_container_defaults.h @@ -0,0 +1,45 @@ +// Copyright 2024 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_HASH_CONTAINER_DEFAULTS_H_ +#define ABSL_CONTAINER_HASH_CONTAINER_DEFAULTS_H_ + +#include "absl/base/config.h" +#include "absl/container/internal/hash_function_defaults.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// DefaultHashContainerHash is a convenience alias for the functor that is used +// by default by Abseil hash-based (unordered) containers for hashing when +// `Hash` type argument is not explicitly specified. +// +// This type alias can be used by generic code that wants to provide more +// flexibility for defining underlying containers. +template +using DefaultHashContainerHash = absl::container_internal::hash_default_hash; + +// DefaultHashContainerEq is a convenience alias for the functor that is used by +// default by Abseil hash-based (unordered) containers for equality check when +// `Eq` type argument is not explicitly specified. +// +// This type alias can be used by generic code that wants to provide more +// flexibility for defining underlying containers. +template +using DefaultHashContainerEq = absl::container_internal::hash_default_eq; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_HASH_CONTAINER_DEFAULTS_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hashtablez_sampler.cc b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hashtablez_sampler.cc new file mode 100644 index 00000000..c0fce879 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hashtablez_sampler.cc @@ -0,0 +1,316 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/hashtablez_sampler.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/per_thread_tls.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/base/no_destructor.h" +#include "absl/base/optimization.h" +#include "absl/debugging/stacktrace.h" +#include "absl/memory/memory.h" +#include "absl/profiling/internal/exponential_biased.h" +#include "absl/profiling/internal/sample_recorder.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/clock.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +namespace { +ABSL_CONST_INIT std::atomic g_hashtablez_enabled{ + false +}; +ABSL_CONST_INIT std::atomic g_hashtablez_sample_parameter{1 << 10}; +std::atomic g_hashtablez_config_listener{nullptr}; + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +ABSL_PER_THREAD_TLS_KEYWORD absl::profiling_internal::ExponentialBiased + g_exponential_biased_generator; +#endif + +void TriggerHashtablezConfigListener() { + auto* listener = g_hashtablez_config_listener.load(std::memory_order_acquire); + if (listener != nullptr) listener(); +} + +} // namespace + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample = {0, 0}; +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +HashtablezSampler& GlobalHashtablezSampler() { + static absl::NoDestructor sampler; + return *sampler; +} + +HashtablezInfo::HashtablezInfo() = default; +HashtablezInfo::~HashtablezInfo() = default; + +void HashtablezInfo::PrepareForSampling(int64_t stride, + size_t inline_element_size_value, + size_t key_size_value, + size_t value_size_value, + uint16_t soo_capacity_value) { + capacity.store(0, std::memory_order_relaxed); + size.store(0, std::memory_order_relaxed); + num_erases.store(0, std::memory_order_relaxed); + num_rehashes.store(0, std::memory_order_relaxed); + max_probe_length.store(0, std::memory_order_relaxed); + total_probe_length.store(0, std::memory_order_relaxed); + hashes_bitwise_or.store(0, std::memory_order_relaxed); + hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed); + hashes_bitwise_xor.store(0, std::memory_order_relaxed); + max_reserve.store(0, std::memory_order_relaxed); + + create_time = absl::Now(); + weight = stride; + // The inliner makes hardcoded skip_count difficult (especially when combined + // with LTO). We use the ability to exclude stacks by regex when encoding + // instead. + depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth, + /* skip_count= */ 0); + inline_element_size = inline_element_size_value; + key_size = key_size_value; + value_size = value_size_value; + soo_capacity = soo_capacity_value; +} + +static bool ShouldForceSampling() { + enum ForceState { + kDontForce, + kForce, + kUninitialized + }; + ABSL_CONST_INIT static std::atomic global_state{ + kUninitialized}; + ForceState state = global_state.load(std::memory_order_relaxed); + if (ABSL_PREDICT_TRUE(state == kDontForce)) return false; + + if (state == kUninitialized) { + state = ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)() + ? kForce + : kDontForce; + global_state.store(state, std::memory_order_relaxed); + } + return state == kForce; +} + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +HashtablezInfoHandle ForcedTrySample(size_t inline_element_size, + size_t key_size, size_t value_size, + uint16_t soo_capacity) { + return HashtablezInfoHandle(SampleSlow(global_next_sample, + inline_element_size, key_size, + value_size, soo_capacity)); +} +void TestOnlyRefreshSamplingStateForCurrentThread() { + global_next_sample.next_sample = + g_hashtablez_sample_parameter.load(std::memory_order_relaxed); + global_next_sample.sample_stride = global_next_sample.next_sample; +} +#else +HashtablezInfoHandle ForcedTrySample(size_t, size_t, size_t, uint16_t) { + return HashtablezInfoHandle{nullptr}; +} +void TestOnlyRefreshSamplingStateForCurrentThread() {} +#endif // ABSL_INTERNAL_HASHTABLEZ_SAMPLE + +HashtablezInfo* SampleSlow(SamplingState& next_sample, + size_t inline_element_size, size_t key_size, + size_t value_size, uint16_t soo_capacity) { + if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { + next_sample.next_sample = 1; + const int64_t old_stride = exchange(next_sample.sample_stride, 1); + HashtablezInfo* result = GlobalHashtablezSampler().Register( + old_stride, inline_element_size, key_size, value_size, soo_capacity); + return result; + } + +#if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + next_sample = { + std::numeric_limits::max(), + std::numeric_limits::max(), + }; + return nullptr; +#else + bool first = next_sample.next_sample < 0; + + const int64_t next_stride = g_exponential_biased_generator.GetStride( + g_hashtablez_sample_parameter.load(std::memory_order_relaxed)); + + next_sample.next_sample = next_stride; + const int64_t old_stride = exchange(next_sample.sample_stride, next_stride); + // Small values of interval are equivalent to just sampling next time. + ABSL_ASSERT(next_stride >= 1); + + // g_hashtablez_enabled can be dynamically flipped, we need to set a threshold + // low enough that we will start sampling in a reasonable time, so we just use + // the default sampling rate. + if (!g_hashtablez_enabled.load(std::memory_order_relaxed)) return nullptr; + + // We will only be negative on our first count, so we should just retry in + // that case. + if (first) { + if (ABSL_PREDICT_TRUE(--next_sample.next_sample > 0)) return nullptr; + return SampleSlow(next_sample, inline_element_size, key_size, value_size, + soo_capacity); + } + + return GlobalHashtablezSampler().Register(old_stride, inline_element_size, + key_size, value_size, soo_capacity); +#endif +} + +void UnsampleSlow(HashtablezInfo* info) { + GlobalHashtablezSampler().Unregister(info); +} + +void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { +#ifdef ABSL_INTERNAL_HAVE_SSE2 + total_probe_length /= 16; +#else + total_probe_length /= 8; +#endif + info->total_probe_length.store(total_probe_length, std::memory_order_relaxed); + info->num_erases.store(0, std::memory_order_relaxed); + // There is only one concurrent writer, so `load` then `store` is sufficient + // instead of using `fetch_add`. + info->num_rehashes.store( + 1 + info->num_rehashes.load(std::memory_order_relaxed), + std::memory_order_relaxed); +} + +void RecordReservationSlow(HashtablezInfo* info, size_t target_capacity) { + info->max_reserve.store( + (std::max)(info->max_reserve.load(std::memory_order_relaxed), + target_capacity), + std::memory_order_relaxed); +} + +void RecordClearedReservationSlow(HashtablezInfo* info) { + info->max_reserve.store(0, std::memory_order_relaxed); +} + +void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, + size_t capacity) { + info->size.store(size, std::memory_order_relaxed); + info->capacity.store(capacity, std::memory_order_relaxed); + if (size == 0) { + // This is a clear, reset the total/num_erases too. + info->total_probe_length.store(0, std::memory_order_relaxed); + info->num_erases.store(0, std::memory_order_relaxed); + } +} + +void RecordInsertSlow(HashtablezInfo* info, size_t hash, + size_t distance_from_desired) { + // SwissTables probe in groups of 16, so scale this to count items probes and + // not offset from desired. + size_t probe_length = distance_from_desired; +#ifdef ABSL_INTERNAL_HAVE_SSE2 + probe_length /= 16; +#else + probe_length /= 8; +#endif + + info->hashes_bitwise_and.fetch_and(hash, std::memory_order_relaxed); + info->hashes_bitwise_or.fetch_or(hash, std::memory_order_relaxed); + info->hashes_bitwise_xor.fetch_xor(hash, std::memory_order_relaxed); + info->max_probe_length.store( + std::max(info->max_probe_length.load(std::memory_order_relaxed), + probe_length), + std::memory_order_relaxed); + info->total_probe_length.fetch_add(probe_length, std::memory_order_relaxed); + info->size.fetch_add(1, std::memory_order_relaxed); +} + +void RecordEraseSlow(HashtablezInfo* info) { + info->size.fetch_sub(1, std::memory_order_relaxed); + // There is only one concurrent writer, so `load` then `store` is sufficient + // instead of using `fetch_add`. + info->num_erases.store(1 + info->num_erases.load(std::memory_order_relaxed), + std::memory_order_relaxed); +} + +void SetHashtablezConfigListener(HashtablezConfigListener l) { + g_hashtablez_config_listener.store(l, std::memory_order_release); +} + +bool IsHashtablezEnabled() { + return g_hashtablez_enabled.load(std::memory_order_acquire); +} + +void SetHashtablezEnabled(bool enabled) { + SetHashtablezEnabledInternal(enabled); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezEnabledInternal(bool enabled) { + g_hashtablez_enabled.store(enabled, std::memory_order_release); +} + +int32_t GetHashtablezSampleParameter() { + return g_hashtablez_sample_parameter.load(std::memory_order_acquire); +} + +void SetHashtablezSampleParameter(int32_t rate) { + SetHashtablezSampleParameterInternal(rate); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezSampleParameterInternal(int32_t rate) { + if (rate > 0) { + g_hashtablez_sample_parameter.store(rate, std::memory_order_release); + } else { + ABSL_RAW_LOG(ERROR, "Invalid hashtablez sample rate: %lld", + static_cast(rate)); // NOLINT(runtime/int) + } +} + +size_t GetHashtablezMaxSamples() { + return GlobalHashtablezSampler().GetMaxSamples(); +} + +void SetHashtablezMaxSamples(size_t max) { + SetHashtablezMaxSamplesInternal(max); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezMaxSamplesInternal(size_t max) { + if (max > 0) { + GlobalHashtablezSampler().SetMaxSamples(max); + } else { + ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: 0"); + } +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hashtablez_sampler_force_weak_definition.cc b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hashtablez_sampler_force_weak_definition.cc new file mode 100644 index 00000000..ed35a7ee --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/hashtablez_sampler_force_weak_definition.cc @@ -0,0 +1,31 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/hashtablez_sampler.h" + +#include "absl/base/attributes.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// See hashtablez_sampler.h for details. +extern "C" ABSL_ATTRIBUTE_WEAK bool ABSL_INTERNAL_C_SYMBOL( + AbslContainerInternalSampleEverything)() { + return false; +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/inlined_vector.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/inlined_vector.h new file mode 100644 index 00000000..f871b349 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/inlined_vector.h @@ -0,0 +1,1019 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: inlined_vector.h +// ----------------------------------------------------------------------------- +// +// This header file contains the declaration and definition of an "inlined +// vector" which behaves in an equivalent fashion to a `std::vector`, except +// that storage for small sequences of the vector are provided inline without +// requiring any heap allocation. +// +// An `absl::InlinedVector` specifies the default capacity `N` as one of +// its template parameters. Instances where `size() <= N` hold contained +// elements in inline space. Typically `N` is very small so that sequences that +// are expected to be short do not require allocations. +// +// An `absl::InlinedVector` does not usually require a specific allocator. If +// the inlined vector grows beyond its initial constraints, it will need to +// allocate (as any normal `std::vector` would). This is usually performed with +// the default allocator (defined as `std::allocator`). Optionally, a custom +// allocator type may be specified as `A` in `absl::InlinedVector`. + +#ifndef ABSL_CONTAINER_INLINED_VECTOR_H_ +#define ABSL_CONTAINER_INLINED_VECTOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/algorithm.h" +#include "absl/base/attributes.h" +#include "absl/base/internal/iterator_traits.h" +#include "absl/base/internal/throw_delegate.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" +#include "absl/container/internal/inlined_vector.h" +#include "absl/hash/internal/weakly_mixed_integer.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +// ----------------------------------------------------------------------------- +// InlinedVector +// ----------------------------------------------------------------------------- +// +// An `absl::InlinedVector` is designed to be a drop-in replacement for +// `std::vector` for use cases where the vector's size is sufficiently small +// that it can be inlined. If the inlined vector does grow beyond its estimated +// capacity, it will trigger an initial allocation on the heap, and will behave +// as a `std::vector`. The API of the `absl::InlinedVector` within this file is +// designed to cover the same API footprint as covered by `std::vector`. +template > +class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector { + static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity."); + + using Storage = inlined_vector_internal::Storage; + + template + using AllocatorTraits = inlined_vector_internal::AllocatorTraits; + template + using MoveIterator = inlined_vector_internal::MoveIterator; + template + using IsMoveAssignOk = inlined_vector_internal::IsMoveAssignOk; + + template + using IteratorValueAdapter = + inlined_vector_internal::IteratorValueAdapter; + template + using CopyValueAdapter = inlined_vector_internal::CopyValueAdapter; + template + using DefaultValueAdapter = + inlined_vector_internal::DefaultValueAdapter; + + template + using EnableIfAtLeastForwardIterator = std::enable_if_t< + base_internal::IsAtLeastForwardIterator::value, int>; + template + using DisableIfAtLeastForwardIterator = std::enable_if_t< + !base_internal::IsAtLeastForwardIterator::value, int>; + + using MemcpyPolicy = typename Storage::MemcpyPolicy; + using ElementwiseAssignPolicy = typename Storage::ElementwiseAssignPolicy; + using ElementwiseConstructPolicy = + typename Storage::ElementwiseConstructPolicy; + using MoveAssignmentPolicy = typename Storage::MoveAssignmentPolicy; + + public: + using allocator_type = A; + using value_type = inlined_vector_internal::ValueType; + using pointer = inlined_vector_internal::Pointer; + using const_pointer = inlined_vector_internal::ConstPointer; + using size_type = inlined_vector_internal::SizeType; + using difference_type = inlined_vector_internal::DifferenceType; + using reference = inlined_vector_internal::Reference; + using const_reference = inlined_vector_internal::ConstReference; + using iterator = inlined_vector_internal::Iterator; + using const_iterator = inlined_vector_internal::ConstIterator; + using reverse_iterator = inlined_vector_internal::ReverseIterator; + using const_reverse_iterator = + inlined_vector_internal::ConstReverseIterator; + + // --------------------------------------------------------------------------- + // InlinedVector Constructors and Destructor + // --------------------------------------------------------------------------- + + // Creates an empty inlined vector with a value-initialized allocator. + InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {} + + // Creates an empty inlined vector with a copy of `allocator`. + explicit InlinedVector(const allocator_type& allocator) noexcept + : storage_(allocator) {} + + // Creates an inlined vector with `n` copies of `value_type()`. + explicit InlinedVector(size_type n, + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(DefaultValueAdapter(), n); + } + + // Creates an inlined vector with `n` copies of `v`. + InlinedVector(size_type n, const_reference v, + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(CopyValueAdapter(std::addressof(v)), n); + } + + // Creates an inlined vector with copies of the elements of `list`. + InlinedVector(std::initializer_list list, + const allocator_type& allocator = allocator_type()) + : InlinedVector(list.begin(), list.end(), allocator) {} + + // Creates an inlined vector with elements constructed from the provided + // forward iterator range [`first`, `last`). + // + // NOTE: the `enable_if` prevents ambiguous interpretation between a call to + // this constructor with two integral arguments and a call to the above + // `InlinedVector(size_type, const_reference)` constructor. + template = 0> + InlinedVector(ForwardIterator first, ForwardIterator last, + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(IteratorValueAdapter(first), + static_cast(std::distance(first, last))); + } + + // Creates an inlined vector with elements constructed from the provided input + // iterator range [`first`, `last`). + template = 0> + InlinedVector(InputIterator first, InputIterator last, + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + std::copy(first, last, std::back_inserter(*this)); + } + + // Creates an inlined vector by copying the contents of `other` using + // `other`'s allocator. + InlinedVector(const InlinedVector& other) + : InlinedVector(other, other.storage_.GetAllocator()) {} + + // Creates an inlined vector by copying the contents of `other` using the + // provided `allocator`. + InlinedVector(const InlinedVector& other, const allocator_type& allocator) + : storage_(allocator) { + // Fast path: if the other vector is empty, there's nothing for us to do. + if (other.empty()) { + return; + } + + // Fast path: if the value type is trivially copy constructible, we know the + // allocator doesn't do anything fancy, and there is nothing on the heap + // then we know it is legal for us to simply memcpy the other vector's + // inlined bytes to form our copy of its elements. + if (absl::is_trivially_copy_constructible::value && + std::is_same>::value && + !other.storage_.GetIsAllocated()) { + storage_.MemcpyFrom(other.storage_); + return; + } + + storage_.InitFrom(other.storage_); + } + + // Creates an inlined vector by moving in the contents of `other` without + // allocating. If `other` contains allocated memory, the newly-created inlined + // vector will take ownership of that memory. However, if `other` does not + // contain allocated memory, the newly-created inlined vector will perform + // element-wise move construction of the contents of `other`. + // + // NOTE: since no allocation is performed for the inlined vector in either + // case, the `noexcept(...)` specification depends on whether moving the + // underlying objects can throw. It is assumed assumed that... + // a) move constructors should only throw due to allocation failure. + // b) if `value_type`'s move constructor allocates, it uses the same + // allocation function as the inlined vector's allocator. + // Thus, the move constructor is non-throwing if the allocator is non-throwing + // or `value_type`'s move constructor is specified as `noexcept`. + InlinedVector(InlinedVector&& other) noexcept( + absl::allocator_is_nothrow::value || + std::is_nothrow_move_constructible::value) + : storage_(other.storage_.GetAllocator()) { + // Fast path: if the value type can be trivially relocated (i.e. moved from + // and destroyed), and we know the allocator doesn't do anything fancy, then + // it's safe for us to simply adopt the contents of the storage for `other` + // and remove its own reference to them. It's as if we had individually + // move-constructed each value and then destroyed the original. + if (absl::is_trivially_relocatable::value && + std::is_same>::value) { + storage_.MemcpyFrom(other.storage_); + other.storage_.SetInlinedSize(0); + return; + } + + // Fast path: if the other vector is on the heap, we can simply take over + // its allocation. + if (other.storage_.GetIsAllocated()) { + storage_.SetAllocation({other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()}); + storage_.SetAllocatedSize(other.storage_.GetSize()); + + other.storage_.SetInlinedSize(0); + return; + } + + // Otherwise we must move each element individually. + IteratorValueAdapter> other_values( + MoveIterator(other.storage_.GetInlinedData())); + + inlined_vector_internal::ConstructElements( + storage_.GetAllocator(), storage_.GetInlinedData(), other_values, + other.storage_.GetSize()); + + storage_.SetInlinedSize(other.storage_.GetSize()); + } + + // Creates an inlined vector by moving in the contents of `other` with a copy + // of `allocator`. + // + // NOTE: if `other`'s allocator is not equal to `allocator`, even if `other` + // contains allocated memory, this move constructor will still allocate. Since + // allocation is performed, this constructor can only be `noexcept` if the + // specified allocator is also `noexcept`. + InlinedVector( + InlinedVector&& other, + const allocator_type& + allocator) noexcept(absl::allocator_is_nothrow::value) + : storage_(allocator) { + // Fast path: if the value type can be trivially relocated (i.e. moved from + // and destroyed), and we know the allocator doesn't do anything fancy, then + // it's safe for us to simply adopt the contents of the storage for `other` + // and remove its own reference to them. It's as if we had individually + // move-constructed each value and then destroyed the original. + if (absl::is_trivially_relocatable::value && + std::is_same>::value) { + storage_.MemcpyFrom(other.storage_); + other.storage_.SetInlinedSize(0); + return; + } + + // Fast path: if the other vector is on the heap and shared the same + // allocator, we can simply take over its allocation. + if ((storage_.GetAllocator() == other.storage_.GetAllocator()) && + other.storage_.GetIsAllocated()) { + storage_.SetAllocation({other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()}); + storage_.SetAllocatedSize(other.storage_.GetSize()); + + other.storage_.SetInlinedSize(0); + return; + } + + // Otherwise we must move each element individually. + storage_.Initialize( + IteratorValueAdapter>(MoveIterator(other.data())), + other.size()); + } + + ~InlinedVector() {} + + // --------------------------------------------------------------------------- + // InlinedVector Member Accessors + // --------------------------------------------------------------------------- + + // `InlinedVector::empty()` + // + // Returns whether the inlined vector contains no elements. + bool empty() const noexcept { return !size(); } + + // `InlinedVector::size()` + // + // Returns the number of elements in the inlined vector. + size_type size() const noexcept { return storage_.GetSize(); } + + // `InlinedVector::max_size()` + // + // Returns the maximum number of elements the inlined vector can hold. + size_type max_size() const noexcept { + // One bit of the size storage is used to indicate whether the inlined + // vector contains allocated memory. As a result, the maximum size that the + // inlined vector can express is the minimum of the limit of how many + // objects we can allocate and std::numeric_limits::max() / 2. + return (std::min)(AllocatorTraits::max_size(storage_.GetAllocator()), + (std::numeric_limits::max)() / 2); + } + + // `InlinedVector::capacity()` + // + // Returns the number of elements that could be stored in the inlined vector + // without requiring a reallocation. + // + // NOTE: for most inlined vectors, `capacity()` should be equal to the + // template parameter `N`. For inlined vectors which exceed this capacity, + // they will no longer be inlined and `capacity()` will equal the capactity of + // the allocated memory. + size_type capacity() const noexcept { + return storage_.GetIsAllocated() ? storage_.GetAllocatedCapacity() + : storage_.GetInlinedCapacity(); + } + + // `InlinedVector::data()` + // + // Returns a `pointer` to the elements of the inlined vector. This pointer + // can be used to access and modify the contained elements. + // + // NOTE: only elements within [`data()`, `data() + size()`) are valid. + pointer data() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return storage_.GetIsAllocated() ? storage_.GetAllocatedData() + : storage_.GetInlinedData(); + } + + // Overload of `InlinedVector::data()` that returns a `const_pointer` to the + // elements of the inlined vector. This pointer can be used to access but not + // modify the contained elements. + // + // NOTE: only elements within [`data()`, `data() + size()`) are valid. + const_pointer data() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return storage_.GetIsAllocated() ? storage_.GetAllocatedData() + : storage_.GetInlinedData(); + } + + // `InlinedVector::operator[](...)` + // + // Returns a `reference` to the `i`th element of the inlined vector. + reference operator[](size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(i < size()); + return data()[i]; + } + + // Overload of `InlinedVector::operator[](...)` that returns a + // `const_reference` to the `i`th element of the inlined vector. + const_reference operator[](size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(i < size()); + return data()[i]; + } + + // `InlinedVector::at(...)` + // + // Returns a `reference` to the `i`th element of the inlined vector. + // + // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, + // in both debug and non-debug builds, `std::out_of_range` will be thrown. + reference at(size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange( + "`InlinedVector::at(size_type)` failed bounds check"); + } + return data()[i]; + } + + // Overload of `InlinedVector::at(...)` that returns a `const_reference` to + // the `i`th element of the inlined vector. + // + // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, + // in both debug and non-debug builds, `std::out_of_range` will be thrown. + const_reference at(size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange( + "`InlinedVector::at(size_type) const` failed bounds check"); + } + return data()[i]; + } + + // `InlinedVector::front()` + // + // Returns a `reference` to the first element of the inlined vector. + reference front() ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } + + // Overload of `InlinedVector::front()` that returns a `const_reference` to + // the first element of the inlined vector. + const_reference front() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } + + // `InlinedVector::back()` + // + // Returns a `reference` to the last element of the inlined vector. + reference back() ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } + + // Overload of `InlinedVector::back()` that returns a `const_reference` to the + // last element of the inlined vector. + const_reference back() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } + + // `InlinedVector::begin()` + // + // Returns an `iterator` to the beginning of the inlined vector. + iterator begin() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); } + + // Overload of `InlinedVector::begin()` that returns a `const_iterator` to + // the beginning of the inlined vector. + const_iterator begin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return data(); + } + + // `InlinedVector::end()` + // + // Returns an `iterator` to the end of the inlined vector. + iterator end() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return data() + size(); + } + + // Overload of `InlinedVector::end()` that returns a `const_iterator` to the + // end of the inlined vector. + const_iterator end() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return data() + size(); + } + + // `InlinedVector::cbegin()` + // + // Returns a `const_iterator` to the beginning of the inlined vector. + const_iterator cbegin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return begin(); + } + + // `InlinedVector::cend()` + // + // Returns a `const_iterator` to the end of the inlined vector. + const_iterator cend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return end(); + } + + // `InlinedVector::rbegin()` + // + // Returns a `reverse_iterator` from the end of the inlined vector. + reverse_iterator rbegin() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return reverse_iterator(end()); + } + + // Overload of `InlinedVector::rbegin()` that returns a + // `const_reverse_iterator` from the end of the inlined vector. + const_reverse_iterator rbegin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return const_reverse_iterator(end()); + } + + // `InlinedVector::rend()` + // + // Returns a `reverse_iterator` from the beginning of the inlined vector. + reverse_iterator rend() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return reverse_iterator(begin()); + } + + // Overload of `InlinedVector::rend()` that returns a `const_reverse_iterator` + // from the beginning of the inlined vector. + const_reverse_iterator rend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return const_reverse_iterator(begin()); + } + + // `InlinedVector::crbegin()` + // + // Returns a `const_reverse_iterator` from the end of the inlined vector. + const_reverse_iterator crbegin() const noexcept + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return rbegin(); + } + + // `InlinedVector::crend()` + // + // Returns a `const_reverse_iterator` from the beginning of the inlined + // vector. + const_reverse_iterator crend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { + return rend(); + } + + // `InlinedVector::get_allocator()` + // + // Returns a copy of the inlined vector's allocator. + allocator_type get_allocator() const { return storage_.GetAllocator(); } + + // --------------------------------------------------------------------------- + // InlinedVector Member Mutators + // --------------------------------------------------------------------------- + + // `InlinedVector::operator=(...)` + // + // Replaces the elements of the inlined vector with copies of the elements of + // `list`. + InlinedVector& operator=(std::initializer_list list) { + assign(list.begin(), list.end()); + + return *this; + } + + // Overload of `InlinedVector::operator=(...)` that replaces the elements of + // the inlined vector with copies of the elements of `other`. + InlinedVector& operator=(const InlinedVector& other) { + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + const_pointer other_data = other.data(); + assign(other_data, other_data + other.size()); + } + + return *this; + } + + // Overload of `InlinedVector::operator=(...)` that moves the elements of + // `other` into the inlined vector. + // + // NOTE: as a result of calling this overload, `other` is left in a valid but + // unspecified state. + InlinedVector& operator=(InlinedVector&& other) { + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + MoveAssignment(MoveAssignmentPolicy{}, std::move(other)); + } + + return *this; + } + + // `InlinedVector::assign(...)` + // + // Replaces the contents of the inlined vector with `n` copies of `v`. + void assign(size_type n, const_reference v) { + storage_.Assign(CopyValueAdapter(std::addressof(v)), n); + } + + // Overload of `InlinedVector::assign(...)` that replaces the contents of the + // inlined vector with copies of the elements of `list`. + void assign(std::initializer_list list) { + assign(list.begin(), list.end()); + } + + // Overload of `InlinedVector::assign(...)` to replace the contents of the + // inlined vector with the range [`first`, `last`). + // + // NOTE: this overload is for iterators that are "forward" category or better. + template = 0> + void assign(ForwardIterator first, ForwardIterator last) { + storage_.Assign(IteratorValueAdapter(first), + static_cast(std::distance(first, last))); + } + + // Overload of `InlinedVector::assign(...)` to replace the contents of the + // inlined vector with the range [`first`, `last`). + // + // NOTE: this overload is for iterators that are "input" category. + template = 0> + void assign(InputIterator first, InputIterator last) { + size_type i = 0; + for (; i < size() && first != last; ++i, static_cast(++first)) { + data()[i] = *first; + } + + erase(data() + i, data() + size()); + std::copy(first, last, std::back_inserter(*this)); + } + + // `InlinedVector::resize(...)` + // + // Resizes the inlined vector to contain `n` elements. + // + // NOTE: If `n` is smaller than `size()`, extra elements are destroyed. If `n` + // is larger than `size()`, new elements are value-initialized. + void resize(size_type n) { + ABSL_HARDENING_ASSERT(n <= max_size()); + storage_.Resize(DefaultValueAdapter(), n); + } + + // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to + // contain `n` elements. + // + // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` + // is larger than `size()`, new elements are copied-constructed from `v`. + void resize(size_type n, const_reference v) { + ABSL_HARDENING_ASSERT(n <= max_size()); + storage_.Resize(CopyValueAdapter(std::addressof(v)), n); + } + + // `InlinedVector::insert(...)` + // + // Inserts a copy of `v` at `pos`, returning an `iterator` to the newly + // inserted element. + iterator insert(const_iterator pos, + const_reference v) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return emplace(pos, v); + } + + // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using + // move semantics, returning an `iterator` to the newly inserted element. + iterator insert(const_iterator pos, + value_type&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return emplace(pos, std::move(v)); + } + + // Overload of `InlinedVector::insert(...)` that inserts `n` contiguous copies + // of `v` starting at `pos`, returning an `iterator` pointing to the first of + // the newly inserted elements. + iterator insert(const_iterator pos, size_type n, + const_reference v) ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); + + if (ABSL_PREDICT_TRUE(n != 0)) { + value_type dealias = v; + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2 + // It appears that GCC thinks that since `pos` is a const pointer and may + // point to uninitialized memory at this point, a warning should be + // issued. But `pos` is actually only used to compute an array index to + // write to. +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + return storage_.Insert(pos, CopyValueAdapter(std::addressof(dealias)), + n); +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + } else { + return const_cast(pos); + } + } + + // Overload of `InlinedVector::insert(...)` that inserts copies of the + // elements of `list` starting at `pos`, returning an `iterator` pointing to + // the first of the newly inserted elements. + iterator insert(const_iterator pos, std::initializer_list list) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return insert(pos, list.begin(), list.end()); + } + + // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, + // `last`) starting at `pos`, returning an `iterator` pointing to the first + // of the newly inserted elements. + // + // NOTE: this overload is for iterators that are "forward" category or better. + template = 0> + iterator insert(const_iterator pos, ForwardIterator first, + ForwardIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); + + if (ABSL_PREDICT_TRUE(first != last)) { + return storage_.Insert( + pos, IteratorValueAdapter(first), + static_cast(std::distance(first, last))); + } else { + return const_cast(pos); + } + } + + // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, + // `last`) starting at `pos`, returning an `iterator` pointing to the first + // of the newly inserted elements. + // + // NOTE: this overload is for iterators that are "input" category. + template = 0> + iterator insert(const_iterator pos, InputIterator first, + InputIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); + + size_type index = static_cast(std::distance(cbegin(), pos)); + for (size_type i = index; first != last; ++i, static_cast(++first)) { + insert(data() + i, *first); + } + + return iterator(data() + index); + } + + // `InlinedVector::emplace(...)` + // + // Constructs and inserts an element using `args...` in the inlined vector at + // `pos`, returning an `iterator` pointing to the newly emplaced element. + template + iterator emplace(const_iterator pos, + Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); + + value_type dealias(std::forward(args)...); + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2 + // It appears that GCC thinks that since `pos` is a const pointer and may + // point to uninitialized memory at this point, a warning should be + // issued. But `pos` is actually only used to compute an array index to + // write to. +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + return storage_.Insert(pos, + IteratorValueAdapter>( + MoveIterator(std::addressof(dealias))), + 1); +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + } + + // `InlinedVector::emplace_back(...)` + // + // Constructs and inserts an element using `args...` in the inlined vector at + // `end()`, returning a `reference` to the newly emplaced element. + template + reference emplace_back(Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return storage_.EmplaceBack(std::forward(args)...); + } + + // `InlinedVector::push_back(...)` + // + // Inserts a copy of `v` in the inlined vector at `end()`. + void push_back(const_reference v) { static_cast(emplace_back(v)); } + + // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()` + // using move semantics. + void push_back(value_type&& v) { + static_cast(emplace_back(std::move(v))); + } + + // `InlinedVector::pop_back()` + // + // Destroys the element at `back()`, reducing the size by `1`. + void pop_back() noexcept { + ABSL_HARDENING_ASSERT(!empty()); + + AllocatorTraits::destroy(storage_.GetAllocator(), data() + (size() - 1)); + storage_.SubtractSize(1); + } + + // `InlinedVector::erase(...)` + // + // Erases the element at `pos`, returning an `iterator` pointing to where the + // erased element was located. + // + // NOTE: may return `end()`, which is not dereferenceable. + iterator erase(const_iterator pos) ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos < end()); + + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2 + // It appears that GCC thinks that since `pos` is a const pointer and may + // point to uninitialized memory at this point, a warning should be + // issued. But `pos` is actually only used to compute an array index to + // write to. +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#pragma GCC diagnostic ignored "-Wuninitialized" +#endif + return storage_.Erase(pos, pos + 1); +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + } + + // Overload of `InlinedVector::erase(...)` that erases every element in the + // range [`from`, `to`), returning an `iterator` pointing to where the first + // erased element was located. + // + // NOTE: may return `end()`, which is not dereferenceable. + iterator erase(const_iterator from, + const_iterator to) ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_HARDENING_ASSERT(from >= begin()); + ABSL_HARDENING_ASSERT(from <= to); + ABSL_HARDENING_ASSERT(to <= end()); + + if (ABSL_PREDICT_TRUE(from != to)) { + return storage_.Erase(from, to); + } else { + return const_cast(from); + } + } + + // `InlinedVector::clear()` + // + // Destroys all elements in the inlined vector, setting the size to `0` and + // deallocating any held memory. + void clear() noexcept { + inlined_vector_internal::DestroyAdapter::DestroyElements( + storage_.GetAllocator(), data(), size()); + storage_.DeallocateIfAllocated(); + + storage_.SetInlinedSize(0); + } + + // `InlinedVector::reserve(...)` + // + // Ensures that there is enough room for at least `n` elements. + void reserve(size_type n) { storage_.Reserve(n); } + + // `InlinedVector::shrink_to_fit()` + // + // Attempts to reduce memory usage by moving elements to (or keeping elements + // in) the smallest available buffer sufficient for containing `size()` + // elements. + // + // If `size()` is sufficiently small, the elements will be moved into (or kept + // in) the inlined space. + void shrink_to_fit() { + if (storage_.GetIsAllocated()) { + storage_.ShrinkToFit(); + } + } + + // `InlinedVector::swap(...)` + // + // Swaps the contents of the inlined vector with `other`. + void swap(InlinedVector& other) { + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + storage_.Swap(std::addressof(other.storage_)); + } + } + + private: + template + friend H AbslHashValue(H h, const absl::InlinedVector& a); + + void MoveAssignment(MemcpyPolicy, InlinedVector&& other) { + // Assumption check: we shouldn't be told to use memcpy to implement move + // assignment unless we have trivially destructible elements and an + // allocator that does nothing fancy. + static_assert(absl::is_trivially_destructible::value, ""); + static_assert(std::is_same>::value, ""); + + // Throw away our existing heap allocation, if any. There is no need to + // destroy the existing elements one by one because we know they are + // trivially destructible. + storage_.DeallocateIfAllocated(); + + // Adopt the other vector's inline elements or heap allocation. + storage_.MemcpyFrom(other.storage_); + other.storage_.SetInlinedSize(0); + } + + // Destroy our existing elements, if any, and adopt the heap-allocated + // elements of the other vector. + // + // REQUIRES: other.storage_.GetIsAllocated() + void DestroyExistingAndAdopt(InlinedVector&& other) { + ABSL_HARDENING_ASSERT(other.storage_.GetIsAllocated()); + + inlined_vector_internal::DestroyAdapter::DestroyElements( + storage_.GetAllocator(), data(), size()); + storage_.DeallocateIfAllocated(); + + storage_.MemcpyFrom(other.storage_); + other.storage_.SetInlinedSize(0); + } + + void MoveAssignment(ElementwiseAssignPolicy, InlinedVector&& other) { + // Fast path: if the other vector is on the heap then we don't worry about + // actually move-assigning each element. Instead we only throw away our own + // existing elements and adopt the heap allocation of the other vector. + if (other.storage_.GetIsAllocated()) { + DestroyExistingAndAdopt(std::move(other)); + return; + } + + storage_.Assign(IteratorValueAdapter>( + MoveIterator(other.storage_.GetInlinedData())), + other.size()); + } + + void MoveAssignment(ElementwiseConstructPolicy, InlinedVector&& other) { + // Fast path: if the other vector is on the heap then we don't worry about + // actually move-assigning each element. Instead we only throw away our own + // existing elements and adopt the heap allocation of the other vector. + if (other.storage_.GetIsAllocated()) { + DestroyExistingAndAdopt(std::move(other)); + return; + } + + inlined_vector_internal::DestroyAdapter::DestroyElements( + storage_.GetAllocator(), data(), size()); + storage_.DeallocateIfAllocated(); + + IteratorValueAdapter> other_values( + MoveIterator(other.storage_.GetInlinedData())); + inlined_vector_internal::ConstructElements( + storage_.GetAllocator(), storage_.GetInlinedData(), other_values, + other.storage_.GetSize()); + storage_.SetInlinedSize(other.storage_.GetSize()); + } + + Storage storage_; +}; + +// ----------------------------------------------------------------------------- +// InlinedVector Non-Member Functions +// ----------------------------------------------------------------------------- + +// `swap(...)` +// +// Swaps the contents of two inlined vectors. +template +void swap(absl::InlinedVector& a, + absl::InlinedVector& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); +} + +// `operator==(...)` +// +// Tests for value-equality of two inlined vectors. +template +bool operator==(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + auto a_data = a.data(); + auto b_data = b.data(); + return std::equal(a_data, a_data + a.size(), b_data, b_data + b.size()); +} + +// `operator!=(...)` +// +// Tests for value-inequality of two inlined vectors. +template +bool operator!=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return !(a == b); +} + +// `operator<(...)` +// +// Tests whether the value of an inlined vector is less than the value of +// another inlined vector using a lexicographical comparison algorithm. +template +bool operator<(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + auto a_data = a.data(); + auto b_data = b.data(); + return std::lexicographical_compare(a_data, a_data + a.size(), b_data, + b_data + b.size()); +} + +// `operator>(...)` +// +// Tests whether the value of an inlined vector is greater than the value of +// another inlined vector using a lexicographical comparison algorithm. +template +bool operator>(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return b < a; +} + +// `operator<=(...)` +// +// Tests whether the value of an inlined vector is less than or equal to the +// value of another inlined vector using a lexicographical comparison algorithm. +template +bool operator<=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return !(b < a); +} + +// `operator>=(...)` +// +// Tests whether the value of an inlined vector is greater than or equal to the +// value of another inlined vector using a lexicographical comparison algorithm. +template +bool operator>=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return !(a < b); +} + +// `AbslHashValue(...)` +// +// Provides `absl::Hash` support for `absl::InlinedVector`. It is uncommon to +// call this directly. +template +H AbslHashValue(H h, const absl::InlinedVector& a) { + auto size = a.size(); + return H::combine(H::combine_contiguous(std::move(h), a.data(), size), + hash_internal::WeaklyMixedInteger{size}); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INLINED_VECTOR_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/btree.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/btree.h new file mode 100644 index 00000000..ed541e75 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/btree.h @@ -0,0 +1,3149 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// A btree implementation of the STL set and map interfaces. A btree is smaller +// and generally also faster than STL set/map (refer to the benchmarks below). +// The red-black tree implementation of STL set/map has an overhead of 3 +// pointers (left, right and parent) plus the node color information for each +// stored value. So a set consumes 40 bytes for each value stored in +// 64-bit mode. This btree implementation stores multiple values on fixed +// size nodes (usually 256 bytes) and doesn't store child pointers for leaf +// nodes. The result is that a btree_set may use much less memory per +// stored value. For the random insertion benchmark in btree_bench.cc, a +// btree_set with node-size of 256 uses 5.1 bytes per stored value. +// +// The packing of multiple values on to each node of a btree has another effect +// besides better space utilization: better cache locality due to fewer cache +// lines being accessed. Better cache locality translates into faster +// operations. +// +// CAVEATS +// +// Insertions and deletions on a btree can cause splitting, merging or +// rebalancing of btree nodes. And even without these operations, insertions +// and deletions on a btree will move values around within a node. In both +// cases, the result is that insertions and deletions can invalidate iterators +// pointing to values other than the one being inserted/deleted. Therefore, this +// container does not provide pointer stability. This is notably different from +// STL set/map which takes care to not invalidate iterators on insert/erase +// except, of course, for iterators pointing to the value being erased. A +// partial workaround when erasing is available: erase() returns an iterator +// pointing to the item just after the one that was erased (or end() if none +// exists). + +#ifndef ABSL_CONTAINER_INTERNAL_BTREE_H_ +#define ABSL_CONTAINER_INTERNAL_BTREE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/container/internal/common.h" +#include "absl/container/internal/common_policy_traits.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/layout.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/cord.h" +#include "absl/strings/string_view.h" +#include "absl/types/compare.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +#ifdef ABSL_BTREE_ENABLE_GENERATIONS +#error ABSL_BTREE_ENABLE_GENERATIONS cannot be directly set +#elif (defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_HWADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_MEMORY_SANITIZER)) && \ + !defined(NDEBUG_SANITIZER) // If defined, performance is important. +// When compiled in sanitizer mode, we add generation integers to the nodes and +// iterators. When iterators are used, we validate that the container has not +// been mutated since the iterator was constructed. +#define ABSL_BTREE_ENABLE_GENERATIONS +#endif + +#ifdef ABSL_BTREE_ENABLE_GENERATIONS +constexpr bool BtreeGenerationsEnabled() { return true; } +#else +constexpr bool BtreeGenerationsEnabled() { return false; } +#endif + +template +using compare_result_t = absl::result_of_t; + +// A helper class that indicates if the Compare parameter is a key-compare-to +// comparator. +template +using btree_is_key_compare_to = + std::is_convertible, absl::weak_ordering>; + +struct StringBtreeDefaultLess { + using is_transparent = void; + + StringBtreeDefaultLess() = default; + + // Compatibility constructor. + StringBtreeDefaultLess(std::less) {} // NOLINT + StringBtreeDefaultLess(std::less) {} // NOLINT + + // Allow converting to std::less for use in key_comp()/value_comp(). + explicit operator std::less() const { return {}; } + explicit operator std::less() const { return {}; } + explicit operator std::less() const { return {}; } + + absl::weak_ordering operator()(absl::string_view lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(lhs.compare(rhs)); + } + StringBtreeDefaultLess(std::less) {} // NOLINT + absl::weak_ordering operator()(const absl::Cord &lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(lhs.Compare(rhs)); + } + absl::weak_ordering operator()(const absl::Cord &lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(lhs.Compare(rhs)); + } + absl::weak_ordering operator()(absl::string_view lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(-rhs.Compare(lhs)); + } +}; + +struct StringBtreeDefaultGreater { + using is_transparent = void; + + StringBtreeDefaultGreater() = default; + + StringBtreeDefaultGreater(std::greater) {} // NOLINT + StringBtreeDefaultGreater(std::greater) {} // NOLINT + + // Allow converting to std::greater for use in key_comp()/value_comp(). + explicit operator std::greater() const { return {}; } + explicit operator std::greater() const { return {}; } + explicit operator std::greater() const { return {}; } + + absl::weak_ordering operator()(absl::string_view lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(rhs.compare(lhs)); + } + StringBtreeDefaultGreater(std::greater) {} // NOLINT + absl::weak_ordering operator()(const absl::Cord &lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(rhs.Compare(lhs)); + } + absl::weak_ordering operator()(const absl::Cord &lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(-lhs.Compare(rhs)); + } + absl::weak_ordering operator()(absl::string_view lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(rhs.Compare(lhs)); + } +}; + +// See below comments for checked_compare. +template ::value> +struct checked_compare_base : Compare { + using Compare::Compare; + explicit checked_compare_base(Compare c) : Compare(std::move(c)) {} + const Compare &comp() const { return *this; } +}; +template +struct checked_compare_base { + explicit checked_compare_base(Compare c) : compare(std::move(c)) {} + const Compare &comp() const { return compare; } + Compare compare; +}; + +// A mechanism for opting out of checked_compare for use only in btree_test.cc. +struct BtreeTestOnlyCheckedCompareOptOutBase {}; + +// A helper class to adapt the specified comparator for two use cases: +// (1) When using common Abseil string types with common comparison functors, +// convert a boolean comparison into a three-way comparison that returns an +// `absl::weak_ordering`. This helper class is specialized for +// less, greater, less, +// greater, less, and greater. +// (2) Adapt the comparator to diagnose cases of non-strict-weak-ordering (see +// https://en.cppreference.com/w/cpp/named_req/Compare) in debug mode. Whenever +// a comparison is made, we will make assertions to verify that the comparator +// is valid. +template +struct key_compare_adapter { + // Inherit from checked_compare_base to support function pointers and also + // keep empty-base-optimization (EBO) support for classes. + // Note: we can't use CompressedTuple here because that would interfere + // with the EBO for `btree::rightmost_`. `btree::rightmost_` is itself a + // CompressedTuple and nested `CompressedTuple`s don't support EBO. + // TODO(b/214288561): use CompressedTuple instead once it supports EBO for + // nested `CompressedTuple`s. + struct checked_compare : checked_compare_base { + private: + using Base = typename checked_compare::checked_compare_base; + using Base::comp; + + // If possible, returns whether `t` is equivalent to itself. We can only do + // this for `Key`s because we can't be sure that it's safe to call + // `comp()(k, k)` otherwise. Even if SFINAE allows it, there could be a + // compilation failure inside the implementation of the comparison operator. + bool is_self_equivalent(const Key &k) const { + // Note: this works for both boolean and three-way comparators. + return comp()(k, k) == 0; + } + // If we can't compare `t` with itself, returns true unconditionally. + template + bool is_self_equivalent(const T &) const { + return true; + } + + public: + using Base::Base; + checked_compare(Compare cmp) : Base(std::move(cmp)) {} // NOLINT + + // Allow converting to Compare for use in key_comp()/value_comp(). + explicit operator Compare() const { return comp(); } + + template >::value, + int> = 0> + bool operator()(const T &lhs, const U &rhs) const { + // NOTE: if any of these assertions fail, then the comparator does not + // establish a strict-weak-ordering (see + // https://en.cppreference.com/w/cpp/named_req/Compare). + assert(is_self_equivalent(lhs)); + assert(is_self_equivalent(rhs)); + const bool lhs_comp_rhs = comp()(lhs, rhs); + assert(!lhs_comp_rhs || !comp()(rhs, lhs)); + return lhs_comp_rhs; + } + + template < + typename T, typename U, + absl::enable_if_t, + absl::weak_ordering>::value, + int> = 0> + absl::weak_ordering operator()(const T &lhs, const U &rhs) const { + // NOTE: if any of these assertions fail, then the comparator does not + // establish a strict-weak-ordering (see + // https://en.cppreference.com/w/cpp/named_req/Compare). + assert(is_self_equivalent(lhs)); + assert(is_self_equivalent(rhs)); + const absl::weak_ordering lhs_comp_rhs = comp()(lhs, rhs); +#ifndef NDEBUG + const absl::weak_ordering rhs_comp_lhs = comp()(rhs, lhs); + if (lhs_comp_rhs > 0) { + assert(rhs_comp_lhs < 0 && "lhs_comp_rhs > 0 -> rhs_comp_lhs < 0"); + } else if (lhs_comp_rhs == 0) { + assert(rhs_comp_lhs == 0 && "lhs_comp_rhs == 0 -> rhs_comp_lhs == 0"); + } else { + assert(rhs_comp_lhs > 0 && "lhs_comp_rhs < 0 -> rhs_comp_lhs > 0"); + } +#endif + return lhs_comp_rhs; + } + }; + using type = absl::conditional_t< + std::is_base_of::value, + Compare, checked_compare>; +}; + +template <> +struct key_compare_adapter, std::string> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_adapter, std::string> { + using type = StringBtreeDefaultGreater; +}; + +template <> +struct key_compare_adapter, absl::string_view> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_adapter, absl::string_view> { + using type = StringBtreeDefaultGreater; +}; + +template <> +struct key_compare_adapter, absl::Cord> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_adapter, absl::Cord> { + using type = StringBtreeDefaultGreater; +}; + +// Detects an 'absl_btree_prefer_linear_node_search' member. This is +// a protocol used as an opt-in or opt-out of linear search. +// +// For example, this would be useful for key types that wrap an integer +// and define their own cheap operator<(). For example: +// +// class K { +// public: +// using absl_btree_prefer_linear_node_search = std::true_type; +// ... +// private: +// friend bool operator<(K a, K b) { return a.k_ < b.k_; } +// int k_; +// }; +// +// btree_map m; // Uses linear search +// +// If T has the preference tag, then it has a preference. +// Btree will use the tag's truth value. +template +struct has_linear_node_search_preference : std::false_type {}; +template +struct prefers_linear_node_search : std::false_type {}; +template +struct has_linear_node_search_preference< + T, absl::void_t> + : std::true_type {}; +template +struct prefers_linear_node_search< + T, absl::void_t> + : T::absl_btree_prefer_linear_node_search {}; + +template +constexpr bool compare_has_valid_result_type() { + using compare_result_type = compare_result_t; + return std::is_same::value || + std::is_convertible::value; +} + +template +class map_value_compare { + template + friend class btree; + + // Note: this `protected` is part of the API of std::map::value_compare. See + // https://en.cppreference.com/w/cpp/container/map/value_compare. + protected: + explicit map_value_compare(original_key_compare c) : comp(std::move(c)) {} + + original_key_compare comp; // NOLINT + + public: + auto operator()(const value_type &lhs, const value_type &rhs) const + -> decltype(comp(lhs.first, rhs.first)) { + return comp(lhs.first, rhs.first); + } +}; + +template +struct common_params : common_policy_traits { + using original_key_compare = Compare; + + // If Compare is a common comparator for a string-like type, then we adapt it + // to use heterogeneous lookup and to be a key-compare-to comparator. + // We also adapt the comparator to diagnose invalid comparators in debug mode. + // We disable this when `Compare` is invalid in a way that will cause + // adaptation to fail (having invalid return type) so that we can give a + // better compilation failure in static_assert_validation. If we don't do + // this, then there will be cascading compilation failures that are confusing + // for users. + using key_compare = + absl::conditional_t(), + Compare, + typename key_compare_adapter::type>; + + static constexpr bool kIsKeyCompareStringAdapted = + std::is_same::value || + std::is_same::value; + static constexpr bool kIsKeyCompareTransparent = + IsTransparent::value || kIsKeyCompareStringAdapted; + + // A type which indicates if we have a key-compare-to functor or a plain old + // key-compare functor. + using is_key_compare_to = btree_is_key_compare_to; + + using allocator_type = Alloc; + using key_type = Key; + using size_type = size_t; + using difference_type = ptrdiff_t; + + using slot_policy = SlotPolicy; + using slot_type = typename slot_policy::slot_type; + using value_type = typename slot_policy::value_type; + using init_type = typename slot_policy::mutable_value_type; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + + using value_compare = + absl::conditional_t, + original_key_compare>; + using is_map_container = std::integral_constant; + + // For the given lookup key type, returns whether we can have multiple + // equivalent keys in the btree. If this is a multi-container, then we can. + // Otherwise, we can have multiple equivalent keys only if all of the + // following conditions are met: + // - The comparator is transparent. + // - The lookup key type is not the same as key_type. + // - The comparator is not a StringBtreeDefault{Less,Greater} comparator + // that we know has the same equivalence classes for all lookup types. + template + constexpr static bool can_have_multiple_equivalent_keys() { + return IsMulti || (IsTransparent::value && + !std::is_same::value && + !kIsKeyCompareStringAdapted); + } + + enum { + kTargetNodeSize = TargetNodeSize, + + // Upper bound for the available space for slots. This is largest for leaf + // nodes, which have overhead of at least a pointer + 4 bytes (for storing + // 3 field_types and an enum). + kNodeSlotSpace = TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4), + }; + + // This is an integral type large enough to hold as many slots as will fit a + // node of TargetNodeSize bytes. + using node_count_type = + absl::conditional_t<(kNodeSlotSpace / sizeof(slot_type) > + (std::numeric_limits::max)()), + uint16_t, uint8_t>; // NOLINT +}; + +// An adapter class that converts a lower-bound compare into an upper-bound +// compare. Note: there is no need to make a version of this adapter specialized +// for key-compare-to functors because the upper-bound (the first value greater +// than the input) is never an exact match. +template +struct upper_bound_adapter { + explicit upper_bound_adapter(const Compare &c) : comp(c) {} + template + bool operator()(const K1 &a, const K2 &b) const { + // Returns true when a is not greater than b. + return !compare_internal::compare_result_as_less_than(comp(b, a)); + } + + private: + Compare comp; +}; + +enum class MatchKind : uint8_t { kEq, kNe }; + +template +struct SearchResult { + V value; + MatchKind match; + + static constexpr bool HasMatch() { return true; } + bool IsEq() const { return match == MatchKind::kEq; } +}; + +// When we don't use CompareTo, `match` is not present. +// This ensures that callers can't use it accidentally when it provides no +// useful information. +template +struct SearchResult { + SearchResult() = default; + explicit SearchResult(V v) : value(v) {} + SearchResult(V v, MatchKind /*match*/) : value(v) {} + + V value; + + static constexpr bool HasMatch() { return false; } + static constexpr bool IsEq() { return false; } +}; + +// A node in the btree holding. The same node type is used for both internal +// and leaf nodes in the btree, though the nodes are allocated in such a way +// that the children array is only valid in internal nodes. +template +class btree_node { + using is_key_compare_to = typename Params::is_key_compare_to; + using field_type = typename Params::node_count_type; + using allocator_type = typename Params::allocator_type; + using slot_type = typename Params::slot_type; + using original_key_compare = typename Params::original_key_compare; + + public: + using params_type = Params; + using key_type = typename Params::key_type; + using value_type = typename Params::value_type; + using pointer = typename Params::pointer; + using const_pointer = typename Params::const_pointer; + using reference = typename Params::reference; + using const_reference = typename Params::const_reference; + using key_compare = typename Params::key_compare; + using size_type = typename Params::size_type; + using difference_type = typename Params::difference_type; + + // Btree decides whether to use linear node search as follows: + // - If the comparator expresses a preference, use that. + // - If the key expresses a preference, use that. + // - If the key is arithmetic and the comparator is std::less or + // std::greater, choose linear. + // - Otherwise, choose binary. + // TODO(ezb): Might make sense to add condition(s) based on node-size. + using use_linear_search = std::integral_constant< + bool, has_linear_node_search_preference::value + ? prefers_linear_node_search::value + : has_linear_node_search_preference::value + ? prefers_linear_node_search::value + : std::is_arithmetic::value && + (std::is_same, + original_key_compare>::value || + std::is_same, + original_key_compare>::value)>; + + // This class is organized by absl::container_internal::Layout as if it had + // the following structure: + // // A pointer to the node's parent. + // btree_node *parent; + // + // // When ABSL_BTREE_ENABLE_GENERATIONS is defined, we also have a + // // generation integer in order to check that when iterators are + // // used, they haven't been invalidated already. Only the generation on + // // the root is used, but we have one on each node because whether a node + // // is root or not can change. + // uint32_t generation; + // + // // The position of the node in the node's parent. + // field_type position; + // // The index of the first populated value in `values`. + // // TODO(ezb): right now, `start` is always 0. Update insertion/merge + // // logic to allow for floating storage within nodes. + // field_type start; + // // The index after the last populated value in `values`. Currently, this + // // is the same as the count of values. + // field_type finish; + // // The maximum number of values the node can hold. This is an integer in + // // [1, kNodeSlots] for root leaf nodes, kNodeSlots for non-root leaf + // // nodes, and kInternalNodeMaxCount (as a sentinel value) for internal + // // nodes (even though there are still kNodeSlots values in the node). + // // TODO(ezb): make max_count use only 4 bits and record log2(capacity) + // // to free extra bits for is_root, etc. + // field_type max_count; + // + // // The array of values. The capacity is `max_count` for leaf nodes and + // // kNodeSlots for internal nodes. Only the values in + // // [start, finish) have been initialized and are valid. + // slot_type values[max_count]; + // + // // The array of child pointers. The keys in children[i] are all less + // // than key(i). The keys in children[i + 1] are all greater than key(i). + // // There are 0 children for leaf nodes and kNodeSlots + 1 children for + // // internal nodes. + // btree_node *children[kNodeSlots + 1]; + // + // This class is only constructed by EmptyNodeType. Normally, pointers to the + // layout above are allocated, cast to btree_node*, and de-allocated within + // the btree implementation. + ~btree_node() = default; + btree_node(btree_node const &) = delete; + btree_node &operator=(btree_node const &) = delete; + + protected: + btree_node() = default; + + private: + using layout_type = + absl::container_internal::Layout; + using leaf_layout_type = typename layout_type::template WithStaticSizes< + /*parent*/ 1, + /*generation*/ BtreeGenerationsEnabled() ? 1 : 0, + /*position, start, finish, max_count*/ 4>; + constexpr static size_type SizeWithNSlots(size_type n) { + return leaf_layout_type(/*slots*/ n, /*children*/ 0).AllocSize(); + } + // A lower bound for the overhead of fields other than slots in a leaf node. + constexpr static size_type MinimumOverhead() { + return SizeWithNSlots(1) - sizeof(slot_type); + } + + // Compute how many values we can fit onto a leaf node taking into account + // padding. + constexpr static size_type NodeTargetSlots(const size_type begin, + const size_type end) { + return begin == end ? begin + : SizeWithNSlots((begin + end) / 2 + 1) > + params_type::kTargetNodeSize + ? NodeTargetSlots(begin, (begin + end) / 2) + : NodeTargetSlots((begin + end) / 2 + 1, end); + } + + constexpr static size_type kTargetNodeSize = params_type::kTargetNodeSize; + constexpr static size_type kNodeTargetSlots = + NodeTargetSlots(0, kTargetNodeSize); + + // We need a minimum of 3 slots per internal node in order to perform + // splitting (1 value for the two nodes involved in the split and 1 value + // propagated to the parent as the delimiter for the split). For performance + // reasons, we don't allow 3 slots-per-node due to bad worst case occupancy of + // 1/3 (for a node, not a b-tree). + constexpr static size_type kMinNodeSlots = 4; + + constexpr static size_type kNodeSlots = + kNodeTargetSlots >= kMinNodeSlots ? kNodeTargetSlots : kMinNodeSlots; + + using internal_layout_type = typename layout_type::template WithStaticSizes< + /*parent*/ 1, + /*generation*/ BtreeGenerationsEnabled() ? 1 : 0, + /*position, start, finish, max_count*/ 4, /*slots*/ kNodeSlots, + /*children*/ kNodeSlots + 1>; + + // The node is internal (i.e. is not a leaf node) if and only if `max_count` + // has this value. + constexpr static field_type kInternalNodeMaxCount = 0; + + // Leaves can have less than kNodeSlots values. + constexpr static leaf_layout_type LeafLayout( + const size_type slot_count = kNodeSlots) { + return leaf_layout_type(slot_count, 0); + } + constexpr static auto InternalLayout() { return internal_layout_type(); } + constexpr static size_type LeafSize(const size_type slot_count = kNodeSlots) { + return LeafLayout(slot_count).AllocSize(); + } + constexpr static size_type InternalSize() { + return InternalLayout().AllocSize(); + } + + constexpr static size_type Alignment() { + static_assert(LeafLayout(1).Alignment() == InternalLayout().Alignment(), + "Alignment of all nodes must be equal."); + return InternalLayout().Alignment(); + } + + // N is the index of the type in the Layout definition. + // ElementType is the Nth type in the Layout definition. + template + inline typename layout_type::template ElementType *GetField() { + // We assert that we don't read from values that aren't there. + assert(N < 4 || is_internal()); + return InternalLayout().template Pointer(reinterpret_cast(this)); + } + template + inline const typename layout_type::template ElementType *GetField() const { + assert(N < 4 || is_internal()); + return InternalLayout().template Pointer( + reinterpret_cast(this)); + } + void set_parent(btree_node *p) { *GetField<0>() = p; } + field_type &mutable_finish() { return GetField<2>()[2]; } + slot_type *slot(size_type i) { return &GetField<3>()[i]; } + slot_type *start_slot() { return slot(start()); } + slot_type *finish_slot() { return slot(finish()); } + const slot_type *slot(size_type i) const { return &GetField<3>()[i]; } + void set_position(field_type v) { GetField<2>()[0] = v; } + void set_start(field_type v) { GetField<2>()[1] = v; } + void set_finish(field_type v) { GetField<2>()[2] = v; } + // This method is only called by the node init methods. + void set_max_count(field_type v) { GetField<2>()[3] = v; } + + public: + // Whether this is a leaf node or not. This value doesn't change after the + // node is created. + bool is_leaf() const { return GetField<2>()[3] != kInternalNodeMaxCount; } + // Whether this is an internal node or not. This value doesn't change after + // the node is created. + bool is_internal() const { return !is_leaf(); } + + // Getter for the position of this node in its parent. + field_type position() const { return GetField<2>()[0]; } + + // Getter for the offset of the first value in the `values` array. + field_type start() const { + // TODO(ezb): when floating storage is implemented, return GetField<2>()[1]; + assert(GetField<2>()[1] == 0); + return 0; + } + + // Getter for the offset after the last value in the `values` array. + field_type finish() const { return GetField<2>()[2]; } + + // Getters for the number of values stored in this node. + field_type count() const { + assert(finish() >= start()); + return finish() - start(); + } + field_type max_count() const { + // Internal nodes have max_count==kInternalNodeMaxCount. + // Leaf nodes have max_count in [1, kNodeSlots]. + const field_type max_count = GetField<2>()[3]; + return max_count == field_type{kInternalNodeMaxCount} + ? field_type{kNodeSlots} + : max_count; + } + + // Getter for the parent of this node. + // TODO(ezb): assert that the child of the returned node at position + // `node_->position()` maps to the current node. + btree_node *parent() const { return *GetField<0>(); } + // Getter for whether the node is the root of the tree. The parent of the + // root of the tree is the leftmost node in the tree which is guaranteed to + // be a leaf. + bool is_root() const { return parent()->is_leaf(); } + void make_root() { + assert(parent()->is_root()); + set_generation(parent()->generation()); + set_parent(parent()->parent()); + } + + // Gets the root node's generation integer, which is the one used by the tree. + uint32_t *get_root_generation() const { + assert(BtreeGenerationsEnabled()); + const btree_node *curr = this; + for (; !curr->is_root(); curr = curr->parent()) continue; + return const_cast(&curr->GetField<1>()[0]); + } + + // Returns the generation for iterator validation. + uint32_t generation() const { + return BtreeGenerationsEnabled() ? *get_root_generation() : 0; + } + // Updates generation. Should only be called on a root node or during node + // initialization. + void set_generation(uint32_t generation) { + if (BtreeGenerationsEnabled()) GetField<1>()[0] = generation; + } + // Updates the generation. We do this whenever the node is mutated. + void next_generation() { + if (BtreeGenerationsEnabled()) ++*get_root_generation(); + } + + // Getters for the key/value at position i in the node. + const key_type &key(size_type i) const { return params_type::key(slot(i)); } + reference value(size_type i) { return params_type::element(slot(i)); } + const_reference value(size_type i) const { + return params_type::element(slot(i)); + } + + // Getters/setter for the child at position i in the node. + btree_node *child(field_type i) const { return GetField<4>()[i]; } + btree_node *start_child() const { return child(start()); } + btree_node *&mutable_child(field_type i) { return GetField<4>()[i]; } + void clear_child(field_type i) { + absl::container_internal::SanitizerPoisonObject(&mutable_child(i)); + } + void set_child_noupdate_position(field_type i, btree_node *c) { + absl::container_internal::SanitizerUnpoisonObject(&mutable_child(i)); + mutable_child(i) = c; + } + void set_child(field_type i, btree_node *c) { + set_child_noupdate_position(i, c); + c->set_position(i); + } + void init_child(field_type i, btree_node *c) { + set_child(i, c); + c->set_parent(this); + } + + // Returns the position of the first value whose key is not less than k. + template + SearchResult lower_bound( + const K &k, const key_compare &comp) const { + return use_linear_search::value ? linear_search(k, comp) + : binary_search(k, comp); + } + // Returns the position of the first value whose key is greater than k. + template + size_type upper_bound(const K &k, const key_compare &comp) const { + auto upper_compare = upper_bound_adapter(comp); + return use_linear_search::value ? linear_search(k, upper_compare).value + : binary_search(k, upper_compare).value; + } + + template + SearchResult::value> + linear_search(const K &k, const Compare &comp) const { + return linear_search_impl(k, start(), finish(), comp, + btree_is_key_compare_to()); + } + + template + SearchResult::value> + binary_search(const K &k, const Compare &comp) const { + return binary_search_impl(k, start(), finish(), comp, + btree_is_key_compare_to()); + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using plain compare. + template + SearchResult linear_search_impl( + const K &k, size_type s, const size_type e, const Compare &comp, + std::false_type /* IsCompareTo */) const { + while (s < e) { + if (!comp(key(s), k)) { + break; + } + ++s; + } + return SearchResult{s}; + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using compare-to. + template + SearchResult linear_search_impl( + const K &k, size_type s, const size_type e, const Compare &comp, + std::true_type /* IsCompareTo */) const { + while (s < e) { + const absl::weak_ordering c = comp(key(s), k); + if (c == 0) { + return {s, MatchKind::kEq}; + } else if (c > 0) { + break; + } + ++s; + } + return {s, MatchKind::kNe}; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using plain compare. + template + SearchResult binary_search_impl( + const K &k, size_type s, size_type e, const Compare &comp, + std::false_type /* IsCompareTo */) const { + while (s != e) { + const size_type mid = (s + e) >> 1; + if (comp(key(mid), k)) { + s = mid + 1; + } else { + e = mid; + } + } + return SearchResult{s}; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using compare-to. + template + SearchResult binary_search_impl( + const K &k, size_type s, size_type e, const CompareTo &comp, + std::true_type /* IsCompareTo */) const { + if (params_type::template can_have_multiple_equivalent_keys()) { + MatchKind exact_match = MatchKind::kNe; + while (s != e) { + const size_type mid = (s + e) >> 1; + const absl::weak_ordering c = comp(key(mid), k); + if (c < 0) { + s = mid + 1; + } else { + e = mid; + if (c == 0) { + // Need to return the first value whose key is not less than k, + // which requires continuing the binary search if there could be + // multiple equivalent keys. + exact_match = MatchKind::kEq; + } + } + } + return {s, exact_match}; + } else { // Can't have multiple equivalent keys. + while (s != e) { + const size_type mid = (s + e) >> 1; + const absl::weak_ordering c = comp(key(mid), k); + if (c < 0) { + s = mid + 1; + } else if (c > 0) { + e = mid; + } else { + return {mid, MatchKind::kEq}; + } + } + return {s, MatchKind::kNe}; + } + } + + // Returns whether key i is ordered correctly with respect to the other keys + // in the node. The motivation here is to detect comparators that violate + // transitivity. Note: we only do comparisons of keys on this node rather than + // the whole tree so that this is constant time. + template + bool is_ordered_correctly(field_type i, const Compare &comp) const { + if (std::is_base_of::value || + params_type::kIsKeyCompareStringAdapted) { + return true; + } + + const auto compare = [&](field_type a, field_type b) { + const absl::weak_ordering cmp = + compare_internal::do_three_way_comparison(comp, key(a), key(b)); + return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; + }; + int cmp = -1; + constexpr bool kCanHaveEquivKeys = + params_type::template can_have_multiple_equivalent_keys(); + for (field_type j = start(); j < finish(); ++j) { + if (j == i) { + if (cmp > 0) return false; + continue; + } + int new_cmp = compare(j, i); + if (new_cmp < cmp || (!kCanHaveEquivKeys && new_cmp == 0)) return false; + cmp = new_cmp; + } + return true; + } + + // Emplaces a value at position i, shifting all existing values and + // children at positions >= i to the right by 1. + template + void emplace_value(field_type i, allocator_type *alloc, Args &&...args); + + // Removes the values at positions [i, i + to_erase), shifting all existing + // values and children after that range to the left by to_erase. Clears all + // children between [i, i + to_erase). + void remove_values(field_type i, field_type to_erase, allocator_type *alloc); + + // Rebalances a node with its right sibling. + void rebalance_right_to_left(field_type to_move, btree_node *right, + allocator_type *alloc); + void rebalance_left_to_right(field_type to_move, btree_node *right, + allocator_type *alloc); + + // Splits a node, moving a portion of the node's values to its right sibling. + void split(int insert_position, btree_node *dest, allocator_type *alloc); + + // Merges a node with its right sibling, moving all of the values and the + // delimiting key in the parent node onto itself, and deleting the src node. + void merge(btree_node *src, allocator_type *alloc); + + // Node allocation/deletion routines. + void init_leaf(field_type position, field_type max_count, + btree_node *parent) { + set_generation(0); + set_parent(parent); + set_position(position); + set_start(0); + set_finish(0); + set_max_count(max_count); + absl::container_internal::SanitizerPoisonMemoryRegion( + start_slot(), max_count * sizeof(slot_type)); + } + void init_internal(field_type position, btree_node *parent) { + init_leaf(position, kNodeSlots, parent); + // Set `max_count` to a sentinel value to indicate that this node is + // internal. + set_max_count(kInternalNodeMaxCount); + absl::container_internal::SanitizerPoisonMemoryRegion( + &mutable_child(start()), (kNodeSlots + 1) * sizeof(btree_node *)); + } + + static void deallocate(const size_type size, btree_node *node, + allocator_type *alloc) { + absl::container_internal::SanitizerUnpoisonMemoryRegion(node, size); + absl::container_internal::Deallocate(alloc, node, size); + } + + // Deletes a node and all of its children. + static void clear_and_delete(btree_node *node, allocator_type *alloc); + + private: + template + void value_init(const field_type i, allocator_type *alloc, Args &&...args) { + next_generation(); + absl::container_internal::SanitizerUnpoisonObject(slot(i)); + params_type::construct(alloc, slot(i), std::forward(args)...); + } + void value_destroy(const field_type i, allocator_type *alloc) { + next_generation(); + params_type::destroy(alloc, slot(i)); + absl::container_internal::SanitizerPoisonObject(slot(i)); + } + void value_destroy_n(const field_type i, const field_type n, + allocator_type *alloc) { + next_generation(); + for (slot_type *s = slot(i), *end = slot(i + n); s != end; ++s) { + params_type::destroy(alloc, s); + absl::container_internal::SanitizerPoisonObject(s); + } + } + + static void transfer(slot_type *dest, slot_type *src, allocator_type *alloc) { + absl::container_internal::SanitizerUnpoisonObject(dest); + params_type::transfer(alloc, dest, src); + absl::container_internal::SanitizerPoisonObject(src); + } + + // Transfers value from slot `src_i` in `src_node` to slot `dest_i` in `this`. + void transfer(const size_type dest_i, const size_type src_i, + btree_node *src_node, allocator_type *alloc) { + next_generation(); + transfer(slot(dest_i), src_node->slot(src_i), alloc); + } + + // Transfers `n` values starting at value `src_i` in `src_node` into the + // values starting at value `dest_i` in `this`. + void transfer_n(const size_type n, const size_type dest_i, + const size_type src_i, btree_node *src_node, + allocator_type *alloc) { + next_generation(); + for (slot_type *src = src_node->slot(src_i), *end = src + n, + *dest = slot(dest_i); + src != end; ++src, ++dest) { + transfer(dest, src, alloc); + } + } + + // Same as above, except that we start at the end and work our way to the + // beginning. + void transfer_n_backward(const size_type n, const size_type dest_i, + const size_type src_i, btree_node *src_node, + allocator_type *alloc) { + next_generation(); + for (slot_type *src = src_node->slot(src_i + n), *end = src - n, + *dest = slot(dest_i + n); + src != end; --src, --dest) { + // If we modified the loop index calculations above to avoid the -1s here, + // it would result in UB in the computation of `end` (and possibly `src` + // as well, if n == 0), since slot() is effectively an array index and it + // is UB to compute the address of any out-of-bounds array element except + // for one-past-the-end. + transfer(dest - 1, src - 1, alloc); + } + } + + template + friend class btree; + template + friend class btree_iterator; + friend class BtreeNodePeer; + friend struct btree_access; +}; + +template +bool AreNodesFromSameContainer(const Node *node_a, const Node *node_b) { + // If either node is null, then give up on checking whether they're from the + // same container. (If exactly one is null, then we'll trigger the + // default-constructed assert in Equals.) + if (node_a == nullptr || node_b == nullptr) return true; + while (!node_a->is_root()) node_a = node_a->parent(); + while (!node_b->is_root()) node_b = node_b->parent(); + return node_a == node_b; +} + +class btree_iterator_generation_info_enabled { + public: + explicit btree_iterator_generation_info_enabled(uint32_t g) + : generation_(g) {} + + // Updates the generation. For use internally right before we return an + // iterator to the user. + template + void update_generation(const Node *node) { + if (node != nullptr) generation_ = node->generation(); + } + uint32_t generation() const { return generation_; } + + template + void assert_valid_generation(const Node *node) const { + if (node != nullptr && node->generation() != generation_) { + ABSL_INTERNAL_LOG( + FATAL, + "Attempting to use an invalidated iterator. The corresponding b-tree " + "container has been mutated since this iterator was constructed."); + } + } + + private: + // Used to check that the iterator hasn't been invalidated. + uint32_t generation_; +}; + +class btree_iterator_generation_info_disabled { + public: + explicit btree_iterator_generation_info_disabled(uint32_t) {} + static void update_generation(const void *) {} + static uint32_t generation() { return 0; } + static void assert_valid_generation(const void *) {} +}; + +#ifdef ABSL_BTREE_ENABLE_GENERATIONS +using btree_iterator_generation_info = btree_iterator_generation_info_enabled; +#else +using btree_iterator_generation_info = btree_iterator_generation_info_disabled; +#endif + +template +class btree_iterator : private btree_iterator_generation_info { + using field_type = typename Node::field_type; + using key_type = typename Node::key_type; + using size_type = typename Node::size_type; + using params_type = typename Node::params_type; + using is_map_container = typename params_type::is_map_container; + + using node_type = Node; + using normal_node = typename std::remove_const::type; + using const_node = const Node; + using normal_pointer = typename params_type::pointer; + using normal_reference = typename params_type::reference; + using const_pointer = typename params_type::const_pointer; + using const_reference = typename params_type::const_reference; + using slot_type = typename params_type::slot_type; + + // In sets, all iterators are const. + using iterator = absl::conditional_t< + is_map_container::value, + btree_iterator, + btree_iterator>; + using const_iterator = + btree_iterator; + + public: + // These aliases are public for std::iterator_traits. + using difference_type = typename Node::difference_type; + using value_type = typename params_type::value_type; + using pointer = Pointer; + using reference = Reference; + using iterator_category = std::bidirectional_iterator_tag; + + btree_iterator() : btree_iterator(nullptr, -1) {} + explicit btree_iterator(Node *n) : btree_iterator(n, n->start()) {} + btree_iterator(Node *n, int p) + : btree_iterator_generation_info(n != nullptr ? n->generation() + : ~uint32_t{}), + node_(n), + position_(p) {} + + // NOTE: this SFINAE allows for implicit conversions from iterator to + // const_iterator, but it specifically avoids hiding the copy constructor so + // that the trivial one will be used when possible. + template , iterator>::value && + std::is_same::value, + int> = 0> + btree_iterator(const btree_iterator other) // NOLINT + : btree_iterator_generation_info(other), + node_(other.node_), + position_(other.position_) {} + + bool operator==(const iterator &other) const { + return Equals(other); + } + bool operator==(const const_iterator &other) const { + return Equals(other); + } + bool operator!=(const iterator &other) const { + return !Equals(other); + } + bool operator!=(const const_iterator &other) const { + return !Equals(other); + } + + // Returns n such that n calls to ++other yields *this. + // Precondition: n exists. + difference_type operator-(const_iterator other) const { + if (node_ == other.node_) { + if (node_->is_leaf()) return position_ - other.position_; + if (position_ == other.position_) return 0; + } + return distance_slow(other); + } + + // Advances the iterator by `n`. Values of `n` must not result in going past + // the `end` iterator (for a positive `n`) or before the `begin` iterator (for + // a negative `n`). + btree_iterator &operator+=(difference_type n) { + assert_valid_generation(node_); + if (n == 0) return *this; + if (n < 0) return decrement_n_slow(-n); + return increment_n_slow(n); + } + + // Moves the iterator by `n` positions backwards. Values of `n` must not + // result in going before the `begin` iterator (for a positive `n`) or past + // the `end` iterator (for a negative `n`). + btree_iterator &operator-=(difference_type n) { + assert_valid_generation(node_); + if (n == 0) return *this; + if (n < 0) return increment_n_slow(-n); + return decrement_n_slow(n); + } + + // Accessors for the key/value the iterator is pointing at. + reference operator*() const { + ABSL_HARDENING_ASSERT(node_ != nullptr); + assert_valid_generation(node_); + ABSL_HARDENING_ASSERT(position_ >= node_->start()); + if (position_ >= node_->finish()) { + ABSL_HARDENING_ASSERT(!IsEndIterator() && "Dereferencing end() iterator"); + ABSL_HARDENING_ASSERT(position_ < node_->finish()); + } + return node_->value(static_cast(position_)); + } + pointer operator->() const { return &operator*(); } + + btree_iterator &operator++() { + increment(); + return *this; + } + btree_iterator &operator--() { + decrement(); + return *this; + } + btree_iterator operator++(int) { + btree_iterator tmp = *this; + ++*this; + return tmp; + } + btree_iterator operator--(int) { + btree_iterator tmp = *this; + --*this; + return tmp; + } + + private: + friend iterator; + friend const_iterator; + template + friend class btree; + template + friend class btree_container; + template + friend class btree_set_container; + template + friend class btree_map_container; + template + friend class btree_multiset_container; + template + friend class base_checker; + friend struct btree_access; + + // This SFINAE allows explicit conversions from const_iterator to + // iterator, but also avoids hiding the copy constructor. + // NOTE: the const_cast is safe because this constructor is only called by + // non-const methods and the container owns the nodes. + template , const_iterator>::value && + std::is_same::value, + int> = 0> + explicit btree_iterator(const btree_iterator other) + : btree_iterator_generation_info(other.generation()), + node_(const_cast(other.node_)), + position_(other.position_) {} + + bool Equals(const const_iterator other) const { + ABSL_HARDENING_ASSERT(((node_ == nullptr && other.node_ == nullptr) || + (node_ != nullptr && other.node_ != nullptr)) && + "Comparing default-constructed iterator with " + "non-default-constructed iterator."); + // Note: we use assert instead of ABSL_HARDENING_ASSERT here because this + // changes the complexity of Equals from O(1) to O(log(N) + log(M)) where + // N/M are sizes of the containers containing node_/other.node_. + assert(AreNodesFromSameContainer(node_, other.node_) && + "Comparing iterators from different containers."); + assert_valid_generation(node_); + other.assert_valid_generation(other.node_); + return node_ == other.node_ && position_ == other.position_; + } + + bool IsEndIterator() const { + if (position_ != node_->finish()) return false; + node_type *node = node_; + while (!node->is_root()) { + if (node->position() != node->parent()->finish()) return false; + node = node->parent(); + } + return true; + } + + // Returns n such that n calls to ++other yields *this. + // Precondition: n exists && (this->node_ != other.node_ || + // !this->node_->is_leaf() || this->position_ != other.position_). + difference_type distance_slow(const_iterator other) const; + + // Increment/decrement the iterator. + void increment() { + assert_valid_generation(node_); + if (node_->is_leaf() && ++position_ < node_->finish()) { + return; + } + increment_slow(); + } + void increment_slow(); + btree_iterator &increment_n_slow(difference_type n); + + void decrement() { + assert_valid_generation(node_); + if (node_->is_leaf() && --position_ >= node_->start()) { + return; + } + decrement_slow(); + } + void decrement_slow(); + btree_iterator &decrement_n_slow(difference_type n); + + const key_type &key() const { + return node_->key(static_cast(position_)); + } + decltype(std::declval()->slot(0)) slot() { + return node_->slot(static_cast(position_)); + } + + void update_generation() { + btree_iterator_generation_info::update_generation(node_); + } + + // The node in the tree the iterator is pointing at. + Node *node_; + // The position within the node of the tree the iterator is pointing at. + // NOTE: this is an int rather than a field_type because iterators can point + // to invalid positions (such as -1) in certain circumstances. + int position_; +}; + +template +class btree { + using node_type = btree_node; + using is_key_compare_to = typename Params::is_key_compare_to; + using field_type = typename node_type::field_type; + + // We use a static empty node for the root/leftmost/rightmost of empty btrees + // in order to avoid branching in begin()/end(). + struct EmptyNodeType : node_type { + using field_type = typename node_type::field_type; + node_type *parent; +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + uint32_t generation = 0; +#endif + field_type position = 0; + field_type start = 0; + field_type finish = 0; + // max_count must be != kInternalNodeMaxCount (so that this node is regarded + // as a leaf node). max_count() is never called when the tree is empty. + field_type max_count = node_type::kInternalNodeMaxCount + 1; + + constexpr EmptyNodeType() : parent(this) {} + }; + + static node_type *EmptyNode() { + alignas(node_type::Alignment()) static constexpr EmptyNodeType empty_node; + return const_cast(&empty_node); + } + + enum : uint32_t { + kNodeSlots = node_type::kNodeSlots, + kMinNodeValues = kNodeSlots / 2, + }; + + struct node_stats { + using size_type = typename Params::size_type; + + node_stats(size_type l, size_type i) : leaf_nodes(l), internal_nodes(i) {} + + node_stats &operator+=(const node_stats &other) { + leaf_nodes += other.leaf_nodes; + internal_nodes += other.internal_nodes; + return *this; + } + + size_type leaf_nodes; + size_type internal_nodes; + }; + + public: + using key_type = typename Params::key_type; + using value_type = typename Params::value_type; + using size_type = typename Params::size_type; + using difference_type = typename Params::difference_type; + using key_compare = typename Params::key_compare; + using original_key_compare = typename Params::original_key_compare; + using value_compare = typename Params::value_compare; + using allocator_type = typename Params::allocator_type; + using reference = typename Params::reference; + using const_reference = typename Params::const_reference; + using pointer = typename Params::pointer; + using const_pointer = typename Params::const_pointer; + using iterator = + typename btree_iterator::iterator; + using const_iterator = typename iterator::const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using node_handle_type = node_handle; + + // Internal types made public for use by btree_container types. + using params_type = Params; + using slot_type = typename Params::slot_type; + + private: + // Copies or moves (depending on the template parameter) the values in + // other into this btree in their order in other. This btree must be empty + // before this method is called. This method is used in copy construction, + // copy assignment, and move assignment. + template + void copy_or_move_values_in_order(Btree &other); + + // Validates that various assumptions/requirements are true at compile time. + constexpr static bool static_assert_validation(); + + public: + btree(const key_compare &comp, const allocator_type &alloc) + : root_(EmptyNode()), rightmost_(comp, alloc, EmptyNode()), size_(0) {} + + btree(const btree &other) : btree(other, other.allocator()) {} + btree(const btree &other, const allocator_type &alloc) + : btree(other.key_comp(), alloc) { + copy_or_move_values_in_order(other); + } + btree(btree &&other) noexcept + : root_(std::exchange(other.root_, EmptyNode())), + rightmost_(std::move(other.rightmost_)), + size_(std::exchange(other.size_, 0u)) { + other.mutable_rightmost() = EmptyNode(); + } + btree(btree &&other, const allocator_type &alloc) + : btree(other.key_comp(), alloc) { + if (alloc == other.allocator()) { + swap(other); + } else { + // Move values from `other` one at a time when allocators are different. + copy_or_move_values_in_order(other); + } + } + + ~btree() { + // Put static_asserts in destructor to avoid triggering them before the type + // is complete. + static_assert(static_assert_validation(), "This call must be elided."); + clear(); + } + + // Assign the contents of other to *this. + btree &operator=(const btree &other); + btree &operator=(btree &&other) noexcept; + + iterator begin() { return iterator(leftmost()); } + const_iterator begin() const { return const_iterator(leftmost()); } + iterator end() { return iterator(rightmost(), rightmost()->finish()); } + const_iterator end() const { + return const_iterator(rightmost(), rightmost()->finish()); + } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + // Finds the first element whose key is not less than `key`. + template + iterator lower_bound(const K &key) { + return internal_end(internal_lower_bound(key).value); + } + template + const_iterator lower_bound(const K &key) const { + return internal_end(internal_lower_bound(key).value); + } + + // Finds the first element whose key is not less than `key` and also returns + // whether that element is equal to `key`. + template + std::pair lower_bound_equal(const K &key) const; + + // Finds the first element whose key is greater than `key`. + template + iterator upper_bound(const K &key) { + return internal_end(internal_upper_bound(key)); + } + template + const_iterator upper_bound(const K &key) const { + return internal_end(internal_upper_bound(key)); + } + + // Finds the range of values which compare equal to key. The first member of + // the returned pair is equal to lower_bound(key). The second member of the + // pair is equal to upper_bound(key). + template + std::pair equal_range(const K &key); + template + std::pair equal_range(const K &key) const { + return const_cast(this)->equal_range(key); + } + + // Inserts a value into the btree only if it does not already exist. The + // boolean return value indicates whether insertion succeeded or failed. + // Requirement: if `key` already exists in the btree, does not consume `args`. + // Requirement: `key` is never referenced after consuming `args`. + template + std::pair insert_unique(const K &key, Args &&...args); + + // Inserts with hint. Checks to see if the value should be placed immediately + // before `position` in the tree. If so, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_unique() were made. + // Requirement: if `key` already exists in the btree, does not consume `args`. + // Requirement: `key` is never referenced after consuming `args`. + template + std::pair insert_hint_unique(iterator position, const K &key, + Args &&...args); + + // Insert a range of values into the btree. + // Note: the first overload avoids constructing a value_type if the key + // already exists in the btree. + template ()( + params_type::key(*std::declval()), + std::declval()))> + void insert_iterator_unique(InputIterator b, InputIterator e, int); + // We need the second overload for cases in which we need to construct a + // value_type in order to compare it with the keys already in the btree. + template + void insert_iterator_unique(InputIterator b, InputIterator e, char); + + // Inserts a value into the btree. + template + iterator insert_multi(const key_type &key, ValueType &&v); + + // Inserts a value into the btree. + template + iterator insert_multi(ValueType &&v) { + return insert_multi(params_type::key(v), std::forward(v)); + } + + // Insert with hint. Check to see if the value should be placed immediately + // before position in the tree. If it does, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_multi(v) were made. + template + iterator insert_hint_multi(iterator position, ValueType &&v); + + // Insert a range of values into the btree. + template + void insert_iterator_multi(InputIterator b, + InputIterator e); + + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + // Requirement: does not read the value at `*iter`. + iterator erase(iterator iter); + + // Erases range. Returns the number of keys erased and an iterator pointing + // to the element after the last erased element. + std::pair erase_range(iterator begin, iterator end); + + // Finds an element with key equivalent to `key` or returns `end()` if `key` + // is not present. + template + iterator find(const K &key) { + return internal_end(internal_find(key)); + } + template + const_iterator find(const K &key) const { + return internal_end(internal_find(key)); + } + + // Clear the btree, deleting all of the values it contains. + void clear(); + + // Swaps the contents of `this` and `other`. + void swap(btree &other); + + const key_compare &key_comp() const noexcept { + return rightmost_.template get<0>(); + } + template + bool compare_keys(const K1 &a, const K2 &b) const { + return compare_internal::compare_result_as_less_than(key_comp()(a, b)); + } + + value_compare value_comp() const { + return value_compare(original_key_compare(key_comp())); + } + + // Verifies the structure of the btree. + void verify() const; + + // Size routines. + size_type size() const { return size_; } + size_type max_size() const { return (std::numeric_limits::max)(); } + bool empty() const { return size_ == 0; } + + // The height of the btree. An empty tree will have height 0. + size_type height() const { + size_type h = 0; + if (!empty()) { + // Count the length of the chain from the leftmost node up to the + // root. We actually count from the root back around to the level below + // the root, but the calculation is the same because of the circularity + // of that traversal. + const node_type *n = root(); + do { + ++h; + n = n->parent(); + } while (n != root()); + } + return h; + } + + // The number of internal, leaf and total nodes used by the btree. + size_type leaf_nodes() const { return internal_stats(root()).leaf_nodes; } + size_type internal_nodes() const { + return internal_stats(root()).internal_nodes; + } + size_type nodes() const { + node_stats stats = internal_stats(root()); + return stats.leaf_nodes + stats.internal_nodes; + } + + // The total number of bytes used by the btree. + // TODO(b/169338300): update to support node_btree_*. + size_type bytes_used() const { + node_stats stats = internal_stats(root()); + if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) { + return sizeof(*this) + node_type::LeafSize(root()->max_count()); + } else { + return sizeof(*this) + stats.leaf_nodes * node_type::LeafSize() + + stats.internal_nodes * node_type::InternalSize(); + } + } + + // The average number of bytes used per value stored in the btree assuming + // random insertion order. + static double average_bytes_per_value() { + // The expected number of values per node with random insertion order is the + // average of the maximum and minimum numbers of values per node. + const double expected_values_per_node = (kNodeSlots + kMinNodeValues) / 2.0; + return node_type::LeafSize() / expected_values_per_node; + } + + // The fullness of the btree. Computed as the number of elements in the btree + // divided by the maximum number of elements a tree with the current number + // of nodes could hold. A value of 1 indicates perfect space + // utilization. Smaller values indicate space wastage. + // Returns 0 for empty trees. + double fullness() const { + if (empty()) return 0.0; + return static_cast(size()) / (nodes() * kNodeSlots); + } + // The overhead of the btree structure in bytes per node. Computed as the + // total number of bytes used by the btree minus the number of bytes used for + // storing elements divided by the number of elements. + // Returns 0 for empty trees. + double overhead() const { + if (empty()) return 0.0; + return (bytes_used() - size() * sizeof(value_type)) / + static_cast(size()); + } + + // The allocator used by the btree. + allocator_type get_allocator() const { return allocator(); } + + private: + friend struct btree_access; + + // Internal accessor routines. + node_type *root() { return root_; } + const node_type *root() const { return root_; } + node_type *&mutable_root() noexcept { return root_; } + node_type *rightmost() { return rightmost_.template get<2>(); } + const node_type *rightmost() const { return rightmost_.template get<2>(); } + node_type *&mutable_rightmost() noexcept { + return rightmost_.template get<2>(); + } + key_compare *mutable_key_comp() noexcept { + return &rightmost_.template get<0>(); + } + + // The leftmost node is stored as the parent of the root node. + node_type *leftmost() { return root()->parent(); } + const node_type *leftmost() const { return root()->parent(); } + + // Allocator routines. + allocator_type *mutable_allocator() noexcept { + return &rightmost_.template get<1>(); + } + const allocator_type &allocator() const noexcept { + return rightmost_.template get<1>(); + } + + // Allocates a correctly aligned node of at least size bytes using the + // allocator. + node_type *allocate(size_type size) { + return reinterpret_cast( + absl::container_internal::Allocate( + mutable_allocator(), size)); + } + + // Node creation/deletion routines. + node_type *new_internal_node(field_type position, node_type *parent) { + node_type *n = allocate(node_type::InternalSize()); + n->init_internal(position, parent); + return n; + } + node_type *new_leaf_node(field_type position, node_type *parent) { + node_type *n = allocate(node_type::LeafSize()); + n->init_leaf(position, kNodeSlots, parent); + return n; + } + node_type *new_leaf_root_node(field_type max_count) { + node_type *n = allocate(node_type::LeafSize(max_count)); + n->init_leaf(/*position=*/0, max_count, /*parent=*/n); + return n; + } + + // Deletion helper routines. + iterator rebalance_after_delete(iterator iter); + + // Rebalances or splits the node iter points to. + void rebalance_or_split(iterator *iter); + + // Merges the values of left, right and the delimiting key on their parent + // onto left, removing the delimiting key and deleting right. + void merge_nodes(node_type *left, node_type *right); + + // Tries to merge node with its left or right sibling, and failing that, + // rebalance with its left or right sibling. Returns true if a merge + // occurred, at which point it is no longer valid to access node. Returns + // false if no merging took place. + bool try_merge_or_rebalance(iterator *iter); + + // Tries to shrink the height of the tree by 1. + void try_shrink(); + + iterator internal_end(iterator iter) { + return iter.node_ != nullptr ? iter : end(); + } + const_iterator internal_end(const_iterator iter) const { + return iter.node_ != nullptr ? iter : end(); + } + + // Emplaces a value into the btree immediately before iter. Requires that + // key(v) <= iter.key() and (--iter).key() <= key(v). + template + iterator internal_emplace(iterator iter, Args &&...args); + + // Returns an iterator pointing to the first value >= the value "iter" is + // pointing at. Note that "iter" might be pointing to an invalid location such + // as iter.position_ == iter.node_->finish(). This routine simply moves iter + // up in the tree to a valid location. Requires: iter.node_ is non-null. + template + static IterType internal_last(IterType iter); + + // Returns an iterator pointing to the leaf position at which key would + // reside in the tree, unless there is an exact match - in which case, the + // result may not be on a leaf. When there's a three-way comparator, we can + // return whether there was an exact match. This allows the caller to avoid a + // subsequent comparison to determine if an exact match was made, which is + // important for keys with expensive comparison, such as strings. + template + SearchResult internal_locate( + const K &key) const; + + // Internal routine which implements lower_bound(). + template + SearchResult internal_lower_bound( + const K &key) const; + + // Internal routine which implements upper_bound(). + template + iterator internal_upper_bound(const K &key) const; + + // Internal routine which implements find(). + template + iterator internal_find(const K &key) const; + + // Verifies the tree structure of node. + size_type internal_verify(const node_type *node, const key_type *lo, + const key_type *hi) const; + + node_stats internal_stats(const node_type *node) const { + // The root can be a static empty node. + if (node == nullptr || (node == root() && empty())) { + return node_stats(0, 0); + } + if (node->is_leaf()) { + return node_stats(1, 0); + } + node_stats res(0, 1); + for (int i = node->start(); i <= node->finish(); ++i) { + res += internal_stats(node->child(i)); + } + return res; + } + + node_type *root_; + + // A pointer to the rightmost node. Note that the leftmost node is stored as + // the root's parent. We use compressed tuple in order to save space because + // key_compare and allocator_type are usually empty. + absl::container_internal::CompressedTuple + rightmost_; + + // Number of values. + size_type size_; +}; + +//// +// btree_node methods +template +template +inline void btree_node

::emplace_value(const field_type i, + allocator_type *alloc, + Args &&...args) { + assert(i >= start()); + assert(i <= finish()); + // Shift old values to create space for new value and then construct it in + // place. + if (i < finish()) { + transfer_n_backward(finish() - i, /*dest_i=*/i + 1, /*src_i=*/i, this, + alloc); + } + value_init(static_cast(i), alloc, std::forward(args)...); + set_finish(finish() + 1); + + if (is_internal() && finish() > i + 1) { + for (field_type j = finish(); j > i + 1; --j) { + set_child(j, child(j - 1)); + } + clear_child(i + 1); + } +} + +template +inline void btree_node

::remove_values(const field_type i, + const field_type to_erase, + allocator_type *alloc) { + // Transfer values after the removed range into their new places. + value_destroy_n(i, to_erase, alloc); + const field_type orig_finish = finish(); + const field_type src_i = i + to_erase; + transfer_n(orig_finish - src_i, i, src_i, this, alloc); + + if (is_internal()) { + // Delete all children between begin and end. + for (field_type j = 0; j < to_erase; ++j) { + clear_and_delete(child(i + j + 1), alloc); + } + // Rotate children after end into new positions. + for (field_type j = i + to_erase + 1; j <= orig_finish; ++j) { + set_child(j - to_erase, child(j)); + clear_child(j); + } + } + set_finish(orig_finish - to_erase); +} + +template +void btree_node

::rebalance_right_to_left(field_type to_move, + btree_node *right, + allocator_type *alloc) { + assert(parent() == right->parent()); + assert(position() + 1 == right->position()); + assert(right->count() >= count()); + assert(to_move >= 1); + assert(to_move <= right->count()); + + // 1) Move the delimiting value in the parent to the left node. + transfer(finish(), position(), parent(), alloc); + + // 2) Move the (to_move - 1) values from the right node to the left node. + transfer_n(to_move - 1, finish() + 1, right->start(), right, alloc); + + // 3) Move the new delimiting value to the parent from the right node. + parent()->transfer(position(), right->start() + to_move - 1, right, alloc); + + // 4) Shift the values in the right node to their correct positions. + right->transfer_n(right->count() - to_move, right->start(), + right->start() + to_move, right, alloc); + + if (is_internal()) { + // Move the child pointers from the right to the left node. + for (field_type i = 0; i < to_move; ++i) { + init_child(finish() + i + 1, right->child(i)); + } + for (field_type i = right->start(); i <= right->finish() - to_move; ++i) { + assert(i + to_move <= right->max_count()); + right->init_child(i, right->child(i + to_move)); + right->clear_child(i + to_move); + } + } + + // Fixup `finish` on the left and right nodes. + set_finish(finish() + to_move); + right->set_finish(right->finish() - to_move); +} + +template +void btree_node

::rebalance_left_to_right(field_type to_move, + btree_node *right, + allocator_type *alloc) { + assert(parent() == right->parent()); + assert(position() + 1 == right->position()); + assert(count() >= right->count()); + assert(to_move >= 1); + assert(to_move <= count()); + + // Values in the right node are shifted to the right to make room for the + // new to_move values. Then, the delimiting value in the parent and the + // other (to_move - 1) values in the left node are moved into the right node. + // Lastly, a new delimiting value is moved from the left node into the + // parent, and the remaining empty left node entries are destroyed. + + // 1) Shift existing values in the right node to their correct positions. + right->transfer_n_backward(right->count(), right->start() + to_move, + right->start(), right, alloc); + + // 2) Move the delimiting value in the parent to the right node. + right->transfer(right->start() + to_move - 1, position(), parent(), alloc); + + // 3) Move the (to_move - 1) values from the left node to the right node. + right->transfer_n(to_move - 1, right->start(), finish() - (to_move - 1), this, + alloc); + + // 4) Move the new delimiting value to the parent from the left node. + parent()->transfer(position(), finish() - to_move, this, alloc); + + if (is_internal()) { + // Move the child pointers from the left to the right node. + for (field_type i = right->finish() + 1; i > right->start(); --i) { + right->init_child(i - 1 + to_move, right->child(i - 1)); + right->clear_child(i - 1); + } + for (field_type i = 1; i <= to_move; ++i) { + right->init_child(i - 1, child(finish() - to_move + i)); + clear_child(finish() - to_move + i); + } + } + + // Fixup the counts on the left and right nodes. + set_finish(finish() - to_move); + right->set_finish(right->finish() + to_move); +} + +template +void btree_node

::split(const int insert_position, btree_node *dest, + allocator_type *alloc) { + assert(dest->count() == 0); + assert(max_count() == kNodeSlots); + assert(position() + 1 == dest->position()); + assert(parent() == dest->parent()); + + // We bias the split based on the position being inserted. If we're + // inserting at the beginning of the left node then bias the split to put + // more values on the right node. If we're inserting at the end of the + // right node then bias the split to put more values on the left node. + if (insert_position == start()) { + dest->set_finish(dest->start() + finish() - 1); + } else if (insert_position == kNodeSlots) { + dest->set_finish(dest->start()); + } else { + dest->set_finish(dest->start() + count() / 2); + } + set_finish(finish() - dest->count()); + assert(count() >= 1); + + // Move values from the left sibling to the right sibling. + dest->transfer_n(dest->count(), dest->start(), finish(), this, alloc); + + // The split key is the largest value in the left sibling. + --mutable_finish(); + parent()->emplace_value(position(), alloc, finish_slot()); + value_destroy(finish(), alloc); + parent()->set_child_noupdate_position(position() + 1, dest); + + if (is_internal()) { + for (field_type i = dest->start(), j = finish() + 1; i <= dest->finish(); + ++i, ++j) { + assert(child(j) != nullptr); + dest->init_child(i, child(j)); + clear_child(j); + } + } +} + +template +void btree_node

::merge(btree_node *src, allocator_type *alloc) { + assert(parent() == src->parent()); + assert(position() + 1 == src->position()); + + // Move the delimiting value to the left node. + value_init(finish(), alloc, parent()->slot(position())); + + // Move the values from the right to the left node. + transfer_n(src->count(), finish() + 1, src->start(), src, alloc); + + if (is_internal()) { + // Move the child pointers from the right to the left node. + for (field_type i = src->start(), j = finish() + 1; i <= src->finish(); + ++i, ++j) { + init_child(j, src->child(i)); + src->clear_child(i); + } + } + + // Fixup `finish` on the src and dest nodes. + set_finish(start() + 1 + count() + src->count()); + src->set_finish(src->start()); + + // Remove the value on the parent node and delete the src node. + parent()->remove_values(position(), /*to_erase=*/1, alloc); +} + +template +void btree_node

::clear_and_delete(btree_node *node, allocator_type *alloc) { + if (node->is_leaf()) { + node->value_destroy_n(node->start(), node->count(), alloc); + deallocate(LeafSize(node->max_count()), node, alloc); + return; + } + if (node->count() == 0) { + deallocate(InternalSize(), node, alloc); + return; + } + + // The parent of the root of the subtree we are deleting. + btree_node *delete_root_parent = node->parent(); + + // Navigate to the leftmost leaf under node, and then delete upwards. + while (node->is_internal()) node = node->start_child(); +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + // When generations are enabled, we delete the leftmost leaf last in case it's + // the parent of the root and we need to check whether it's a leaf before we + // can update the root's generation. + // TODO(ezb): if we change btree_node::is_root to check a bool inside the node + // instead of checking whether the parent is a leaf, we can remove this logic. + btree_node *leftmost_leaf = node; +#endif + // Use `size_type` because `pos` needs to be able to hold `kNodeSlots+1`, + // which isn't guaranteed to be a valid `field_type`. + size_type pos = node->position(); + btree_node *parent = node->parent(); + for (;;) { + // In each iteration of the next loop, we delete one leaf node and go right. + assert(pos <= parent->finish()); + do { + node = parent->child(static_cast(pos)); + if (node->is_internal()) { + // Navigate to the leftmost leaf under node. + while (node->is_internal()) node = node->start_child(); + pos = node->position(); + parent = node->parent(); + } + node->value_destroy_n(node->start(), node->count(), alloc); +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + if (leftmost_leaf != node) +#endif + deallocate(LeafSize(node->max_count()), node, alloc); + ++pos; + } while (pos <= parent->finish()); + + // Once we've deleted all children of parent, delete parent and go up/right. + assert(pos > parent->finish()); + do { + node = parent; + pos = node->position(); + parent = node->parent(); + node->value_destroy_n(node->start(), node->count(), alloc); + deallocate(InternalSize(), node, alloc); + if (parent == delete_root_parent) { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + deallocate(LeafSize(leftmost_leaf->max_count()), leftmost_leaf, alloc); +#endif + return; + } + ++pos; + } while (pos > parent->finish()); + } +} + +//// +// btree_iterator methods + +// Note: the implementation here is based on btree_node::clear_and_delete. +template +auto btree_iterator::distance_slow(const_iterator other) const + -> difference_type { + const_iterator begin = other; + const_iterator end = *this; + assert(begin.node_ != end.node_ || !begin.node_->is_leaf() || + begin.position_ != end.position_); + + const node_type *node = begin.node_; + // We need to compensate for double counting if begin.node_ is a leaf node. + difference_type count = node->is_leaf() ? -begin.position_ : 0; + + // First navigate to the leftmost leaf node past begin. + if (node->is_internal()) { + ++count; + node = node->child(begin.position_ + 1); + } + while (node->is_internal()) node = node->start_child(); + + // Use `size_type` because `pos` needs to be able to hold `kNodeSlots+1`, + // which isn't guaranteed to be a valid `field_type`. + size_type pos = node->position(); + const node_type *parent = node->parent(); + for (;;) { + // In each iteration of the next loop, we count one leaf node and go right. + assert(pos <= parent->finish()); + do { + node = parent->child(static_cast(pos)); + if (node->is_internal()) { + // Navigate to the leftmost leaf under node. + while (node->is_internal()) node = node->start_child(); + pos = node->position(); + parent = node->parent(); + } + if (node == end.node_) return count + end.position_; + if (parent == end.node_ && pos == static_cast(end.position_)) + return count + node->count(); + // +1 is for the next internal node value. + count += node->count() + 1; + ++pos; + } while (pos <= parent->finish()); + + // Once we've counted all children of parent, go up/right. + assert(pos > parent->finish()); + do { + node = parent; + pos = node->position(); + parent = node->parent(); + // -1 because we counted the value at end and shouldn't. + if (parent == end.node_ && pos == static_cast(end.position_)) + return count - 1; + ++pos; + } while (pos > parent->finish()); + } +} + +template +void btree_iterator::increment_slow() { + N* node = node_; + int position = position_; + if (node->is_leaf()) { + assert(position >= node->finish()); + while (position == node->finish() && !node->is_root()) { + assert(node->parent()->child(node->position()) == node); + position = node->position(); + node = node->parent(); + } + // TODO(ezb): assert we aren't incrementing end() instead of handling. + if (position == node->finish()) { + return; + } + } else { + assert(position < node->finish()); + node = node->child(static_cast(position + 1)); + while (node->is_internal()) { + node = node->start_child(); + } + position = node->start(); + } + *this = {node, position}; +} + +template +void btree_iterator::decrement_slow() { + N* node = node_; + int position = position_; + if (node->is_leaf()) { + assert(position <= -1); + while (position < node->start() && !node->is_root()) { + assert(node->parent()->child(node->position()) == node); + position = node->position() - 1; + node = node->parent(); + } + // TODO(ezb): assert we aren't decrementing begin() instead of handling. + if (position < node->start()) { + return; + } + } else { + assert(position >= node->start()); + node = node->child(static_cast(position)); + while (node->is_internal()) { + node = node->child(node->finish()); + } + position = node->finish() - 1; + } + *this = {node, position}; +} + +template +btree_iterator &btree_iterator::increment_n_slow( + difference_type n) { + N *node = node_; + int position = position_; + ABSL_ASSUME(n > 0); + while (n > 0) { + if (node->is_leaf()) { + if (position + n < node->finish()) { + position += n; + break; + } else { + n -= node->finish() - position; + position = node->finish(); + btree_iterator save = {node, position}; + while (position == node->finish() && !node->is_root()) { + position = node->position(); + node = node->parent(); + } + if (position == node->finish()) { + ABSL_HARDENING_ASSERT(n == 0); + return *this = save; + } + } + } else { + --n; + assert(position < node->finish()); + node = node->child(static_cast(position + 1)); + while (node->is_internal()) { + node = node->start_child(); + } + position = node->start(); + } + } + node_ = node; + position_ = position; + return *this; +} + +template +btree_iterator &btree_iterator::decrement_n_slow( + difference_type n) { + N *node = node_; + int position = position_; + ABSL_ASSUME(n > 0); + while (n > 0) { + if (node->is_leaf()) { + if (position - n >= node->start()) { + position -= n; + break; + } else { + n -= 1 + position - node->start(); + position = node->start() - 1; + while (position < node->start() && !node->is_root()) { + position = node->position() - 1; + node = node->parent(); + } + ABSL_HARDENING_ASSERT(position >= node->start()); + } + } else { + --n; + assert(position >= node->start()); + node = node->child(static_cast(position)); + while (node->is_internal()) { + node = node->child(node->finish()); + } + position = node->finish() - 1; + } + } + node_ = node; + position_ = position; + return *this; +} + +//// +// btree methods +template +template +void btree

::copy_or_move_values_in_order(Btree &other) { + static_assert(std::is_same::value || + std::is_same::value, + "Btree type must be same or const."); + assert(empty()); + + // We can avoid key comparisons because we know the order of the + // values is the same order we'll store them in. + auto iter = other.begin(); + if (iter == other.end()) return; + insert_multi(iter.slot()); + ++iter; + for (; iter != other.end(); ++iter) { + // If the btree is not empty, we can just insert the new value at the end + // of the tree. + internal_emplace(end(), iter.slot()); + } +} + +template +constexpr bool btree

::static_assert_validation() { + static_assert(std::is_nothrow_copy_constructible::value, + "Key comparison must be nothrow copy constructible"); + static_assert(std::is_nothrow_copy_constructible::value, + "Allocator must be nothrow copy constructible"); + static_assert(std::is_trivially_copyable::value, + "iterator not trivially copyable."); + + // Note: We assert that kTargetValues, which is computed from + // Params::kTargetNodeSize, must fit the node_type::field_type. + static_assert( + kNodeSlots < (1 << (8 * sizeof(typename node_type::field_type))), + "target node size too large"); + + // Verify that key_compare returns an absl::{weak,strong}_ordering or bool. + static_assert( + compare_has_valid_result_type(), + "key comparison function must return absl::{weak,strong}_ordering or " + "bool."); + + // Test the assumption made in setting kNodeSlotSpace. + static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4, + "node space assumption incorrect"); + + return true; +} + +template +template +auto btree

::lower_bound_equal(const K &key) const + -> std::pair { + const SearchResult res = + internal_lower_bound(key); + const iterator lower = iterator(internal_end(res.value)); + const bool equal = res.HasMatch() + ? res.IsEq() + : lower != end() && !compare_keys(key, lower.key()); + return {lower, equal}; +} + +template +template +auto btree

::equal_range(const K &key) -> std::pair { + const std::pair lower_and_equal = lower_bound_equal(key); + const iterator lower = lower_and_equal.first; + if (!lower_and_equal.second) { + return {lower, lower}; + } + + const iterator next = std::next(lower); + if (!params_type::template can_have_multiple_equivalent_keys()) { + // The next iterator after lower must point to a key greater than `key`. + // Note: if this assert fails, then it may indicate that the comparator does + // not meet the equivalence requirements for Compare + // (see https://en.cppreference.com/w/cpp/named_req/Compare). + assert(next == end() || compare_keys(key, next.key())); + return {lower, next}; + } + // Try once more to avoid the call to upper_bound() if there's only one + // equivalent key. This should prevent all calls to upper_bound() in cases of + // unique-containers with heterogeneous comparators in which all comparison + // operators have the same equivalence classes. + if (next == end() || compare_keys(key, next.key())) return {lower, next}; + + // In this case, we need to call upper_bound() to avoid worst case O(N) + // behavior if we were to iterate over equal keys. + return {lower, upper_bound(key)}; +} + +template +template +auto btree

::insert_unique(const K &key, Args &&...args) + -> std::pair { + if (empty()) { + mutable_root() = mutable_rightmost() = new_leaf_root_node(1); + } + + SearchResult res = internal_locate(key); + iterator iter = res.value; + + if (res.HasMatch()) { + if (res.IsEq()) { + // The key already exists in the tree, do nothing. + return {iter, false}; + } + } else { + iterator last = internal_last(iter); + if (last.node_ && !compare_keys(key, last.key())) { + // The key already exists in the tree, do nothing. + return {last, false}; + } + } + return {internal_emplace(iter, std::forward(args)...), true}; +} + +template +template +inline auto btree

::insert_hint_unique(iterator position, const K &key, + Args &&...args) + -> std::pair { + if (!empty()) { + if (position == end() || compare_keys(key, position.key())) { + if (position == begin() || compare_keys(std::prev(position).key(), key)) { + // prev.key() < key < position.key() + return {internal_emplace(position, std::forward(args)...), true}; + } + } else if (compare_keys(position.key(), key)) { + ++position; + if (position == end() || compare_keys(key, position.key())) { + // {original `position`}.key() < key < {current `position`}.key() + return {internal_emplace(position, std::forward(args)...), true}; + } + } else { + // position.key() == key + return {position, false}; + } + } + return insert_unique(key, std::forward(args)...); +} + +template +template +void btree

::insert_iterator_unique(InputIterator b, InputIterator e, int) { + for (; b != e; ++b) { + insert_hint_unique(end(), params_type::key(*b), *b); + } +} + +template +template +void btree

::insert_iterator_unique(InputIterator b, InputIterator e, char) { + for (; b != e; ++b) { + // Use a node handle to manage a temp slot. + auto node_handle = + CommonAccess::Construct(get_allocator(), *b); + slot_type *slot = CommonAccess::GetSlot(node_handle); + insert_hint_unique(end(), params_type::key(slot), slot); + } +} + +template +template +auto btree

::insert_multi(const key_type &key, ValueType &&v) -> iterator { + if (empty()) { + mutable_root() = mutable_rightmost() = new_leaf_root_node(1); + } + + iterator iter = internal_upper_bound(key); + if (iter.node_ == nullptr) { + iter = end(); + } + return internal_emplace(iter, std::forward(v)); +} + +template +template +auto btree

::insert_hint_multi(iterator position, ValueType &&v) -> iterator { + if (!empty()) { + const key_type &key = params_type::key(v); + if (position == end() || !compare_keys(position.key(), key)) { + if (position == begin() || + !compare_keys(key, std::prev(position).key())) { + // prev.key() <= key <= position.key() + return internal_emplace(position, std::forward(v)); + } + } else { + ++position; + if (position == end() || !compare_keys(position.key(), key)) { + // {original `position`}.key() < key < {current `position`}.key() + return internal_emplace(position, std::forward(v)); + } + } + } + return insert_multi(std::forward(v)); +} + +template +template +void btree

::insert_iterator_multi(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_hint_multi(end(), *b); + } +} + +template +auto btree

::operator=(const btree &other) -> btree & { + if (this != &other) { + clear(); + + *mutable_key_comp() = other.key_comp(); + if (absl::allocator_traits< + allocator_type>::propagate_on_container_copy_assignment::value) { + *mutable_allocator() = other.allocator(); + } + + copy_or_move_values_in_order(other); + } + return *this; +} + +template +auto btree

::operator=(btree &&other) noexcept -> btree & { + if (this != &other) { + clear(); + + using std::swap; + if (absl::allocator_traits< + allocator_type>::propagate_on_container_move_assignment::value) { + swap(root_, other.root_); + // Note: `rightmost_` also contains the allocator and the key comparator. + swap(rightmost_, other.rightmost_); + swap(size_, other.size_); + } else { + if (allocator() == other.allocator()) { + swap(mutable_root(), other.mutable_root()); + swap(*mutable_key_comp(), *other.mutable_key_comp()); + swap(mutable_rightmost(), other.mutable_rightmost()); + swap(size_, other.size_); + } else { + // We aren't allowed to propagate the allocator and the allocator is + // different so we can't take over its memory. We must move each element + // individually. We need both `other` and `this` to have `other`s key + // comparator while moving the values so we can't swap the key + // comparators. + *mutable_key_comp() = other.key_comp(); + copy_or_move_values_in_order(other); + } + } + } + return *this; +} + +template +auto btree

::erase(iterator iter) -> iterator { + iter.node_->value_destroy(static_cast(iter.position_), + mutable_allocator()); + iter.update_generation(); + + const bool internal_delete = iter.node_->is_internal(); + if (internal_delete) { + // Deletion of a value on an internal node. First, transfer the largest + // value from our left child here, then erase/rebalance from that position. + // We can get to the largest value from our left child by decrementing iter. + iterator internal_iter(iter); + --iter; + assert(iter.node_->is_leaf()); + internal_iter.node_->transfer( + static_cast(internal_iter.position_), + static_cast(iter.position_), iter.node_, + mutable_allocator()); + } else { + // Shift values after erased position in leaf. In the internal case, we + // don't need to do this because the leaf position is the end of the node. + const field_type transfer_from = + static_cast(iter.position_ + 1); + const field_type num_to_transfer = iter.node_->finish() - transfer_from; + iter.node_->transfer_n(num_to_transfer, + static_cast(iter.position_), + transfer_from, iter.node_, mutable_allocator()); + } + // Update node finish and container size. + iter.node_->set_finish(iter.node_->finish() - 1); + --size_; + + // We want to return the next value after the one we just erased. If we + // erased from an internal node (internal_delete == true), then the next + // value is ++(++iter). If we erased from a leaf node (internal_delete == + // false) then the next value is ++iter. Note that ++iter may point to an + // internal node and the value in the internal node may move to a leaf node + // (iter.node_) when rebalancing is performed at the leaf level. + + iterator res = rebalance_after_delete(iter); + + // If we erased from an internal node, advance the iterator. + if (internal_delete) { + ++res; + } + return res; +} + +template +auto btree

::rebalance_after_delete(iterator iter) -> iterator { + // Merge/rebalance as we walk back up the tree. + iterator res(iter); + bool first_iteration = true; + for (;;) { + if (iter.node_ == root()) { + try_shrink(); + if (empty()) { + return end(); + } + break; + } + if (iter.node_->count() >= kMinNodeValues) { + break; + } + bool merged = try_merge_or_rebalance(&iter); + // On the first iteration, we should update `res` with `iter` because `res` + // may have been invalidated. + if (first_iteration) { + res = iter; + first_iteration = false; + } + if (!merged) { + break; + } + iter.position_ = iter.node_->position(); + iter.node_ = iter.node_->parent(); + } + res.update_generation(); + + // Adjust our return value. If we're pointing at the end of a node, advance + // the iterator. + if (res.position_ == res.node_->finish()) { + res.position_ = res.node_->finish() - 1; + ++res; + } + + return res; +} + +// Note: we tried implementing this more efficiently by erasing all of the +// elements in [begin, end) at once and then doing rebalancing once at the end +// (rather than interleaving deletion and rebalancing), but that adds a lot of +// complexity, which seems to outweigh the performance win. +template +auto btree

::erase_range(iterator begin, iterator end) + -> std::pair { + size_type count = static_cast(end - begin); + assert(count >= 0); + + if (count == 0) { + return {0, begin}; + } + + if (static_cast(count) == size_) { + clear(); + return {count, this->end()}; + } + + if (begin.node_ == end.node_) { + assert(end.position_ > begin.position_); + begin.node_->remove_values( + static_cast(begin.position_), + static_cast(end.position_ - begin.position_), + mutable_allocator()); + size_ -= count; + return {count, rebalance_after_delete(begin)}; + } + + const size_type target_size = size_ - count; + while (size_ > target_size) { + if (begin.node_->is_leaf()) { + const size_type remaining_to_erase = size_ - target_size; + const size_type remaining_in_node = + static_cast(begin.node_->finish() - begin.position_); + const field_type to_erase = static_cast( + (std::min)(remaining_to_erase, remaining_in_node)); + begin.node_->remove_values(static_cast(begin.position_), + to_erase, mutable_allocator()); + size_ -= to_erase; + begin = rebalance_after_delete(begin); + } else { + begin = erase(begin); + } + } + begin.update_generation(); + return {count, begin}; +} + +template +void btree

::clear() { + if (!empty()) { + node_type::clear_and_delete(root(), mutable_allocator()); + } + mutable_root() = mutable_rightmost() = EmptyNode(); + size_ = 0; +} + +template +void btree

::swap(btree &other) { + using std::swap; + if (absl::allocator_traits< + allocator_type>::propagate_on_container_swap::value) { + // Note: `rightmost_` also contains the allocator and the key comparator. + swap(rightmost_, other.rightmost_); + } else { + // It's undefined behavior if the allocators are unequal here. + assert(allocator() == other.allocator()); + swap(mutable_rightmost(), other.mutable_rightmost()); + swap(*mutable_key_comp(), *other.mutable_key_comp()); + } + swap(mutable_root(), other.mutable_root()); + swap(size_, other.size_); +} + +template +void btree

::verify() const { + assert(root() != nullptr); + assert(leftmost() != nullptr); + assert(rightmost() != nullptr); + assert(empty() || size() == internal_verify(root(), nullptr, nullptr)); + assert(leftmost() == (++const_iterator(root(), -1)).node_); + assert(rightmost() == (--const_iterator(root(), root()->finish())).node_); + assert(leftmost()->is_leaf()); + assert(rightmost()->is_leaf()); +} + +template +void btree

::rebalance_or_split(iterator *iter) { + node_type *&node = iter->node_; + int &insert_position = iter->position_; + assert(node->count() == node->max_count()); + assert(kNodeSlots == node->max_count()); + + // First try to make room on the node by rebalancing. + node_type *parent = node->parent(); + if (node != root()) { + if (node->position() > parent->start()) { + // Try rebalancing with our left sibling. + node_type *left = parent->child(node->position() - 1); + assert(left->max_count() == kNodeSlots); + if (left->count() < kNodeSlots) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the end of the right node then we bias rebalancing to + // fill up the left node. + field_type to_move = + (kNodeSlots - left->count()) / + (1 + (static_cast(insert_position) < kNodeSlots)); + to_move = (std::max)(field_type{1}, to_move); + + if (static_cast(insert_position) - to_move >= + node->start() || + left->count() + to_move < kNodeSlots) { + left->rebalance_right_to_left(to_move, node, mutable_allocator()); + + assert(node->max_count() - node->count() == to_move); + insert_position = static_cast( + static_cast(insert_position) - to_move); + if (insert_position < node->start()) { + insert_position = insert_position + left->count() + 1; + node = left; + } + + assert(node->count() < node->max_count()); + return; + } + } + } + + if (node->position() < parent->finish()) { + // Try rebalancing with our right sibling. + node_type *right = parent->child(node->position() + 1); + assert(right->max_count() == kNodeSlots); + if (right->count() < kNodeSlots) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the beginning of the left node then we bias rebalancing + // to fill up the right node. + field_type to_move = (kNodeSlots - right->count()) / + (1 + (insert_position > node->start())); + to_move = (std::max)(field_type{1}, to_move); + + if (static_cast(insert_position) <= + node->finish() - to_move || + right->count() + to_move < kNodeSlots) { + node->rebalance_left_to_right(to_move, right, mutable_allocator()); + + if (insert_position > node->finish()) { + insert_position = insert_position - node->count() - 1; + node = right; + } + + assert(node->count() < node->max_count()); + return; + } + } + } + + // Rebalancing failed, make sure there is room on the parent node for a new + // value. + assert(parent->max_count() == kNodeSlots); + if (parent->count() == kNodeSlots) { + iterator parent_iter(parent, node->position()); + rebalance_or_split(&parent_iter); + parent = node->parent(); + } + } else { + // Rebalancing not possible because this is the root node. + // Create a new root node and set the current root node as the child of the + // new root. + parent = new_internal_node(/*position=*/0, parent); + parent->set_generation(root()->generation()); + parent->init_child(parent->start(), node); + mutable_root() = parent; + // If the former root was a leaf node, then it's now the rightmost node. + assert(parent->start_child()->is_internal() || + parent->start_child() == rightmost()); + } + + // Split the node. + node_type *split_node; + if (node->is_leaf()) { + split_node = new_leaf_node(node->position() + 1, parent); + node->split(insert_position, split_node, mutable_allocator()); + if (rightmost() == node) mutable_rightmost() = split_node; + } else { + split_node = new_internal_node(node->position() + 1, parent); + node->split(insert_position, split_node, mutable_allocator()); + } + + if (insert_position > node->finish()) { + insert_position = insert_position - node->count() - 1; + node = split_node; + } +} + +template +void btree

::merge_nodes(node_type *left, node_type *right) { + left->merge(right, mutable_allocator()); + if (rightmost() == right) mutable_rightmost() = left; +} + +template +bool btree

::try_merge_or_rebalance(iterator *iter) { + node_type *parent = iter->node_->parent(); + if (iter->node_->position() > parent->start()) { + // Try merging with our left sibling. + node_type *left = parent->child(iter->node_->position() - 1); + assert(left->max_count() == kNodeSlots); + if (1U + left->count() + iter->node_->count() <= kNodeSlots) { + iter->position_ += 1 + left->count(); + merge_nodes(left, iter->node_); + iter->node_ = left; + return true; + } + } + if (iter->node_->position() < parent->finish()) { + // Try merging with our right sibling. + node_type *right = parent->child(iter->node_->position() + 1); + assert(right->max_count() == kNodeSlots); + if (1U + iter->node_->count() + right->count() <= kNodeSlots) { + merge_nodes(iter->node_, right); + return true; + } + // Try rebalancing with our right sibling. We don't perform rebalancing if + // we deleted the first element from iter->node_ and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the front of the tree. + if (right->count() > kMinNodeValues && + (iter->node_->count() == 0 || iter->position_ > iter->node_->start())) { + field_type to_move = (right->count() - iter->node_->count()) / 2; + to_move = + (std::min)(to_move, static_cast(right->count() - 1)); + iter->node_->rebalance_right_to_left(to_move, right, mutable_allocator()); + return false; + } + } + if (iter->node_->position() > parent->start()) { + // Try rebalancing with our left sibling. We don't perform rebalancing if + // we deleted the last element from iter->node_ and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the back of the tree. + node_type *left = parent->child(iter->node_->position() - 1); + if (left->count() > kMinNodeValues && + (iter->node_->count() == 0 || + iter->position_ < iter->node_->finish())) { + field_type to_move = (left->count() - iter->node_->count()) / 2; + to_move = (std::min)(to_move, static_cast(left->count() - 1)); + left->rebalance_left_to_right(to_move, iter->node_, mutable_allocator()); + iter->position_ += to_move; + return false; + } + } + return false; +} + +template +void btree

::try_shrink() { + node_type *orig_root = root(); + if (orig_root->count() > 0) { + return; + } + // Deleted the last item on the root node, shrink the height of the tree. + if (orig_root->is_leaf()) { + assert(size() == 0); + mutable_root() = mutable_rightmost() = EmptyNode(); + } else { + node_type *child = orig_root->start_child(); + child->make_root(); + mutable_root() = child; + } + node_type::clear_and_delete(orig_root, mutable_allocator()); +} + +template +template +inline IterType btree

::internal_last(IterType iter) { + assert(iter.node_ != nullptr); + while (iter.position_ == iter.node_->finish()) { + iter.position_ = iter.node_->position(); + iter.node_ = iter.node_->parent(); + if (iter.node_->is_leaf()) { + iter.node_ = nullptr; + break; + } + } + iter.update_generation(); + return iter; +} + +template +template +inline auto btree

::internal_emplace(iterator iter, Args &&...args) + -> iterator { + if (iter.node_->is_internal()) { + // We can't insert on an internal node. Instead, we'll insert after the + // previous value which is guaranteed to be on a leaf node. + --iter; + ++iter.position_; + } + const field_type max_count = iter.node_->max_count(); + allocator_type *alloc = mutable_allocator(); + + const auto transfer_and_delete = [&](node_type *old_node, + node_type *new_node) { + new_node->transfer_n(old_node->count(), new_node->start(), + old_node->start(), old_node, alloc); + new_node->set_finish(old_node->finish()); + old_node->set_finish(old_node->start()); + new_node->set_generation(old_node->generation()); + node_type::clear_and_delete(old_node, alloc); + }; + const auto replace_leaf_root_node = [&](field_type new_node_size) { + assert(iter.node_ == root()); + node_type *old_root = iter.node_; + node_type *new_root = iter.node_ = new_leaf_root_node(new_node_size); + transfer_and_delete(old_root, new_root); + mutable_root() = mutable_rightmost() = new_root; + }; + + bool replaced_node = false; + if (iter.node_->count() == max_count) { + // Make room in the leaf for the new item. + if (max_count < kNodeSlots) { + // Insertion into the root where the root is smaller than the full node + // size. Simply grow the size of the root node. + replace_leaf_root_node(static_cast( + (std::min)(static_cast(kNodeSlots), 2 * max_count))); + replaced_node = true; + } else { + rebalance_or_split(&iter); + } + } + (void)replaced_node; +#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_HWADDRESS_SANITIZER) + if (!replaced_node) { + assert(iter.node_->is_leaf()); + if (iter.node_->is_root()) { + replace_leaf_root_node(max_count); + } else { + node_type *old_node = iter.node_; + const bool was_rightmost = rightmost() == old_node; + const bool was_leftmost = leftmost() == old_node; + node_type *parent = old_node->parent(); + const field_type position = old_node->position(); + node_type *new_node = iter.node_ = new_leaf_node(position, parent); + parent->set_child_noupdate_position(position, new_node); + transfer_and_delete(old_node, new_node); + if (was_rightmost) mutable_rightmost() = new_node; + // The leftmost node is stored as the parent of the root node. + if (was_leftmost) root()->set_parent(new_node); + } + } +#endif + iter.node_->emplace_value(static_cast(iter.position_), alloc, + std::forward(args)...); + assert( + iter.node_->is_ordered_correctly(static_cast(iter.position_), + original_key_compare(key_comp())) && + "If this assert fails, then either (1) the comparator may violate " + "transitivity, i.e. comp(a,b) && comp(b,c) -> comp(a,c) (see " + "https://en.cppreference.com/w/cpp/named_req/Compare), or (2) a " + "key may have been mutated after it was inserted into the tree."); + ++size_; + iter.update_generation(); + return iter; +} + +template +template +inline auto btree

::internal_locate(const K &key) const + -> SearchResult { + iterator iter(const_cast(root())); + for (;;) { + SearchResult res = + iter.node_->lower_bound(key, key_comp()); + iter.position_ = static_cast(res.value); + if (res.IsEq()) { + return {iter, MatchKind::kEq}; + } + // Note: in the non-key-compare-to case, we don't need to walk all the way + // down the tree if the keys are equal, but determining equality would + // require doing an extra comparison on each node on the way down, and we + // will need to go all the way to the leaf node in the expected case. + if (iter.node_->is_leaf()) { + break; + } + iter.node_ = iter.node_->child(static_cast(iter.position_)); + } + // Note: in the non-key-compare-to case, the key may actually be equivalent + // here (and the MatchKind::kNe is ignored). + return {iter, MatchKind::kNe}; +} + +template +template +auto btree

::internal_lower_bound(const K &key) const + -> SearchResult { + if (!params_type::template can_have_multiple_equivalent_keys()) { + SearchResult ret = internal_locate(key); + ret.value = internal_last(ret.value); + return ret; + } + iterator iter(const_cast(root())); + SearchResult res; + bool seen_eq = false; + for (;;) { + res = iter.node_->lower_bound(key, key_comp()); + iter.position_ = static_cast(res.value); + if (iter.node_->is_leaf()) { + break; + } + seen_eq = seen_eq || res.IsEq(); + iter.node_ = iter.node_->child(static_cast(iter.position_)); + } + if (res.IsEq()) return {iter, MatchKind::kEq}; + return {internal_last(iter), seen_eq ? MatchKind::kEq : MatchKind::kNe}; +} + +template +template +auto btree

::internal_upper_bound(const K &key) const -> iterator { + iterator iter(const_cast(root())); + for (;;) { + iter.position_ = static_cast(iter.node_->upper_bound(key, key_comp())); + if (iter.node_->is_leaf()) { + break; + } + iter.node_ = iter.node_->child(static_cast(iter.position_)); + } + return internal_last(iter); +} + +template +template +auto btree

::internal_find(const K &key) const -> iterator { + SearchResult res = internal_locate(key); + if (res.HasMatch()) { + if (res.IsEq()) { + return res.value; + } + } else { + const iterator iter = internal_last(res.value); + if (iter.node_ != nullptr && !compare_keys(key, iter.key())) { + return iter; + } + } + return {nullptr, 0}; +} + +template +typename btree

::size_type btree

::internal_verify( + const node_type *node, const key_type *lo, const key_type *hi) const { + assert(node->count() > 0); + assert(node->count() <= node->max_count()); + if (lo) { + assert(!compare_keys(node->key(node->start()), *lo)); + } + if (hi) { + assert(!compare_keys(*hi, node->key(node->finish() - 1))); + } + for (int i = node->start() + 1; i < node->finish(); ++i) { + assert(!compare_keys(node->key(i), node->key(i - 1))); + } + size_type count = node->count(); + if (node->is_internal()) { + for (field_type i = node->start(); i <= node->finish(); ++i) { + assert(node->child(i) != nullptr); + assert(node->child(i)->parent() == node); + assert(node->child(i)->position() == i); + count += internal_verify(node->child(i), + i == node->start() ? lo : &node->key(i - 1), + i == node->finish() ? hi : &node->key(i)); + } + } + return count; +} + +struct btree_access { + template + static auto erase_if(BtreeContainer &container, Pred pred) -> + typename BtreeContainer::size_type { + const auto initial_size = container.size(); + auto &tree = container.tree_; + auto *alloc = tree.mutable_allocator(); + for (auto it = container.begin(); it != container.end();) { + if (!pred(*it)) { + ++it; + continue; + } + auto *node = it.node_; + if (node->is_internal()) { + // Handle internal nodes normally. + it = container.erase(it); + continue; + } + // If this is a leaf node, then we do all the erases from this node + // at once before doing rebalancing. + + // The current position to transfer slots to. + int to_pos = it.position_; + node->value_destroy(it.position_, alloc); + while (++it.position_ < node->finish()) { + it.update_generation(); + if (pred(*it)) { + node->value_destroy(it.position_, alloc); + } else { + node->transfer(node->slot(to_pos++), node->slot(it.position_), alloc); + } + } + const int num_deleted = node->finish() - to_pos; + tree.size_ -= num_deleted; + node->set_finish(to_pos); + it.position_ = to_pos; + it = tree.rebalance_after_delete(it); + } + return initial_size - container.size(); + } +}; + +#undef ABSL_BTREE_ENABLE_GENERATIONS + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_BTREE_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/btree_container.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/btree_container.h new file mode 100644 index 00000000..21f00ae4 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/btree_container.h @@ -0,0 +1,867 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ +#define ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/internal/throw_delegate.h" +#include "absl/container/internal/btree.h" // IWYU pragma: export +#include "absl/container/internal/common.h" +#include "absl/hash/internal/weakly_mixed_integer.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A common base class for btree_set, btree_map, btree_multiset, and +// btree_multimap. +template +class btree_container { + using params_type = typename Tree::params_type; + + protected: + // Alias used for heterogeneous lookup functions. + // `key_arg` evaluates to `K` when the functors are transparent and to + // `key_type` otherwise. It permits template argument deduction on `K` for the + // transparent case. + template + using key_arg = + typename KeyArg::template type< + K, typename Tree::key_type>; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using difference_type = typename Tree::difference_type; + using key_compare = typename Tree::original_key_compare; + using value_compare = typename Tree::value_compare; + using allocator_type = typename Tree::allocator_type; + using reference = typename Tree::reference; + using const_reference = typename Tree::const_reference; + using pointer = typename Tree::pointer; + using const_pointer = typename Tree::const_pointer; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using reverse_iterator = typename Tree::reverse_iterator; + using const_reverse_iterator = typename Tree::const_reverse_iterator; + using node_type = typename Tree::node_handle_type; + + struct extract_and_get_next_return_type { + node_type node; + iterator next; + }; + + // Constructors/assignments. + btree_container() : tree_(key_compare(), allocator_type()) {} + explicit btree_container(const key_compare &comp, + const allocator_type &alloc = allocator_type()) + : tree_(comp, alloc) {} + explicit btree_container(const allocator_type &alloc) + : tree_(key_compare(), alloc) {} + + btree_container(const btree_container &other) + : btree_container(other, absl::allocator_traits:: + select_on_container_copy_construction( + other.get_allocator())) {} + btree_container(const btree_container &other, const allocator_type &alloc) + : tree_(other.tree_, alloc) {} + + btree_container(btree_container &&other) noexcept( + std::is_nothrow_move_constructible::value) = default; + btree_container(btree_container &&other, const allocator_type &alloc) + : tree_(std::move(other.tree_), alloc) {} + + btree_container &operator=(const btree_container &other) = default; + btree_container &operator=(btree_container &&other) noexcept( + std::is_nothrow_move_assignable::value) = default; + + // Iterator routines. + iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return tree_.begin(); } + const_iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.begin(); + } + const_iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.begin(); + } + iterator end() ABSL_ATTRIBUTE_LIFETIME_BOUND { return tree_.end(); } + const_iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.end(); + } + const_iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.end(); + } + reverse_iterator rbegin() ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.rbegin(); + } + const_reverse_iterator rbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.rbegin(); + } + const_reverse_iterator crbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.rbegin(); + } + reverse_iterator rend() ABSL_ATTRIBUTE_LIFETIME_BOUND { return tree_.rend(); } + const_reverse_iterator rend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.rend(); + } + const_reverse_iterator crend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.rend(); + } + + // Lookup routines. + template + size_type count(const key_arg &key) const { + auto equal_range = this->equal_range(key); + return equal_range.second - equal_range.first; + } + template + iterator find(const key_arg &key) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.find(key); + } + template + const_iterator find(const key_arg &key) const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.find(key); + } + template + bool contains(const key_arg &key) const { + return find(key) != end(); + } + template + iterator lower_bound(const key_arg &key) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.lower_bound(key); + } + template + const_iterator lower_bound(const key_arg &key) const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.lower_bound(key); + } + template + iterator upper_bound(const key_arg &key) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.upper_bound(key); + } + template + const_iterator upper_bound(const key_arg &key) const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.upper_bound(key); + } + template + std::pair equal_range(const key_arg &key) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.equal_range(key); + } + template + std::pair equal_range( + const key_arg &key) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.equal_range(key); + } + + // Deletion routines. Note that there is also a deletion routine that is + // specific to btree_set_container/btree_multiset_container. + + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + iterator erase(const_iterator iter) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.erase(iterator(iter)); + } + iterator erase(iterator iter) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.erase(iter); + } + iterator erase(const_iterator first, + const_iterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return tree_.erase_range(iterator(first), iterator(last)).second; + } + template + size_type erase(const key_arg &key) { + auto equal_range = this->equal_range(key); + return tree_.erase_range(equal_range.first, equal_range.second).first; + } + + // Extract routines. + extract_and_get_next_return_type extract_and_get_next(const_iterator position) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // Use Construct instead of Transfer because the rebalancing code will + // destroy the slot later. + // Note: we rely on erase() taking place after Construct(). + return {CommonAccess::Construct(get_allocator(), + iterator(position).slot()), + erase(position)}; + } + node_type extract(iterator position) { + // Use Construct instead of Transfer because the rebalancing code will + // destroy the slot later. + auto node = + CommonAccess::Construct(get_allocator(), position.slot()); + erase(position); + return node; + } + node_type extract(const_iterator position) { + return extract(iterator(position)); + } + + // Utility routines. + ABSL_ATTRIBUTE_REINITIALIZES void clear() { tree_.clear(); } + void swap(btree_container &other) { tree_.swap(other.tree_); } + void verify() const { tree_.verify(); } + + // Size routines. + size_type size() const { return tree_.size(); } + size_type max_size() const { return tree_.max_size(); } + bool empty() const { return tree_.empty(); } + + friend bool operator==(const btree_container &x, const btree_container &y) { + if (x.size() != y.size()) return false; + return std::equal(x.begin(), x.end(), y.begin()); + } + + friend bool operator!=(const btree_container &x, const btree_container &y) { + return !(x == y); + } + + friend bool operator<(const btree_container &x, const btree_container &y) { + return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); + } + + friend bool operator>(const btree_container &x, const btree_container &y) { + return y < x; + } + + friend bool operator<=(const btree_container &x, const btree_container &y) { + return !(y < x); + } + + friend bool operator>=(const btree_container &x, const btree_container &y) { + return !(x < y); + } + + // The allocator used by the btree. + allocator_type get_allocator() const { return tree_.get_allocator(); } + + // The key comparator used by the btree. + key_compare key_comp() const { return key_compare(tree_.key_comp()); } + value_compare value_comp() const { return tree_.value_comp(); } + + // Support absl::Hash. + template + friend State AbslHashValue(State h, const btree_container &b) { + for (const auto &v : b) { + h = State::combine(std::move(h), v); + } + return State::combine(std::move(h), + hash_internal::WeaklyMixedInteger{b.size()}); + } + + protected: + friend struct btree_access; + Tree tree_; +}; + +// A common base class for btree_set and btree_map. +template +class btree_set_container : public btree_container { + using super_type = btree_container; + using params_type = typename Tree::params_type; + using init_type = typename params_type::init_type; + using is_key_compare_to = typename params_type::is_key_compare_to; + friend class BtreeNodePeer; + + protected: + template + using key_arg = typename super_type::template key_arg; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using key_compare = typename Tree::original_key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using node_type = typename super_type::node_type; + using insert_return_type = InsertReturnType; + + // Inherit constructors. + using super_type::super_type; + btree_set_container() {} + + // Range constructors. + template + btree_set_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + template + btree_set_container(InputIterator b, InputIterator e, + const allocator_type &alloc) + : btree_set_container(b, e, key_compare(), alloc) {} + + // Initializer list constructors. + btree_set_container(std::initializer_list init, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : btree_set_container(init.begin(), init.end(), comp, alloc) {} + btree_set_container(std::initializer_list init, + const allocator_type &alloc) + : btree_set_container(init.begin(), init.end(), alloc) {} + + // Insertion routines. + std::pair insert(const value_type &v) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->tree_.insert_unique(params_type::key(v), v); + } + std::pair insert(value_type &&v) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->tree_.insert_unique(params_type::key(v), std::move(v)); + } + template + std::pair emplace(Args &&...args) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + auto *slot = CommonAccess::GetSlot(node); + return this->tree_.insert_unique(params_type::key(slot), slot); + } + iterator insert(const_iterator hint, + const value_type &v) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->tree_ + .insert_hint_unique(iterator(hint), params_type::key(v), v) + .first; + } + iterator insert(const_iterator hint, + value_type &&v) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->tree_ + .insert_hint_unique(iterator(hint), params_type::key(v), std::move(v)) + .first; + } + template + iterator emplace_hint(const_iterator hint, + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + auto *slot = CommonAccess::GetSlot(node); + return this->tree_ + .insert_hint_unique(iterator(hint), params_type::key(slot), slot) + .first; + } + template + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_iterator_unique(b, e, 0); + } + void insert(std::initializer_list init) { + this->tree_.insert_iterator_unique(init.begin(), init.end(), 0); + } + insert_return_type insert(node_type &&node) ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (!node) return {this->end(), false, node_type()}; + std::pair res = + this->tree_.insert_unique(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + if (res.second) { + CommonAccess::Destroy(&node); + return {res.first, true, node_type()}; + } else { + return {res.first, false, std::move(node)}; + } + } + iterator insert(const_iterator hint, + node_type &&node) ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (!node) return this->end(); + std::pair res = this->tree_.insert_hint_unique( + iterator(hint), params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + if (res.second) CommonAccess::Destroy(&node); + return res.first; + } + + // Node extraction routines. + template + node_type extract(const key_arg &key) { + const std::pair lower_and_equal = + this->tree_.lower_bound_equal(key); + return lower_and_equal.second ? extract(lower_and_equal.first) + : node_type(); + } + using super_type::extract; + + // Merge routines. + // Moves elements from `src` into `this`. If the element already exists in + // `this`, it is left unmodified in `src`. + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &src) { // NOLINT + for (auto src_it = src.begin(); src_it != src.end();) { + if (insert(std::move(params_type::element(src_it.slot()))).second) { + src_it = src.erase(src_it); + } else { + ++src_it; + } + } + } + + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &&src) { + merge(src); + } +}; + +// Base class for btree_map. +template +class btree_map_container : public btree_set_container { + using super_type = btree_set_container; + using params_type = typename Tree::params_type; + friend class BtreeNodePeer; + + private: + template + using key_arg = typename super_type::template key_arg; + + // NOTE: The mess here is to shorten the code for the (very repetitive) + // function overloads, and to allow the lifetime-bound overloads to dispatch + // to the non-lifetime-bound overloads, to ensure there is a single source of + // truth for each overload set. + // + // Enabled if an assignment from the given type would require the + // source object to remain alive for the life of the element. + // + // TODO(b/402804213): Remove these traits and simplify the overloads whenever + // we have a better mechanism available to handle lifetime analysis. + template + using LifetimeBoundK = + HasValue>; + template + using LifetimeBoundV = + HasValue>; + template + using LifetimeBoundKV = + absl::conjunction>, + LifetimeBoundV>; + + public: + using key_type = typename Tree::key_type; + using mapped_type = typename params_type::mapped_type; + using value_type = typename Tree::value_type; + using key_compare = typename Tree::original_key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + + // Inherit constructors. + using super_type::super_type; + btree_map_container() {} + + // TODO(b/402804213): Remove these macros whenever we have a better mechanism + // available to handle lifetime analysis. +#define ABSL_INTERNAL_X(Func, Callee, KQual, MQual, KValue, MValue, ...) \ + template < \ + typename K = key_type, class M, \ + ABSL_INTERNAL_IF_##KValue##_NOR_##MValue( \ + int = (EnableIf::AddPtr, \ + IfRRef::AddPtr>>()), \ + ABSL_INTERNAL_SINGLE_ARG( \ + int &..., \ + decltype(EnableIf>()) = \ + 0))> \ + decltype(auto) Func( \ + __VA_ARGS__ key_arg KQual k ABSL_INTERNAL_IF_##KValue( \ + ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)), \ + M MQual obj ABSL_INTERNAL_IF_##MValue( \ + ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this))) \ + ABSL_ATTRIBUTE_LIFETIME_BOUND { \ + return ABSL_INTERNAL_IF_##KValue##_OR_##MValue( \ + (this->template Func), Callee)( \ + __VA_ARGS__ std::forward(k), \ + std::forward(obj)); \ + } \ + friend struct std::enable_if /* just to force a semicolon */ + // Insertion routines. + // Note: the nullptr template arguments and extra `const M&` overloads allow + // for supporting bitfield arguments. + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, true); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, true); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + true); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + true); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + true); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + true); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, + false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, true); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, true); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, + const &, false, false, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, + const &, false, true, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, + const &, true, false, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, + const &, true, true, + const_iterator(hint) ABSL_INTERNAL_COMMA); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, &&, + false, false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, &&, + false, true, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, &&, + true, false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, &&, + true, true, const_iterator(hint) ABSL_INTERNAL_COMMA); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, const &, + false, false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, const &, + false, true, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, const &, + true, false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, const &, + true, true, const_iterator(hint) ABSL_INTERNAL_COMMA); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, &&, false, + false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, &&, false, + true, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, &&, true, + false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, &&, true, + true, const_iterator(hint) ABSL_INTERNAL_COMMA); +#undef ABSL_INTERNAL_X + +#define ABSL_INTERNAL_X(Func, Callee, KQual, KValue, ...) \ + template < \ + class K = key_type, \ + ABSL_INTERNAL_IF_##KValue( \ + class... Args, \ + int = (EnableIf< \ + LifetimeBoundK::AddPtr>>())), \ + ABSL_INTERNAL_IF_##KValue( \ + decltype(EnableIf::AddPtr>>()) = 0, \ + class... Args), \ + std::enable_if_t::value, int> = \ + 0> \ + decltype(auto) Func( \ + __VA_ARGS__ key_arg KQual k ABSL_INTERNAL_IF_##KValue( \ + ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)), \ + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { \ + return ABSL_INTERNAL_IF_##KValue((this->template Func), Callee)( \ + __VA_ARGS__ std::forward(k), \ + std::forward(args)...); \ + } \ + friend struct std::enable_if /* just to force a semicolon */ + ABSL_INTERNAL_X(try_emplace, try_emplace_impl, const &, false); + ABSL_INTERNAL_X(try_emplace, try_emplace_impl, const &, true); + ABSL_INTERNAL_X(try_emplace, try_emplace_impl, &&, false); + ABSL_INTERNAL_X(try_emplace, try_emplace_impl, &&, true); + ABSL_INTERNAL_X(try_emplace, try_emplace_hint_impl, const &, false, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(try_emplace, try_emplace_hint_impl, const &, true, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(try_emplace, try_emplace_hint_impl, &&, false, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(try_emplace, try_emplace_hint_impl, &&, true, + const_iterator(hint) ABSL_INTERNAL_COMMA); +#undef ABSL_INTERNAL_X + + template >()> + mapped_type &operator[](const key_arg &k) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return try_emplace(k).first->second; + } + template > = 0> + mapped_type &operator[]( + const key_arg &k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template operator[](k); + } + template >()> + mapped_type &operator[](key_arg &&k) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return try_emplace(std::forward(k)).first->second; + } + template > = 0> + mapped_type &operator[](key_arg &&k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY( + this)) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template operator[](std::forward(k)); + } + + template + mapped_type &at(const key_arg &key) ABSL_ATTRIBUTE_LIFETIME_BOUND { + auto it = this->find(key); + if (it == this->end()) + base_internal::ThrowStdOutOfRange("absl::btree_map::at"); + return it->second; + } + template + const mapped_type &at(const key_arg &key) const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + auto it = this->find(key); + if (it == this->end()) + base_internal::ThrowStdOutOfRange("absl::btree_map::at"); + return it->second; + } + + private: + // Note: when we call `std::forward(obj)` twice, it's safe because + // insert_unique/insert_hint_unique are guaranteed to not consume `obj` when + // `ret.second` is false. + template + std::pair insert_or_assign_impl(K &&k, M &&obj) { + const std::pair ret = + this->tree_.insert_unique(k, std::forward(k), std::forward(obj)); + if (!ret.second) ret.first->second = std::forward(obj); + return ret; + } + template + iterator insert_or_assign_hint_impl(const_iterator hint, K &&k, M &&obj) { + const std::pair ret = this->tree_.insert_hint_unique( + iterator(hint), k, std::forward(k), std::forward(obj)); + if (!ret.second) ret.first->second = std::forward(obj); + return ret.first; + } + + template + std::pair try_emplace_impl(K &&k, Args &&... args) { + return this->tree_.insert_unique( + k, std::piecewise_construct, std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)); + } + template + iterator try_emplace_hint_impl(const_iterator hint, K &&k, Args &&... args) { + return this->tree_ + .insert_hint_unique(iterator(hint), k, std::piecewise_construct, + std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)) + .first; + } +}; + +// A common base class for btree_multiset and btree_multimap. +template +class btree_multiset_container : public btree_container { + using super_type = btree_container; + using params_type = typename Tree::params_type; + using init_type = typename params_type::init_type; + using is_key_compare_to = typename params_type::is_key_compare_to; + friend class BtreeNodePeer; + + template + using key_arg = typename super_type::template key_arg; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using key_compare = typename Tree::original_key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using node_type = typename super_type::node_type; + + // Inherit constructors. + using super_type::super_type; + btree_multiset_container() {} + + // Range constructors. + template + btree_multiset_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + template + btree_multiset_container(InputIterator b, InputIterator e, + const allocator_type &alloc) + : btree_multiset_container(b, e, key_compare(), alloc) {} + + // Initializer list constructors. + btree_multiset_container(std::initializer_list init, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : btree_multiset_container(init.begin(), init.end(), comp, alloc) {} + btree_multiset_container(std::initializer_list init, + const allocator_type &alloc) + : btree_multiset_container(init.begin(), init.end(), alloc) {} + + // Insertion routines. + iterator insert(const value_type &v) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->tree_.insert_multi(v); + } + iterator insert(value_type &&v) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->tree_.insert_multi(std::move(v)); + } + iterator insert(const_iterator hint, + const value_type &v) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->tree_.insert_hint_multi(iterator(hint), v); + } + iterator insert(const_iterator hint, + value_type &&v) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->tree_.insert_hint_multi(iterator(hint), std::move(v)); + } + template + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_iterator_multi(b, e); + } + void insert(std::initializer_list init) { + this->tree_.insert_iterator_multi(init.begin(), init.end()); + } + template + iterator emplace(Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + return this->tree_.insert_multi(CommonAccess::GetSlot(node)); + } + template + iterator emplace_hint(const_iterator hint, + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + return this->tree_.insert_hint_multi(iterator(hint), + CommonAccess::GetSlot(node)); + } + iterator insert(node_type &&node) ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (!node) return this->end(); + iterator res = + this->tree_.insert_multi(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + CommonAccess::Destroy(&node); + return res; + } + iterator insert(const_iterator hint, + node_type &&node) ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (!node) return this->end(); + iterator res = this->tree_.insert_hint_multi( + iterator(hint), + std::move(params_type::element(CommonAccess::GetSlot(node)))); + CommonAccess::Destroy(&node); + return res; + } + + // Node extraction routines. + template + node_type extract(const key_arg &key) { + const std::pair lower_and_equal = + this->tree_.lower_bound_equal(key); + return lower_and_equal.second ? extract(lower_and_equal.first) + : node_type(); + } + using super_type::extract; + + // Merge routines. + // Moves all elements from `src` into `this`. + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &src) { // NOLINT + for (auto src_it = src.begin(), end = src.end(); src_it != end; ++src_it) { + insert(std::move(params_type::element(src_it.slot()))); + } + src.clear(); + } + + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &&src) { + merge(src); + } +}; + +// A base class for btree_multimap. +template +class btree_multimap_container : public btree_multiset_container { + using super_type = btree_multiset_container; + using params_type = typename Tree::params_type; + friend class BtreeNodePeer; + + public: + using mapped_type = typename params_type::mapped_type; + + // Inherit constructors. + using super_type::super_type; + btree_multimap_container() {} +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/common.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/common.h new file mode 100644 index 00000000..5ef6c569 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/common.h @@ -0,0 +1,250 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_COMMON_H_ +#define ABSL_CONTAINER_INTERNAL_COMMON_H_ + +#include +#include + +#include "absl/meta/type_traits.h" +#include "absl/types/optional.h" + +// TODO(b/402804213): Clean up these macros when no longer needed. +#define ABSL_INTERNAL_SINGLE_ARG(...) __VA_ARGS__ + +#define ABSL_INTERNAL_IF_true(if_satisfied, ...) if_satisfied +#define ABSL_INTERNAL_IF_false(if_satisfied, ...) __VA_ARGS__ + +#define ABSL_INTERNAL_IF_true_AND_true ABSL_INTERNAL_IF_true +#define ABSL_INTERNAL_IF_false_AND_false ABSL_INTERNAL_IF_false +#define ABSL_INTERNAL_IF_true_AND_false ABSL_INTERNAL_IF_false_AND_false +#define ABSL_INTERNAL_IF_false_AND_true ABSL_INTERNAL_IF_false_AND_false + +#define ABSL_INTERNAL_IF_true_OR_true ABSL_INTERNAL_IF_true +#define ABSL_INTERNAL_IF_false_OR_false ABSL_INTERNAL_IF_false +#define ABSL_INTERNAL_IF_true_OR_false ABSL_INTERNAL_IF_true_OR_true +#define ABSL_INTERNAL_IF_false_OR_true ABSL_INTERNAL_IF_true_OR_true + +#define ABSL_INTERNAL_IF_true_NOR_true ABSL_INTERNAL_IF_false_AND_false +#define ABSL_INTERNAL_IF_false_NOR_false ABSL_INTERNAL_IF_true_AND_true +#define ABSL_INTERNAL_IF_true_NOR_false ABSL_INTERNAL_IF_false_AND_true +#define ABSL_INTERNAL_IF_false_NOR_true ABSL_INTERNAL_IF_true_AND_false + +#define ABSL_INTERNAL_COMMA , + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// TODO(b/402804213): Clean up these traits when no longer needed or +// deduplicate them with absl::functional_internal::EnableIf. +template +using EnableIf = std::enable_if_t; + +template +using HasValue = std::conditional_t>; + +template +struct IfRRef { + template + using AddPtr = Other; +}; + +template +struct IfRRef { + template + using AddPtr = Other*; +}; + +template +struct IsTransparent : std::false_type {}; +template +struct IsTransparent> + : std::true_type {}; + +template +struct KeyArg { + // Transparent. Forward `K`. + template + using type = K; +}; + +template <> +struct KeyArg { + // Not transparent. Always use `key_type`. + template + using type = key_type; +}; + +// The node_handle concept from C++17. +// We specialize node_handle for sets and maps. node_handle_base holds the +// common API of both. +template +class node_handle_base { + protected: + using slot_type = typename PolicyTraits::slot_type; + + public: + using allocator_type = Alloc; + + constexpr node_handle_base() = default; + node_handle_base(node_handle_base&& other) noexcept { + *this = std::move(other); + } + ~node_handle_base() { destroy(); } + node_handle_base& operator=(node_handle_base&& other) noexcept { + destroy(); + if (!other.empty()) { + alloc_ = other.alloc_; + PolicyTraits::transfer(alloc(), slot(), other.slot()); + other.reset(); + } + return *this; + } + + bool empty() const noexcept { return !alloc_; } + explicit operator bool() const noexcept { return !empty(); } + allocator_type get_allocator() const { return *alloc_; } + + protected: + friend struct CommonAccess; + + struct transfer_tag_t {}; + node_handle_base(transfer_tag_t, const allocator_type& a, slot_type* s) + : alloc_(a) { + PolicyTraits::transfer(alloc(), slot(), s); + } + + struct construct_tag_t {}; + template + node_handle_base(construct_tag_t, const allocator_type& a, Args&&... args) + : alloc_(a) { + PolicyTraits::construct(alloc(), slot(), std::forward(args)...); + } + + void destroy() { + if (!empty()) { + PolicyTraits::destroy(alloc(), slot()); + reset(); + } + } + + void reset() { + assert(alloc_.has_value()); + alloc_ = absl::nullopt; + } + + slot_type* slot() const { + assert(!empty()); + return reinterpret_cast(std::addressof(slot_space_)); + } + allocator_type* alloc() { return std::addressof(*alloc_); } + + private: + absl::optional alloc_ = {}; + alignas(slot_type) mutable unsigned char slot_space_[sizeof(slot_type)] = {}; +}; + +// For sets. +template +class node_handle : public node_handle_base { + using Base = node_handle_base; + + public: + using value_type = typename PolicyTraits::value_type; + + constexpr node_handle() {} + + value_type& value() const { return PolicyTraits::element(this->slot()); } + + private: + friend struct CommonAccess; + + using Base::Base; +}; + +// For maps. +template +class node_handle> + : public node_handle_base { + using Base = node_handle_base; + using slot_type = typename PolicyTraits::slot_type; + + public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + + constexpr node_handle() {} + + // When C++17 is available, we can use std::launder to provide mutable + // access to the key. Otherwise, we provide const access. + auto key() const + -> decltype(PolicyTraits::mutable_key(std::declval())) { + return PolicyTraits::mutable_key(this->slot()); + } + + mapped_type& mapped() const { + return PolicyTraits::value(&PolicyTraits::element(this->slot())); + } + + private: + friend struct CommonAccess; + + using Base::Base; +}; + +// Provide access to non-public node-handle functions. +struct CommonAccess { + template + static auto GetSlot(const Node& node) -> decltype(node.slot()) { + return node.slot(); + } + + template + static void Destroy(Node* node) { + node->destroy(); + } + + template + static void Reset(Node* node) { + node->reset(); + } + + template + static T Transfer(Args&&... args) { + return T(typename T::transfer_tag_t{}, std::forward(args)...); + } + + template + static T Construct(Args&&... args) { + return T(typename T::construct_tag_t{}, std::forward(args)...); + } +}; + +// Implement the insert_return_type<> concept of C++17. +template +struct InsertReturnType { + Iterator position; + bool inserted; + NodeType node; +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_COMMON_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/common_policy_traits.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/common_policy_traits.h new file mode 100644 index 00000000..86e038e1 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/common_policy_traits.h @@ -0,0 +1,151 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_COMMON_POLICY_TRAITS_H_ +#define ABSL_CONTAINER_INTERNAL_COMMON_POLICY_TRAITS_H_ + +#include +#include +#include +#include +#include +#include + +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct policy_trait_element_is_owner : std::false_type {}; + +template +struct policy_trait_element_is_owner< + Policy, + std::enable_if_t::value>> + : Policy::element_is_owner {}; + +// Defines how slots are initialized/destroyed/moved. +template +struct common_policy_traits { + // The actual object stored in the container. + using slot_type = typename Policy::slot_type; + using reference = decltype(Policy::element(std::declval())); + using value_type = typename std::remove_reference::type; + + // PRECONDITION: `slot` is UNINITIALIZED + // POSTCONDITION: `slot` is INITIALIZED + template + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + Policy::construct(alloc, slot, std::forward(args)...); + } + + // PRECONDITION: `slot` is INITIALIZED + // POSTCONDITION: `slot` is UNINITIALIZED + // Returns std::true_type in case destroy is trivial. + template + static auto destroy(Alloc* alloc, slot_type* slot) { + return Policy::destroy(alloc, slot); + } + + // Transfers the `old_slot` to `new_slot`. Any memory allocated by the + // allocator inside `old_slot` to `new_slot` can be transferred. + // + // OPTIONAL: defaults to: + // + // clone(new_slot, std::move(*old_slot)); + // destroy(old_slot); + // + // PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED + // POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is + // UNINITIALIZED + template + static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) { + transfer_impl(alloc, new_slot, old_slot, Rank2{}); + } + + // PRECONDITION: `slot` is INITIALIZED + // POSTCONDITION: `slot` is INITIALIZED + // Note: we use remove_const_t so that the two overloads have different args + // in the case of sets with explicitly const value_types. + template + static auto element(absl::remove_const_t* slot) + -> decltype(P::element(slot)) { + return P::element(slot); + } + template + static auto element(const slot_type* slot) -> decltype(P::element(slot)) { + return P::element(slot); + } + + static constexpr bool transfer_uses_memcpy() { + return std::is_same>( + nullptr, nullptr, nullptr, Rank2{})), + std::true_type>::value; + } + + // Returns true if destroy is trivial and can be omitted. + template + static constexpr bool destroy_is_trivial() { + return std::is_same(nullptr, nullptr)), + std::true_type>::value; + } + + private: + // Use go/ranked-overloads for dispatching. + struct Rank0 {}; + struct Rank1 : Rank0 {}; + struct Rank2 : Rank1 {}; + + // Use auto -> decltype as an enabler. + // P::transfer returns std::true_type if transfer uses memcpy (e.g. in + // node_slot_policy). + template + static auto transfer_impl(Alloc* alloc, slot_type* new_slot, + slot_type* old_slot, + Rank2) -> decltype(P::transfer(alloc, new_slot, + old_slot)) { + return P::transfer(alloc, new_slot, old_slot); + } + + // This overload returns true_type for the trait below. + // The conditional_t is to make the enabler type dependent. + template >::value>> + static std::true_type transfer_impl(Alloc*, slot_type* new_slot, + slot_type* old_slot, Rank1) { + // TODO(b/247130232): remove casts after fixing warnings. + // TODO(b/251814870): remove casts after fixing warnings. + std::memcpy( + static_cast(std::launder( + const_cast*>(&element(new_slot)))), + static_cast(&element(old_slot)), sizeof(value_type)); + return {}; + } + + template + static void transfer_impl(Alloc* alloc, slot_type* new_slot, + slot_type* old_slot, Rank0) { + construct(alloc, new_slot, std::move(element(old_slot))); + destroy(alloc, old_slot); + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_COMMON_POLICY_TRAITS_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/compressed_tuple.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/compressed_tuple.h new file mode 100644 index 00000000..6db0468d --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/compressed_tuple.h @@ -0,0 +1,271 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Helper class to perform the Empty Base Optimization. +// Ts can contain classes and non-classes, empty or not. For the ones that +// are empty classes, we perform the optimization. If all types in Ts are empty +// classes, then CompressedTuple is itself an empty class. +// +// To access the members, use member get() function. +// +// Eg: +// absl::container_internal::CompressedTuple value(7, t1, t2, +// t3); +// assert(value.get<0>() == 7); +// T1& t1 = value.get<1>(); +// const T2& t2 = value.get<2>(); +// ... +// +// https://en.cppreference.com/w/cpp/language/ebo + +#ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ +#define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ + +#include +#include +#include +#include + +#include "absl/utility/utility.h" + +#if defined(_MSC_VER) && !defined(__NVCC__) +// We need to mark these classes with this declspec to ensure that +// CompressedTuple happens. +#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases) +#else +#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class CompressedTuple; + +namespace internal_compressed_tuple { + +template +struct Elem; +template +struct Elem, I> + : std::tuple_element> {}; +template +using ElemT = typename Elem::type; + +// We can't use EBCO on other CompressedTuples because that would mean that we +// derive from multiple Storage<> instantiations with the same I parameter, +// and potentially from multiple identical Storage<> instantiations. So anytime +// we use type inheritance rather than encapsulation, we mark +// CompressedTupleImpl, to make this easy to detect. +struct uses_inheritance {}; + +template +constexpr bool ShouldUseBase() { + return std::is_class::value && std::is_empty::value && + !std::is_final::value && + !std::is_base_of::value; +} + +// The storage class provides two specializations: +// - For empty classes, it stores T as a base class. +// - For everything else, it stores T as a member. +template ()> +struct Storage { + T value; + constexpr Storage() = default; + template + explicit constexpr Storage(absl::in_place_t, V&& v) + : value(std::forward(v)) {} + constexpr const T& get() const& { return value; } + constexpr T& get() & { return value; } + constexpr const T&& get() const&& { return std::move(*this).value; } + constexpr T&& get() && { return std::move(*this).value; } +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage : T { + constexpr Storage() = default; + + template + explicit constexpr Storage(absl::in_place_t, V&& v) : T(std::forward(v)) {} + + constexpr const T& get() const& { return *this; } + constexpr T& get() & { return *this; } + constexpr const T&& get() const&& { return std::move(*this); } + constexpr T&& get() && { return std::move(*this); } +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< + CompressedTuple, absl::index_sequence, ShouldAnyUseBase> + // We use the dummy identity function through std::integral_constant to + // convince MSVC of accepting and expanding I in that context. Without it + // you would get: + // error C3548: 'I': parameter pack cannot be used in this context + : uses_inheritance, + Storage::value>... { + constexpr CompressedTupleImpl() = default; + template + explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) + : Storage(absl::in_place, std::forward(args))... {} + friend CompressedTuple; +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< + CompressedTuple, absl::index_sequence, false> + // We use the dummy identity function as above... + : Storage::value, false>... { + constexpr CompressedTupleImpl() = default; + template + explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) + : Storage(absl::in_place, std::forward(args))... {} + friend CompressedTuple; +}; + +std::false_type Or(std::initializer_list); +std::true_type Or(std::initializer_list); + +// MSVC requires this to be done separately rather than within the declaration +// of CompressedTuple below. +template +constexpr bool ShouldAnyUseBase() { + return decltype( + Or({std::integral_constant()>()...})){}; +} + +template +using TupleElementMoveConstructible = + typename std::conditional::value, + std::is_convertible, + std::is_constructible>::type; + +template +struct TupleMoveConstructible : std::false_type {}; + +template +struct TupleMoveConstructible, Vs...> + : std::integral_constant< + bool, absl::conjunction< + TupleElementMoveConstructible...>::value> {}; + +template +struct compressed_tuple_size; + +template +struct compressed_tuple_size> + : public std::integral_constant {}; + +template +struct TupleItemsMoveConstructible + : std::integral_constant< + bool, TupleMoveConstructible::value == + sizeof...(Vs), + T, Vs...>::value> {}; + +} // namespace internal_compressed_tuple + +// Helper class to perform the Empty Base Class Optimization. +// Ts can contain classes and non-classes, empty or not. For the ones that +// are empty classes, we perform the CompressedTuple. If all types in Ts are +// empty classes, then CompressedTuple is itself an empty class. (This +// does not apply when one or more of those empty classes is itself an empty +// CompressedTuple.) +// +// To access the members, use member .get() function. +// +// Eg: +// absl::container_internal::CompressedTuple value(7, t1, t2, +// t3); +// assert(value.get<0>() == 7); +// T1& t1 = value.get<1>(); +// const T2& t2 = value.get<2>(); +// ... +// +// https://en.cppreference.com/w/cpp/language/ebo +template +class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple + : private internal_compressed_tuple::CompressedTupleImpl< + CompressedTuple, absl::index_sequence_for, + internal_compressed_tuple::ShouldAnyUseBase()> { + private: + template + using ElemT = internal_compressed_tuple::ElemT; + + template + using StorageT = internal_compressed_tuple::Storage, I>; + + public: + // There seems to be a bug in MSVC dealing in which using '=default' here will + // cause the compiler to ignore the body of other constructors. The work- + // around is to explicitly implement the default constructor. +#if defined(_MSC_VER) + constexpr CompressedTuple() : CompressedTuple::CompressedTupleImpl() {} +#else + constexpr CompressedTuple() = default; +#endif + explicit constexpr CompressedTuple(const Ts&... base) + : CompressedTuple::CompressedTupleImpl(absl::in_place, base...) {} + + template )>>, + internal_compressed_tuple::TupleItemsMoveConstructible< + CompressedTuple, First, Vs...>>::value, + bool> = true> + explicit constexpr CompressedTuple(First&& first, Vs&&... base) + : CompressedTuple::CompressedTupleImpl(absl::in_place, + std::forward(first), + std::forward(base)...) {} + + template + constexpr ElemT& get() & { + return StorageT::get(); + } + + template + constexpr const ElemT& get() const& { + return StorageT::get(); + } + + template + constexpr ElemT&& get() && { + return std::move(*this).StorageT::get(); + } + + template + constexpr const ElemT&& get() const&& { + return std::move(*this).StorageT::get(); + } +}; + +// Explicit specialization for a zero-element tuple +// (needed to avoid ambiguous overloads for the default constructor). +template <> +class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC + +#endif // ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/container_memory.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/container_memory.h new file mode 100644 index 00000000..e7ac1dba --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/container_memory.h @@ -0,0 +1,491 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ +#define ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +#include +#endif + +#ifdef ABSL_HAVE_MEMORY_SANITIZER +#include +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct alignas(Alignment) AlignedType {}; + +// Allocates at least n bytes aligned to the specified alignment. +// Alignment must be a power of 2. It must be positive. +// +// Note that many allocators don't honor alignment requirements above certain +// threshold (usually either alignof(std::max_align_t) or alignof(void*)). +// Allocate() doesn't apply alignment corrections. If the underlying allocator +// returns insufficiently alignment pointer, that's what you are going to get. +template +void* Allocate(Alloc* alloc, size_t n) { + static_assert(Alignment > 0, ""); + assert(n && "n must be positive"); + using M = AlignedType; + using A = typename absl::allocator_traits::template rebind_alloc; + using AT = typename absl::allocator_traits::template rebind_traits; + // On macOS, "mem_alloc" is a #define with one argument defined in + // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it + // with the "foo(bar)" syntax. + A my_mem_alloc(*alloc); + void* p = AT::allocate(my_mem_alloc, (n + sizeof(M) - 1) / sizeof(M)); + assert(reinterpret_cast(p) % Alignment == 0 && + "allocator does not respect alignment"); + return p; +} + +// Returns true if the destruction of the value with given Allocator will be +// trivial. +template +constexpr auto IsDestructionTrivial() { + constexpr bool result = + std::is_trivially_destructible::value && + std::is_same::template rebind_alloc, + std::allocator>::value; + return std::integral_constant(); +} + +// The pointer must have been previously obtained by calling +// Allocate(alloc, n). +template +void Deallocate(Alloc* alloc, void* p, size_t n) { + static_assert(Alignment > 0, ""); + assert(n && "n must be positive"); + using M = AlignedType; + using A = typename absl::allocator_traits::template rebind_alloc; + using AT = typename absl::allocator_traits::template rebind_traits; + // On macOS, "mem_alloc" is a #define with one argument defined in + // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it + // with the "foo(bar)" syntax. + A my_mem_alloc(*alloc); + AT::deallocate(my_mem_alloc, static_cast(p), + (n + sizeof(M) - 1) / sizeof(M)); +} + +namespace memory_internal { + +// Constructs T into uninitialized storage pointed by `ptr` using the args +// specified in the tuple. +template +void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t, + absl::index_sequence) { + absl::allocator_traits::construct( + *alloc, ptr, std::get(std::forward(t))...); +} + +template +struct WithConstructedImplF { + template + decltype(std::declval()(std::declval())) operator()( + Args&&... args) const { + return std::forward(f)(T(std::forward(args)...)); + } + F&& f; +}; + +template +decltype(std::declval()(std::declval())) WithConstructedImpl( + Tuple&& t, absl::index_sequence, F&& f) { + return WithConstructedImplF{std::forward(f)}( + std::get(std::forward(t))...); +} + +template +auto TupleRefImpl(T&& t, absl::index_sequence) + -> decltype(std::forward_as_tuple(std::get(std::forward(t))...)) { + return std::forward_as_tuple(std::get(std::forward(t))...); +} + +// Returns a tuple of references to the elements of the input tuple. T must be a +// tuple. +template +auto TupleRef(T&& t) -> decltype(TupleRefImpl( + std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>())) { + return TupleRefImpl( + std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>()); +} + +template +decltype(std::declval()(std::declval(), std::piecewise_construct, + std::declval>(), std::declval())) +DecomposePairImpl(F&& f, std::pair, V> p) { + const auto& key = std::get<0>(p.first); + return std::forward(f)(key, std::piecewise_construct, std::move(p.first), + std::move(p.second)); +} + +} // namespace memory_internal + +// Constructs T into uninitialized storage pointed by `ptr` using the args +// specified in the tuple. +template +void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) { + memory_internal::ConstructFromTupleImpl( + alloc, ptr, std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>()); +} + +// Constructs T using the args specified in the tuple and calls F with the +// constructed value. +template +decltype(std::declval()(std::declval())) WithConstructed(Tuple&& t, + F&& f) { + return memory_internal::WithConstructedImpl( + std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>(), + std::forward(f)); +} + +// Given arguments of an std::pair's constructor, PairArgs() returns a pair of +// tuples with references to the passed arguments. The tuples contain +// constructor arguments for the first and the second elements of the pair. +// +// The following two snippets are equivalent. +// +// 1. std::pair p(args...); +// +// 2. auto a = PairArgs(args...); +// std::pair p(std::piecewise_construct, +// std::move(a.first), std::move(a.second)); +inline std::pair, std::tuple<>> PairArgs() { return {}; } +template +std::pair, std::tuple> PairArgs(F&& f, S&& s) { + return {std::piecewise_construct, std::forward_as_tuple(std::forward(f)), + std::forward_as_tuple(std::forward(s))}; +} +template +std::pair, std::tuple> PairArgs( + const std::pair& p) { + return PairArgs(p.first, p.second); +} +template +std::pair, std::tuple> PairArgs(std::pair&& p) { + return PairArgs(std::forward(p.first), std::forward(p.second)); +} +template +auto PairArgs(std::piecewise_construct_t, F&& f, S&& s) + -> decltype(std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s)))) { + return std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s))); +} + +// A helper function for implementing apply() in map policies. +template +auto DecomposePair(F&& f, Args&&... args) + -> decltype(memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...))) { + return memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...)); +} + +// A helper function for implementing apply() in set policies. +template +decltype(std::declval()(std::declval(), std::declval())) +DecomposeValue(F&& f, Arg&& arg) { + const auto& key = arg; + return std::forward(f)(key, std::forward(arg)); +} + +// Helper functions for asan and msan. +inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) { +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + ASAN_POISON_MEMORY_REGION(m, s); +#endif +#ifdef ABSL_HAVE_MEMORY_SANITIZER + __msan_poison(m, s); +#endif + (void)m; + (void)s; +} + +inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) { +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + ASAN_UNPOISON_MEMORY_REGION(m, s); +#endif +#ifdef ABSL_HAVE_MEMORY_SANITIZER + __msan_unpoison(m, s); +#endif + (void)m; + (void)s; +} + +template +inline void SanitizerPoisonObject(const T* object) { + SanitizerPoisonMemoryRegion(object, sizeof(T)); +} + +template +inline void SanitizerUnpoisonObject(const T* object) { + SanitizerUnpoisonMemoryRegion(object, sizeof(T)); +} + +namespace memory_internal { + +// If Pair is a standard-layout type, OffsetOf::kFirst and +// OffsetOf::kSecond are equivalent to offsetof(Pair, first) and +// offsetof(Pair, second) respectively. Otherwise they are -1. +// +// The purpose of OffsetOf is to avoid calling offsetof() on non-standard-layout +// type, which is non-portable. +template +struct OffsetOf { + static constexpr size_t kFirst = static_cast(-1); + static constexpr size_t kSecond = static_cast(-1); +}; + +template +struct OffsetOf::type> { + static constexpr size_t kFirst = offsetof(Pair, first); + static constexpr size_t kSecond = offsetof(Pair, second); +}; + +template +struct IsLayoutCompatible { + private: + struct Pair { + K first; + V second; + }; + + // Is P layout-compatible with Pair? + template + static constexpr bool LayoutCompatible() { + return std::is_standard_layout

() && sizeof(P) == sizeof(Pair) && + alignof(P) == alignof(Pair) && + memory_internal::OffsetOf

::kFirst == + memory_internal::OffsetOf::kFirst && + memory_internal::OffsetOf

::kSecond == + memory_internal::OffsetOf::kSecond; + } + + public: + // Whether pair and pair are layout-compatible. If they are, + // then it is safe to store them in a union and read from either. + static constexpr bool value = std::is_standard_layout() && + std::is_standard_layout() && + memory_internal::OffsetOf::kFirst == 0 && + LayoutCompatible>() && + LayoutCompatible>(); +}; + +} // namespace memory_internal + +// The internal storage type for key-value containers like flat_hash_map. +// +// It is convenient for the value_type of a flat_hash_map to be +// pair; the "const K" prevents accidental modification of the key +// when dealing with the reference returned from find() and similar methods. +// However, this creates other problems; we want to be able to emplace(K, V) +// efficiently with move operations, and similarly be able to move a +// pair in insert(). +// +// The solution is this union, which aliases the const and non-const versions +// of the pair. This also allows flat_hash_map to work, even though +// that has the same efficiency issues with move in emplace() and insert() - +// but people do it anyway. +// +// If kMutableKeys is false, only the value member can be accessed. +// +// If kMutableKeys is true, key can be accessed through all slots while value +// and mutable_value must be accessed only via INITIALIZED slots. Slots are +// created and destroyed via mutable_value so that the key can be moved later. +// +// Accessing one of the union fields while the other is active is safe as +// long as they are layout-compatible, which is guaranteed by the definition of +// kMutableKeys. For C++11, the relevant section of the standard is +// https://timsong-cpp.github.io/cppwp/n3337/class.mem#19 (9.2.19) +template +union map_slot_type { + map_slot_type() {} + ~map_slot_type() = delete; + using value_type = std::pair; + using mutable_value_type = + std::pair, absl::remove_const_t>; + + value_type value; + mutable_value_type mutable_value; + absl::remove_const_t key; +}; + +template +struct map_slot_policy { + using slot_type = map_slot_type; + using value_type = std::pair; + using mutable_value_type = + std::pair, absl::remove_const_t>; + + private: + static void emplace(slot_type* slot) { + // The construction of union doesn't do anything at runtime but it allows us + // to access its members without violating aliasing rules. + new (slot) slot_type; + } + // If pair and pair are layout-compatible, we can accept one + // or the other via slot_type. We are also free to access the key via + // slot_type::key in this case. + using kMutableKeys = memory_internal::IsLayoutCompatible; + + public: + static value_type& element(slot_type* slot) { return slot->value; } + static const value_type& element(const slot_type* slot) { + return slot->value; + } + + static K& mutable_key(slot_type* slot) { + // Still check for kMutableKeys so that we can avoid calling std::launder + // unless necessary because it can interfere with optimizations. + return kMutableKeys::value ? slot->key + : *std::launder(const_cast( + std::addressof(slot->value.first))); + } + + static const K& key(const slot_type* slot) { + return kMutableKeys::value ? slot->key : slot->value.first; + } + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + emplace(slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct(*alloc, &slot->mutable_value, + std::forward(args)...); + } else { + absl::allocator_traits::construct(*alloc, &slot->value, + std::forward(args)...); + } + } + + // Construct this slot by moving from another slot. + template + static void construct(Allocator* alloc, slot_type* slot, slot_type* other) { + emplace(slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct( + *alloc, &slot->mutable_value, std::move(other->mutable_value)); + } else { + absl::allocator_traits::construct(*alloc, &slot->value, + std::move(other->value)); + } + } + + // Construct this slot by copying from another slot. + template + static void construct(Allocator* alloc, slot_type* slot, + const slot_type* other) { + emplace(slot); + absl::allocator_traits::construct(*alloc, &slot->value, + other->value); + } + + template + static auto destroy(Allocator* alloc, slot_type* slot) { + if (kMutableKeys::value) { + absl::allocator_traits::destroy(*alloc, &slot->mutable_value); + } else { + absl::allocator_traits::destroy(*alloc, &slot->value); + } + return IsDestructionTrivial(); + } + + template + static auto transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + // This should really just be + // typename absl::is_trivially_relocatable::type() + // but std::pair is not trivially copyable in C++23 in some standard + // library versions. + // See https://github.com/llvm/llvm-project/pull/95444 for instance. + auto is_relocatable = typename std::conjunction< + absl::is_trivially_relocatable, + absl::is_trivially_relocatable>:: + type(); + + emplace(new_slot); + if (is_relocatable) { + // TODO(b/247130232,b/251814870): remove casts after fixing warnings. + std::memcpy(static_cast(std::launder(&new_slot->value)), + static_cast(&old_slot->value), + sizeof(value_type)); + return is_relocatable; + } + + if (kMutableKeys::value) { + absl::allocator_traits::construct( + *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value)); + } else { + absl::allocator_traits::construct(*alloc, &new_slot->value, + std::move(old_slot->value)); + } + destroy(alloc, old_slot); + return is_relocatable; + } +}; + +// Type erased function for computing hash of the slot. +using HashSlotFn = size_t (*)(const void* hash_fn, void* slot); + +// Type erased function to apply `Fn` to data inside of the `slot`. +// The data is expected to have type `T`. +template +size_t TypeErasedApplyToSlotFn(const void* fn, void* slot) { + const auto* f = static_cast(fn); + return (*f)(*static_cast(slot)); +} + +// Type erased function to apply `Fn` to data inside of the `*slot_ptr`. +// The data is expected to have type `T`. +template +size_t TypeErasedDerefAndApplyToSlotFn(const void* fn, void* slot_ptr) { + const auto* f = static_cast(fn); + const T* slot = *static_cast(slot_ptr); + return (*f)(*slot); +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h new file mode 100644 index 00000000..0f07bcfe --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h @@ -0,0 +1,276 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Define the default Hash and Eq functions for SwissTable containers. +// +// std::hash and std::equal_to are not appropriate hash and equal +// functions for SwissTable containers. There are two reasons for this. +// +// SwissTable containers are power of 2 sized containers: +// +// This means they use the lower bits of the hash value to find the slot for +// each entry. The typical hash function for integral types is the identity. +// This is a very weak hash function for SwissTable and any power of 2 sized +// hashtable implementation which will lead to excessive collisions. For +// SwissTable we use murmur3 style mixing to reduce collisions to a minimum. +// +// SwissTable containers support heterogeneous lookup: +// +// In order to make heterogeneous lookup work, hash and equal functions must be +// polymorphic. At the same time they have to satisfy the same requirements the +// C++ standard imposes on hash functions and equality operators. That is: +// +// if hash_default_eq(a, b) returns true for any a and b of type T, then +// hash_default_hash(a) must equal hash_default_hash(b) +// +// For SwissTable containers this requirement is relaxed to allow a and b of +// any and possibly different types. Note that like the standard the hash and +// equal functions are still bound to T. This is important because some type U +// can be hashed by/tested for equality differently depending on T. A notable +// example is `const char*`. `const char*` is treated as a c-style string when +// the hash function is hash but as a pointer when the hash +// function is hash. +// +#ifndef ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/container/internal/common.h" +#include "absl/hash/hash.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/cord.h" +#include "absl/strings/string_view.h" + +#ifdef ABSL_HAVE_STD_STRING_VIEW +#include +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// The hash of an object of type T is computed by using absl::Hash. +template +struct HashEq { + using Hash = absl::Hash; + using Eq = std::equal_to; +}; + +struct StringHash { + using is_transparent = void; + + size_t operator()(absl::string_view v) const { + return absl::Hash{}(v); + } + size_t operator()(const absl::Cord& v) const { + return absl::Hash{}(v); + } +}; + +struct StringEq { + using is_transparent = void; + bool operator()(absl::string_view lhs, absl::string_view rhs) const { + return lhs == rhs; + } + bool operator()(const absl::Cord& lhs, const absl::Cord& rhs) const { + return lhs == rhs; + } + bool operator()(const absl::Cord& lhs, absl::string_view rhs) const { + return lhs == rhs; + } + bool operator()(absl::string_view lhs, const absl::Cord& rhs) const { + return lhs == rhs; + } +}; + +// Supports heterogeneous lookup for string-like elements. +struct StringHashEq { + using Hash = StringHash; + using Eq = StringEq; +}; + +template <> +struct HashEq : StringHashEq {}; +template <> +struct HashEq : StringHashEq {}; +template <> +struct HashEq : StringHashEq {}; + +#ifdef ABSL_HAVE_STD_STRING_VIEW + +template +struct BasicStringHash { + using is_transparent = void; + + size_t operator()(std::basic_string_view v) const { + return absl::Hash>{}(v); + } +}; + +template +struct BasicStringEq { + using is_transparent = void; + bool operator()(std::basic_string_view lhs, + std::basic_string_view rhs) const { + return lhs == rhs; + } +}; + +// Supports heterogeneous lookup for w/u16/u32 string + string_view + char*. +template +struct BasicStringHashEq { + using Hash = BasicStringHash; + using Eq = BasicStringEq; +}; + +template <> +struct HashEq : BasicStringHashEq {}; +template <> +struct HashEq : BasicStringHashEq {}; +template <> +struct HashEq : BasicStringHashEq {}; +template <> +struct HashEq : BasicStringHashEq {}; +template <> +struct HashEq : BasicStringHashEq {}; +template <> +struct HashEq : BasicStringHashEq {}; + +#endif // ABSL_HAVE_STD_STRING_VIEW + +// Supports heterogeneous lookup for pointers and smart pointers. +template +struct HashEq { + struct Hash { + using is_transparent = void; + template + size_t operator()(const U& ptr) const { + return absl::Hash{}(HashEq::ToPtr(ptr)); + } + }; + struct Eq { + using is_transparent = void; + template + bool operator()(const A& a, const B& b) const { + return HashEq::ToPtr(a) == HashEq::ToPtr(b); + } + }; + + private: + static const T* ToPtr(const T* ptr) { return ptr; } + template + static const T* ToPtr(const std::unique_ptr& ptr) { + return ptr.get(); + } + template + static const T* ToPtr(const std::shared_ptr& ptr) { + return ptr.get(); + } +}; + +template +struct HashEq> : HashEq {}; +template +struct HashEq> : HashEq {}; + +template +struct HasAbslContainerHash : std::false_type {}; + +template +struct HasAbslContainerHash> + : std::true_type {}; + +template +struct HasAbslContainerEq : std::false_type {}; + +template +struct HasAbslContainerEq> + : std::true_type {}; + +template +struct AbslContainerEq { + using type = std::equal_to<>; +}; + +template +struct AbslContainerEq< + T, typename std::enable_if_t::value>> { + using type = typename T::absl_container_eq; +}; + +template +struct AbslContainerHash { + using type = void; +}; + +template +struct AbslContainerHash< + T, typename std::enable_if_t::value>> { + using type = typename T::absl_container_hash; +}; + +// HashEq specialization for user types that provide `absl_container_hash` and +// (optionally) `absl_container_eq`. This specialization allows user types to +// provide heterogeneous lookup without requiring to explicitly specify Hash/Eq +// type arguments in unordered Abseil containers. +// +// Both `absl_container_hash` and `absl_container_eq` should be transparent +// (have inner is_transparent type). While there is no technical reason to +// restrict to transparent-only types, there is also no feasible use case when +// it shouldn't be transparent - it is easier to relax the requirement later if +// such a case arises rather than restricting it. +// +// If type provides only `absl_container_hash` then `eq` part will be +// `std::equal_to`. +// +// User types are not allowed to provide only a `Eq` part as there is no +// feasible use case for this behavior - if Hash should be a default one then Eq +// should be an equivalent to the `std::equal_to`. +template +struct HashEq::value>> { + using Hash = typename AbslContainerHash::type; + using Eq = typename AbslContainerEq::type; + static_assert(IsTransparent::value, + "absl_container_hash must be transparent. To achieve it add a " + "`using is_transparent = void;` clause to this type."); + static_assert(IsTransparent::value, + "absl_container_eq must be transparent. To achieve it add a " + "`using is_transparent = void;` clause to this type."); +}; + +// This header's visibility is restricted. If you need to access the default +// hasher please use the container's ::hasher alias instead. +// +// Example: typename Hash = typename absl::flat_hash_map::hasher +template +using hash_default_hash = typename container_internal::HashEq::Hash; + +// This header's visibility is restricted. If you need to access the default +// key equal please use the container's ::key_equal alias instead. +// +// Example: typename Eq = typename absl::flat_hash_map::key_equal +template +using hash_default_eq = typename container_internal::HashEq::Eq; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hash_policy_traits.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hash_policy_traits.h new file mode 100644 index 00000000..cd6b42f9 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hash_policy_traits.h @@ -0,0 +1,203 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ + +#include +#include +#include +#include +#include + +#include "absl/container/internal/common_policy_traits.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Defines how slots are initialized/destroyed/moved. +template +struct hash_policy_traits : common_policy_traits { + // The type of the keys stored in the hashtable. + using key_type = typename Policy::key_type; + + private: + struct ReturnKey { + template ::value, int> = 0> + static key_type& Impl(Key&& k, int) { + return *std::launder( + const_cast(std::addressof(std::forward(k)))); + } + + template + static Key Impl(Key&& k, char) { + return std::forward(k); + } + + // When Key=T&, we forward the lvalue reference. + // When Key=T, we return by value to avoid a dangling reference. + // eg, for string_hash_map. + template + auto operator()(Key&& k, const Args&...) const + -> decltype(Impl(std::forward(k), 0)) { + return Impl(std::forward(k), 0); + } + }; + + template + struct ConstantIteratorsImpl : std::false_type {}; + + template + struct ConstantIteratorsImpl> + : P::constant_iterators {}; + + public: + // The actual object stored in the hash table. + using slot_type = typename Policy::slot_type; + + // The argument type for insertions into the hashtable. This is different + // from value_type for increased performance. See initializer_list constructor + // and insert() member functions for more details. + using init_type = typename Policy::init_type; + + using reference = decltype(Policy::element(std::declval())); + using pointer = typename std::remove_reference::type*; + using value_type = typename std::remove_reference::type; + + // Policies can set this variable to tell raw_hash_set that all iterators + // should be constant, even `iterator`. This is useful for set-like + // containers. + // Defaults to false if not provided by the policy. + using constant_iterators = ConstantIteratorsImpl<>; + + // Returns the amount of memory owned by `slot`, exclusive of `sizeof(*slot)`. + // + // If `slot` is nullptr, returns the constant amount of memory owned by any + // full slot or -1 if slots own variable amounts of memory. + // + // PRECONDITION: `slot` is INITIALIZED or nullptr + template + static size_t space_used(const slot_type* slot) { + return P::space_used(slot); + } + + // Provides generalized access to the key for elements, both for elements in + // the table and for elements that have not yet been inserted (or even + // constructed). We would like an API that allows us to say: `key(args...)` + // but we cannot do that for all cases, so we use this more general API that + // can be used for many things, including the following: + // + // - Given an element in a table, get its key. + // - Given an element initializer, get its key. + // - Given `emplace()` arguments, get the element key. + // + // Implementations of this must adhere to a very strict technical + // specification around aliasing and consuming arguments: + // + // Let `value_type` be the result type of `element()` without ref- and + // cv-qualifiers. The first argument is a functor, the rest are constructor + // arguments for `value_type`. Returns `std::forward(f)(k, xs...)`, where + // `k` is the element key, and `xs...` are the new constructor arguments for + // `value_type`. It's allowed for `k` to alias `xs...`, and for both to alias + // `ts...`. The key won't be touched once `xs...` are used to construct an + // element; `ts...` won't be touched at all, which allows `apply()` to consume + // any rvalues among them. + // + // If `value_type` is constructible from `Ts&&...`, `Policy::apply()` must not + // trigger a hard compile error unless it originates from `f`. In other words, + // `Policy::apply()` must be SFINAE-friendly. If `value_type` is not + // constructible from `Ts&&...`, either SFINAE or a hard compile error is OK. + // + // If `Ts...` is `[cv] value_type[&]` or `[cv] init_type[&]`, + // `Policy::apply()` must work. A compile error is not allowed, SFINAE or not. + template + static auto apply(F&& f, Ts&&... ts) + -> decltype(P::apply(std::forward(f), std::forward(ts)...)) { + return P::apply(std::forward(f), std::forward(ts)...); + } + + // Returns the "key" portion of the slot. + // Used for node handle manipulation. + template + static auto mutable_key(slot_type* slot) + -> decltype(P::apply(ReturnKey(), hash_policy_traits::element(slot))) { + return P::apply(ReturnKey(), hash_policy_traits::element(slot)); + } + + // Returns the "value" (as opposed to the "key") portion of the element. Used + // by maps to implement `operator[]`, `at()` and `insert_or_assign()`. + template + static auto value(T* elem) -> decltype(P::value(elem)) { + return P::value(elem); + } + + using HashSlotFn = size_t (*)(const void* hash_fn, void* slot); + + template + static constexpr HashSlotFn get_hash_slot_fn() { +// get_hash_slot_fn may return nullptr to signal that non type erased function +// should be used. GCC warns against comparing function address with nullptr. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +// silent error: the address of * will never be NULL [-Werror=address] +#pragma GCC diagnostic ignored "-Waddress" +#endif + return Policy::template get_hash_slot_fn() == nullptr + ? &hash_slot_fn_non_type_erased + : Policy::template get_hash_slot_fn(); +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + } + + // Whether small object optimization is enabled. True by default. + static constexpr bool soo_enabled() { return soo_enabled_impl(Rank1{}); } + + private: + template + struct HashElement { + template + size_t operator()(const K& key, Args&&...) const { + return h(key); + } + const Hash& h; + }; + + template + static size_t hash_slot_fn_non_type_erased(const void* hash_fn, void* slot) { + return Policy::apply(HashElement{*static_cast(hash_fn)}, + Policy::element(static_cast(slot))); + } + + // Use go/ranked-overloads for dispatching. Rank1 is preferred. + struct Rank0 {}; + struct Rank1 : Rank0 {}; + + // Use auto -> decltype as an enabler. + template + static constexpr auto soo_enabled_impl(Rank1) -> decltype(P::soo_enabled()) { + return P::soo_enabled(); + } + + static constexpr bool soo_enabled_impl(Rank0) { return true; } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_control_bytes.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_control_bytes.h new file mode 100644 index 00000000..abaadc3b --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_control_bytes.h @@ -0,0 +1,527 @@ +// Copyright 2025 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file contains the implementation of the hashtable control bytes +// manipulation. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_CONTROL_BYTES_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLE_CONTROL_BYTES_H_ + +#include +#include +#include +#include + +#include "absl/base/config.h" + +#ifdef ABSL_INTERNAL_HAVE_SSE2 +#include +#endif + +#ifdef ABSL_INTERNAL_HAVE_SSSE3 +#include +#endif + +#ifdef _MSC_VER +#include +#endif + +#ifdef ABSL_INTERNAL_HAVE_ARM_NEON +#include +#endif + +#include "absl/base/optimization.h" +#include "absl/numeric/bits.h" +#include "absl/base/internal/endian.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +#ifdef ABSL_SWISSTABLE_ASSERT +#error ABSL_SWISSTABLE_ASSERT cannot be directly set +#else +// We use this macro for assertions that users may see when the table is in an +// invalid state that sanitizers may help diagnose. +#define ABSL_SWISSTABLE_ASSERT(CONDITION) \ + assert((CONDITION) && "Try enabling sanitizers.") +#endif + + +template +uint32_t TrailingZeros(T x) { + ABSL_ASSUME(x != 0); + return static_cast(countr_zero(x)); +} + +// 8 bytes bitmask with most significant bit set for every byte. +constexpr uint64_t kMsbs8Bytes = 0x8080808080808080ULL; +// 8 kEmpty bytes that is useful for small table initialization. +constexpr uint64_t k8EmptyBytes = kMsbs8Bytes; + +// An abstract bitmask, such as that emitted by a SIMD instruction. +// +// Specifically, this type implements a simple bitset whose representation is +// controlled by `SignificantBits` and `Shift`. `SignificantBits` is the number +// of abstract bits in the bitset, while `Shift` is the log-base-two of the +// width of an abstract bit in the representation. +// This mask provides operations for any number of real bits set in an abstract +// bit. To add iteration on top of that, implementation must guarantee no more +// than the most significant real bit is set in a set abstract bit. +template +class NonIterableBitMask { + public: + explicit NonIterableBitMask(T mask) : mask_(mask) {} + + explicit operator bool() const { return this->mask_ != 0; } + + // Returns the index of the lowest *abstract* bit set in `self`. + uint32_t LowestBitSet() const { + return container_internal::TrailingZeros(mask_) >> Shift; + } + + // Returns the index of the highest *abstract* bit set in `self`. + uint32_t HighestBitSet() const { + return static_cast((bit_width(mask_) - 1) >> Shift); + } + + // Returns the number of trailing zero *abstract* bits. + uint32_t TrailingZeros() const { + return container_internal::TrailingZeros(mask_) >> Shift; + } + + // Returns the number of leading zero *abstract* bits. + uint32_t LeadingZeros() const { + constexpr int total_significant_bits = SignificantBits << Shift; + constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits; + return static_cast( + countl_zero(static_cast(mask_ << extra_bits))) >> + Shift; + } + + T mask_; +}; + +// Mask that can be iterable +// +// For example, when `SignificantBits` is 16 and `Shift` is zero, this is just +// an ordinary 16-bit bitset occupying the low 16 bits of `mask`. When +// `SignificantBits` is 8 and `Shift` is 3, abstract bits are represented as +// the bytes `0x00` and `0x80`, and it occupies all 64 bits of the bitmask. +// If NullifyBitsOnIteration is true (only allowed for Shift == 3), +// non zero abstract bit is allowed to have additional bits +// (e.g., `0xff`, `0x83` and `0x9c` are ok, but `0x6f` is not). +// +// For example: +// for (int i : BitMask(0b101)) -> yields 0, 2 +// for (int i : BitMask(0x0000000080800000)) -> yields 2, 3 +template +class BitMask : public NonIterableBitMask { + using Base = NonIterableBitMask; + static_assert(std::is_unsigned::value, ""); + static_assert(Shift == 0 || Shift == 3, ""); + static_assert(!NullifyBitsOnIteration || Shift == 3, ""); + + public: + explicit BitMask(T mask) : Base(mask) { + if (Shift == 3 && !NullifyBitsOnIteration) { + ABSL_SWISSTABLE_ASSERT(this->mask_ == (this->mask_ & kMsbs8Bytes)); + } + } + // BitMask is an iterator over the indices of its abstract bits. + using value_type = int; + using iterator = BitMask; + using const_iterator = BitMask; + + BitMask& operator++() { + if (Shift == 3 && NullifyBitsOnIteration) { + this->mask_ &= kMsbs8Bytes; + } + this->mask_ &= (this->mask_ - 1); + return *this; + } + + uint32_t operator*() const { return Base::LowestBitSet(); } + + BitMask begin() const { return *this; } + BitMask end() const { return BitMask(0); } + + private: + friend bool operator==(const BitMask& a, const BitMask& b) { + return a.mask_ == b.mask_; + } + friend bool operator!=(const BitMask& a, const BitMask& b) { + return a.mask_ != b.mask_; + } +}; + +using h2_t = uint8_t; + +// The values here are selected for maximum performance. See the static asserts +// below for details. + +// A `ctrl_t` is a single control byte, which can have one of four +// states: empty, deleted, full (which has an associated seven-bit h2_t value) +// and the sentinel. They have the following bit patterns: +// +// empty: 1 0 0 0 0 0 0 0 +// deleted: 1 1 1 1 1 1 1 0 +// full: 0 h h h h h h h // h represents the hash bits. +// sentinel: 1 1 1 1 1 1 1 1 +// +// These values are specifically tuned for SSE-flavored SIMD. +// The static_asserts below detail the source of these choices. +// +// We use an enum class so that when strict aliasing is enabled, the compiler +// knows ctrl_t doesn't alias other types. +enum class ctrl_t : int8_t { + kEmpty = -128, // 0b10000000 + kDeleted = -2, // 0b11111110 + kSentinel = -1, // 0b11111111 +}; +static_assert( + (static_cast(ctrl_t::kEmpty) & + static_cast(ctrl_t::kDeleted) & + static_cast(ctrl_t::kSentinel) & 0x80) != 0, + "Special markers need to have the MSB to make checking for them efficient"); +static_assert( + ctrl_t::kEmpty < ctrl_t::kSentinel && ctrl_t::kDeleted < ctrl_t::kSentinel, + "ctrl_t::kEmpty and ctrl_t::kDeleted must be smaller than " + "ctrl_t::kSentinel to make the SIMD test of IsEmptyOrDeleted() efficient"); +static_assert( + ctrl_t::kSentinel == static_cast(-1), + "ctrl_t::kSentinel must be -1 to elide loading it from memory into SIMD " + "registers (pcmpeqd xmm, xmm)"); +static_assert(ctrl_t::kEmpty == static_cast(-128), + "ctrl_t::kEmpty must be -128 to make the SIMD check for its " + "existence efficient (psignb xmm, xmm)"); +static_assert( + (~static_cast(ctrl_t::kEmpty) & + ~static_cast(ctrl_t::kDeleted) & + static_cast(ctrl_t::kSentinel) & 0x7F) != 0, + "ctrl_t::kEmpty and ctrl_t::kDeleted must share an unset bit that is not " + "shared by ctrl_t::kSentinel to make the scalar test for " + "MaskEmptyOrDeleted() efficient"); +static_assert(ctrl_t::kDeleted == static_cast(-2), + "ctrl_t::kDeleted must be -2 to make the implementation of " + "ConvertSpecialToEmptyAndFullToDeleted efficient"); + +// Helpers for checking the state of a control byte. +inline bool IsEmpty(ctrl_t c) { return c == ctrl_t::kEmpty; } +inline bool IsFull(ctrl_t c) { + // Cast `c` to the underlying type instead of casting `0` to `ctrl_t` as `0` + // is not a value in the enum. Both ways are equivalent, but this way makes + // linters happier. + return static_cast>(c) >= 0; +} +inline bool IsDeleted(ctrl_t c) { return c == ctrl_t::kDeleted; } +inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; } + +#ifdef ABSL_INTERNAL_HAVE_SSE2 +// Quick reference guide for intrinsics used below: +// +// * __m128i: An XMM (128-bit) word. +// +// * _mm_setzero_si128: Returns a zero vector. +// * _mm_set1_epi8: Returns a vector with the same i8 in each lane. +// +// * _mm_subs_epi8: Saturating-subtracts two i8 vectors. +// * _mm_and_si128: Ands two i128s together. +// * _mm_or_si128: Ors two i128s together. +// * _mm_andnot_si128: And-nots two i128s together. +// +// * _mm_cmpeq_epi8: Component-wise compares two i8 vectors for equality, +// filling each lane with 0x00 or 0xff. +// * _mm_cmpgt_epi8: Same as above, but using > rather than ==. +// +// * _mm_loadu_si128: Performs an unaligned load of an i128. +// * _mm_storeu_si128: Performs an unaligned store of an i128. +// +// * _mm_sign_epi8: Retains, negates, or zeroes each i8 lane of the first +// argument if the corresponding lane of the second +// argument is positive, negative, or zero, respectively. +// * _mm_movemask_epi8: Selects the sign bit out of each i8 lane and produces a +// bitmask consisting of those bits. +// * _mm_shuffle_epi8: Selects i8s from the first argument, using the low +// four bits of each i8 lane in the second argument as +// indices. + +// https://github.com/abseil/abseil-cpp/issues/209 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853 +// _mm_cmpgt_epi8 is broken under GCC with -funsigned-char +// Work around this by using the portable implementation of Group +// when using -funsigned-char under GCC. +inline __m128i _mm_cmpgt_epi8_fixed(__m128i a, __m128i b) { +#if defined(__GNUC__) && !defined(__clang__) + if (std::is_unsigned::value) { + const __m128i mask = _mm_set1_epi8(0x80); + const __m128i diff = _mm_subs_epi8(b, a); + return _mm_cmpeq_epi8(_mm_and_si128(diff, mask), mask); + } +#endif + return _mm_cmpgt_epi8(a, b); +} + +struct GroupSse2Impl { + static constexpr size_t kWidth = 16; // the number of slots per group + using BitMaskType = BitMask; + using NonIterableBitMaskType = NonIterableBitMask; + + explicit GroupSse2Impl(const ctrl_t* pos) { + ctrl = _mm_loadu_si128(reinterpret_cast(pos)); + } + + // Returns a bitmask representing the positions of slots that match hash. + BitMaskType Match(h2_t hash) const { + auto match = _mm_set1_epi8(static_cast(hash)); + return BitMaskType( + static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)))); + } + + // Returns a bitmask representing the positions of empty slots. + NonIterableBitMaskType MaskEmpty() const { +#ifdef ABSL_INTERNAL_HAVE_SSSE3 + // This only works because ctrl_t::kEmpty is -128. + return NonIterableBitMaskType( + static_cast(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)))); +#else + auto match = _mm_set1_epi8(static_cast(ctrl_t::kEmpty)); + return NonIterableBitMaskType( + static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)))); +#endif + } + + // Returns a bitmask representing the positions of full slots. + // Note: for `is_small()` tables group may contain the "same" slot twice: + // original and mirrored. + BitMaskType MaskFull() const { + return BitMaskType(static_cast(_mm_movemask_epi8(ctrl) ^ 0xffff)); + } + + // Returns a bitmask representing the positions of non full slots. + // Note: this includes: kEmpty, kDeleted, kSentinel. + // It is useful in contexts when kSentinel is not present. + auto MaskNonFull() const { + return BitMaskType(static_cast(_mm_movemask_epi8(ctrl))); + } + + // Returns a bitmask representing the positions of empty or deleted slots. + NonIterableBitMaskType MaskEmptyOrDeleted() const { + auto special = _mm_set1_epi8(static_cast(ctrl_t::kSentinel)); + return NonIterableBitMaskType(static_cast( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)))); + } + + // Returns the number of trailing empty or deleted elements in the group. + uint32_t CountLeadingEmptyOrDeleted() const { + auto special = _mm_set1_epi8(static_cast(ctrl_t::kSentinel)); + return TrailingZeros(static_cast( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1)); + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + auto msbs = _mm_set1_epi8(static_cast(-128)); + auto x126 = _mm_set1_epi8(126); +#ifdef ABSL_INTERNAL_HAVE_SSSE3 + auto res = _mm_or_si128(_mm_shuffle_epi8(x126, ctrl), msbs); +#else + auto zero = _mm_setzero_si128(); + auto special_mask = _mm_cmpgt_epi8_fixed(zero, ctrl); + auto res = _mm_or_si128(msbs, _mm_andnot_si128(special_mask, x126)); +#endif + _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), res); + } + + __m128i ctrl; +}; +#endif // ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 + +#if defined(ABSL_INTERNAL_HAVE_ARM_NEON) && defined(ABSL_IS_LITTLE_ENDIAN) +struct GroupAArch64Impl { + static constexpr size_t kWidth = 8; + using BitMaskType = BitMask; + using NonIterableBitMaskType = + NonIterableBitMask; + + explicit GroupAArch64Impl(const ctrl_t* pos) { + ctrl = vld1_u8(reinterpret_cast(pos)); + } + + auto Match(h2_t hash) const { + uint8x8_t dup = vdup_n_u8(hash); + auto mask = vceq_u8(ctrl, dup); + return BitMaskType(vget_lane_u64(vreinterpret_u64_u8(mask), 0)); + } + + auto MaskEmpty() const { + uint64_t mask = + vget_lane_u64(vreinterpret_u64_u8(vceq_s8( + vdup_n_s8(static_cast(ctrl_t::kEmpty)), + vreinterpret_s8_u8(ctrl))), + 0); + return NonIterableBitMaskType(mask); + } + + // Returns a bitmask representing the positions of full slots. + // Note: for `is_small()` tables group may contain the "same" slot twice: + // original and mirrored. + auto MaskFull() const { + uint64_t mask = vget_lane_u64( + vreinterpret_u64_u8(vcge_s8(vreinterpret_s8_u8(ctrl), + vdup_n_s8(static_cast(0)))), + 0); + return BitMaskType(mask); + } + + // Returns a bitmask representing the positions of non full slots. + // Note: this includes: kEmpty, kDeleted, kSentinel. + // It is useful in contexts when kSentinel is not present. + auto MaskNonFull() const { + uint64_t mask = vget_lane_u64( + vreinterpret_u64_u8(vclt_s8(vreinterpret_s8_u8(ctrl), + vdup_n_s8(static_cast(0)))), + 0); + return BitMaskType(mask); + } + + auto MaskEmptyOrDeleted() const { + uint64_t mask = + vget_lane_u64(vreinterpret_u64_u8(vcgt_s8( + vdup_n_s8(static_cast(ctrl_t::kSentinel)), + vreinterpret_s8_u8(ctrl))), + 0); + return NonIterableBitMaskType(mask); + } + + uint32_t CountLeadingEmptyOrDeleted() const { + uint64_t mask = + vget_lane_u64(vreinterpret_u64_u8(vcle_s8( + vdup_n_s8(static_cast(ctrl_t::kSentinel)), + vreinterpret_s8_u8(ctrl))), + 0); + // Similar to MaskEmptyorDeleted() but we invert the logic to invert the + // produced bitfield. We then count number of trailing zeros. + // Clang and GCC optimize countr_zero to rbit+clz without any check for 0, + // so we should be fine. + return static_cast(countr_zero(mask)) >> 3; + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(ctrl), 0); + constexpr uint64_t slsbs = 0x0202020202020202ULL; + constexpr uint64_t midbs = 0x7e7e7e7e7e7e7e7eULL; + auto x = slsbs & (mask >> 6); + auto res = (x + midbs) | kMsbs8Bytes; + little_endian::Store64(dst, res); + } + + uint8x8_t ctrl; +}; +#endif // ABSL_INTERNAL_HAVE_ARM_NEON && ABSL_IS_LITTLE_ENDIAN + +struct GroupPortableImpl { + static constexpr size_t kWidth = 8; + using BitMaskType = BitMask; + using NonIterableBitMaskType = + NonIterableBitMask; + + explicit GroupPortableImpl(const ctrl_t* pos) + : ctrl(little_endian::Load64(pos)) {} + + BitMaskType Match(h2_t hash) const { + // For the technique, see: + // http://graphics.stanford.edu/~seander/bithacks.html##ValueInWord + // (Determine if a word has a byte equal to n). + // + // Caveat: there are false positives but: + // - they only occur if there is a real match + // - they never occur on ctrl_t::kEmpty, ctrl_t::kDeleted, ctrl_t::kSentinel + // - they will be handled gracefully by subsequent checks in code + // + // Example: + // v = 0x1716151413121110 + // hash = 0x12 + // retval = (v - lsbs) & ~v & msbs = 0x0000000080800000 + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = ctrl ^ (lsbs * hash); + return BitMaskType((x - lsbs) & ~x & kMsbs8Bytes); + } + + auto MaskEmpty() const { + return NonIterableBitMaskType((ctrl & ~(ctrl << 6)) & kMsbs8Bytes); + } + + // Returns a bitmask representing the positions of full slots. + // Note: for `is_small()` tables group may contain the "same" slot twice: + // original and mirrored. + auto MaskFull() const { + return BitMaskType((ctrl ^ kMsbs8Bytes) & kMsbs8Bytes); + } + + // Returns a bitmask representing the positions of non full slots. + // Note: this includes: kEmpty, kDeleted, kSentinel. + // It is useful in contexts when kSentinel is not present. + auto MaskNonFull() const { return BitMaskType(ctrl & kMsbs8Bytes); } + + auto MaskEmptyOrDeleted() const { + return NonIterableBitMaskType((ctrl & ~(ctrl << 7)) & kMsbs8Bytes); + } + + uint32_t CountLeadingEmptyOrDeleted() const { + // ctrl | ~(ctrl >> 7) will have the lowest bit set to zero for kEmpty and + // kDeleted. We lower all other bits and count number of trailing zeros. + constexpr uint64_t bits = 0x0101010101010101ULL; + return static_cast(countr_zero((ctrl | ~(ctrl >> 7)) & bits) >> + 3); + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = ctrl & kMsbs8Bytes; + auto res = (~x + (x >> 7)) & ~lsbs; + little_endian::Store64(dst, res); + } + + uint64_t ctrl; +}; + +#ifdef ABSL_INTERNAL_HAVE_SSE2 +using Group = GroupSse2Impl; +using GroupFullEmptyOrDeleted = GroupSse2Impl; +#elif defined(ABSL_INTERNAL_HAVE_ARM_NEON) && defined(ABSL_IS_LITTLE_ENDIAN) +using Group = GroupAArch64Impl; +// For Aarch64, we use the portable implementation for counting and masking +// full, empty or deleted group elements. This is to avoid the latency of moving +// between data GPRs and Neon registers when it does not provide a benefit. +// Using Neon is profitable when we call Match(), but is not when we don't, +// which is the case when we do *EmptyOrDeleted and MaskFull operations. +// It is difficult to make a similar approach beneficial on other architectures +// such as x86 since they have much lower GPR <-> vector register transfer +// latency and 16-wide Groups. +using GroupFullEmptyOrDeleted = GroupPortableImpl; +#else +using Group = GroupPortableImpl; +using GroupFullEmptyOrDeleted = GroupPortableImpl; +#endif + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#undef ABSL_SWISSTABLE_ASSERT + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_CONTROL_BYTES_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_debug.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_debug.h new file mode 100644 index 00000000..c79c1a98 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_debug.h @@ -0,0 +1,102 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This library provides APIs to debug the probing behavior of hash tables. +// +// In general, the probing behavior is a black box for users and only the +// side effects can be measured in the form of performance differences. +// These APIs give a glimpse on the actual behavior of the probing algorithms in +// these hashtables given a specified hash function and a set of elements. +// +// The probe count distribution can be used to assess the quality of the hash +// function for that particular hash table. Note that a hash function that +// performs well in one hash table implementation does not necessarily performs +// well in a different one. +// +// This library supports std::unordered_{set,map}, dense_hash_{set,map} and +// absl::{flat,node,string}_hash_{set,map}. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ + +#include +#include +#include +#include + +#include "absl/container/internal/hashtable_debug_hooks.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Returns the number of probes required to lookup `key`. Returns 0 for a +// search with no collisions. Higher values mean more hash collisions occurred; +// however, the exact meaning of this number varies according to the container +// type. +template +size_t GetHashtableDebugNumProbes( + const C& c, const typename C::key_type& key) { + return absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess::GetNumProbes(c, key); +} + +// Gets a histogram of the number of probes for each elements in the container. +// The sum of all the values in the vector is equal to container.size(). +template +std::vector GetHashtableDebugNumProbesHistogram(const C& container) { + std::vector v; + for (auto it = container.begin(); it != container.end(); ++it) { + size_t num_probes = GetHashtableDebugNumProbes( + container, + absl::container_internal::hashtable_debug_internal::GetKey(*it, 0)); + v.resize((std::max)(v.size(), num_probes + 1)); + v[num_probes]++; + } + return v; +} + +struct HashtableDebugProbeSummary { + size_t total_elements; + size_t total_num_probes; + double mean; +}; + +// Gets a summary of the probe count distribution for the elements in the +// container. +template +HashtableDebugProbeSummary GetHashtableDebugProbeSummary(const C& container) { + auto probes = GetHashtableDebugNumProbesHistogram(container); + HashtableDebugProbeSummary summary = {}; + for (size_t i = 0; i < probes.size(); ++i) { + summary.total_elements += probes[i]; + summary.total_num_probes += probes[i] * i; + } + summary.mean = 1.0 * summary.total_num_probes / summary.total_elements; + return summary; +} + +// Returns the number of bytes requested from the allocator by the container +// and not freed. +template +size_t AllocatedByteSize(const C& c) { + return absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess::AllocatedByteSize(c); +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_debug_hooks.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_debug_hooks.h new file mode 100644 index 00000000..3e9ea595 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtable_debug_hooks.h @@ -0,0 +1,85 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Provides the internal API for hashtable_debug.h. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ + +#include + +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace hashtable_debug_internal { + +// If it is a map, call get<0>(). +using std::get; +template +auto GetKey(const typename T::value_type& pair, int) -> decltype(get<0>(pair)) { + return get<0>(pair); +} + +// If it is not a map, return the value directly. +template +const typename T::key_type& GetKey(const typename T::key_type& key, char) { + return key; +} + +// Containers should specialize this to provide debug information for that +// container. +template +struct HashtableDebugAccess { + // Returns the number of probes required to find `key` in `c`. The "number of + // probes" is a concept that can vary by container. Implementations should + // return 0 when `key` was found in the minimum number of operations and + // should increment the result for each non-trivial operation required to find + // `key`. + // + // The default implementation uses the bucket api from the standard and thus + // works for `std::unordered_*` containers. + static size_t GetNumProbes(const Container& c, + const typename Container::key_type& key) { + if (!c.bucket_count()) return {}; + size_t num_probes = 0; + size_t bucket = c.bucket(key); + for (auto it = c.begin(bucket), e = c.end(bucket);; ++it, ++num_probes) { + if (it == e) return num_probes; + if (c.key_eq()(key, GetKey(*it, 0))) return num_probes; + } + } + + // Returns the number of bytes requested from the allocator by the container + // and not freed. + // + // static size_t AllocatedByteSize(const Container& c); + + // Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type + // `Container` and `c.size()` is equal to `num_elements`. + // + // static size_t LowerBoundAllocatedByteSize(size_t num_elements); +}; + +} // namespace hashtable_debug_internal +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.h new file mode 100644 index 00000000..305dc855 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.h @@ -0,0 +1,294 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: hashtablez_sampler.h +// ----------------------------------------------------------------------------- +// +// This header file defines the API for a low level library to sample hashtables +// and collect runtime statistics about them. +// +// `HashtablezSampler` controls the lifecycle of `HashtablezInfo` objects which +// store information about a single sample. +// +// `Record*` methods store information into samples. +// `Sample()` and `Unsample()` make use of a single global sampler with +// properties controlled by the flags hashtablez_enabled, +// hashtablez_sample_rate, and hashtablez_max_samples. +// +// WARNING +// +// Using this sampling API may cause sampled Swiss tables to use the global +// allocator (operator `new`) in addition to any custom allocator. If you +// are using a table in an unusual circumstance where allocation or calling a +// linux syscall is unacceptable, this could interfere. +// +// This utility is internal-only. Use at your own risk. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ + +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/per_thread_tls.h" +#include "absl/base/optimization.h" +#include "absl/base/thread_annotations.h" +#include "absl/profiling/internal/sample_recorder.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/time.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Stores information about a sampled hashtable. All mutations to this *must* +// be made through `Record*` functions below. All reads from this *must* only +// occur in the callback to `HashtablezSampler::Iterate`. +struct HashtablezInfo : public profiling_internal::Sample { + // Constructs the object but does not fill in any fields. + HashtablezInfo(); + ~HashtablezInfo(); + HashtablezInfo(const HashtablezInfo&) = delete; + HashtablezInfo& operator=(const HashtablezInfo&) = delete; + + // Puts the object into a clean state, fills in the logically `const` members, + // blocking for any readers that are currently sampling the object. + void PrepareForSampling(int64_t stride, size_t inline_element_size_value, + size_t key_size, size_t value_size, + uint16_t soo_capacity_value) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu); + + // These fields are mutated by the various Record* APIs and need to be + // thread-safe. + std::atomic capacity; + std::atomic size; + std::atomic num_erases; + std::atomic num_rehashes; + std::atomic max_probe_length; + std::atomic total_probe_length; + std::atomic hashes_bitwise_or; + std::atomic hashes_bitwise_and; + std::atomic hashes_bitwise_xor; + std::atomic max_reserve; + + // All of the fields below are set by `PrepareForSampling`, they must not be + // mutated in `Record*` functions. They are logically `const` in that sense. + // These are guarded by init_mu, but that is not externalized to clients, + // which can read them only during `SampleRecorder::Iterate` which will hold + // the lock. + static constexpr int kMaxStackDepth = 64; + absl::Time create_time; + int32_t depth; + // The SOO capacity for this table in elements (not bytes). Note that sampled + // tables are never SOO because we need to store the infoz handle on the heap. + // Tables that would be SOO if not sampled should have: soo_capacity > 0 && + // size <= soo_capacity && max_reserve <= soo_capacity. + uint16_t soo_capacity; + void* stack[kMaxStackDepth]; + size_t inline_element_size; // How big is the slot in bytes? + size_t key_size; // sizeof(key_type) + size_t value_size; // sizeof(value_type) +}; + +void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length); + +void RecordReservationSlow(HashtablezInfo* info, size_t target_capacity); + +void RecordClearedReservationSlow(HashtablezInfo* info); + +void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, + size_t capacity); + +void RecordInsertSlow(HashtablezInfo* info, size_t hash, + size_t distance_from_desired); + +void RecordEraseSlow(HashtablezInfo* info); + +struct SamplingState { + int64_t next_sample; + // When we make a sampling decision, we record that distance so we can weight + // each sample. + int64_t sample_stride; +}; + +HashtablezInfo* SampleSlow(SamplingState& next_sample, + size_t inline_element_size, size_t key_size, + size_t value_size, uint16_t soo_capacity); +void UnsampleSlow(HashtablezInfo* info); + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +#error ABSL_INTERNAL_HASHTABLEZ_SAMPLE cannot be directly set +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +class HashtablezInfoHandle { + public: + explicit HashtablezInfoHandle() : info_(nullptr) {} + explicit HashtablezInfoHandle(HashtablezInfo* info) : info_(info) {} + + // We do not have a destructor. Caller is responsible for calling Unregister + // before destroying the handle. + void Unregister() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + UnsampleSlow(info_); + } + + inline bool IsSampled() const { return ABSL_PREDICT_FALSE(info_ != nullptr); } + + inline void RecordStorageChanged(size_t size, size_t capacity) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordStorageChangedSlow(info_, size, capacity); + } + + inline void RecordRehash(size_t total_probe_length) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordRehashSlow(info_, total_probe_length); + } + + inline void RecordReservation(size_t target_capacity) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordReservationSlow(info_, target_capacity); + } + + inline void RecordClearedReservation() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordClearedReservationSlow(info_); + } + + inline void RecordInsert(size_t hash, size_t distance_from_desired) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordInsertSlow(info_, hash, distance_from_desired); + } + + inline void RecordErase() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordEraseSlow(info_); + } + + friend inline void swap(HashtablezInfoHandle& lhs, + HashtablezInfoHandle& rhs) { + std::swap(lhs.info_, rhs.info_); + } + + private: + friend class HashtablezInfoHandlePeer; + HashtablezInfo* info_; +}; +#else +// Ensure that when Hashtablez is turned off at compile time, HashtablezInfo can +// be removed by the linker, in order to reduce the binary size. +class HashtablezInfoHandle { + public: + explicit HashtablezInfoHandle() = default; + explicit HashtablezInfoHandle(std::nullptr_t) {} + + inline void Unregister() {} + inline bool IsSampled() const { return false; } + inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {} + inline void RecordRehash(size_t /*total_probe_length*/) {} + inline void RecordReservation(size_t /*target_capacity*/) {} + inline void RecordClearedReservation() {} + inline void RecordInsert(size_t /*hash*/, size_t /*distance_from_desired*/) {} + inline void RecordErase() {} + + friend inline void swap(HashtablezInfoHandle& /*lhs*/, + HashtablezInfoHandle& /*rhs*/) {} +}; +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +extern ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample; +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +// Returns true if the next table should be sampled. +// This function updates the global state. +// If the function returns true, actual sampling should be done by calling +// ForcedTrySample(). +inline bool ShouldSampleNextTable() { +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + if (ABSL_PREDICT_TRUE(--global_next_sample.next_sample > 0)) { + return false; + } + return true; +#else + return false; +#endif // ABSL_INTERNAL_HASHTABLEZ_SAMPLE +} + +// Returns a sampling handle. +// Must be called only if HashSetShouldBeSampled() returned true. +// Returned handle still can be unsampled if sampling is not possible. +HashtablezInfoHandle ForcedTrySample(size_t inline_element_size, + size_t key_size, size_t value_size, + uint16_t soo_capacity); + +// In case sampling needs to be disabled and re-enabled in tests, this function +// can be used to reset the sampling state for the current thread. +// It is useful to avoid sampling attempts and sampling delays in tests. +void TestOnlyRefreshSamplingStateForCurrentThread(); + +// Returns a sampling handle. +inline HashtablezInfoHandle Sample(size_t inline_element_size, size_t key_size, + size_t value_size, uint16_t soo_capacity) { + if (ABSL_PREDICT_TRUE(!ShouldSampleNextTable())) { + return HashtablezInfoHandle(nullptr); + } + return ForcedTrySample(inline_element_size, key_size, value_size, + soo_capacity); +} + +using HashtablezSampler = + ::absl::profiling_internal::SampleRecorder; + +// Returns a global Sampler. +HashtablezSampler& GlobalHashtablezSampler(); + +using HashtablezConfigListener = void (*)(); +void SetHashtablezConfigListener(HashtablezConfigListener l); + +// Enables or disables sampling for Swiss tables. +bool IsHashtablezEnabled(); +void SetHashtablezEnabled(bool enabled); +void SetHashtablezEnabledInternal(bool enabled); + +// Sets the rate at which Swiss tables will be sampled. +int32_t GetHashtablezSampleParameter(); +void SetHashtablezSampleParameter(int32_t rate); +void SetHashtablezSampleParameterInternal(int32_t rate); + +// Sets a soft max for the number of samples that will be kept. +size_t GetHashtablezMaxSamples(); +void SetHashtablezMaxSamples(size_t max); +void SetHashtablezMaxSamplesInternal(size_t max); + +// Configuration override. +// This allows process-wide sampling without depending on order of +// initialization of static storage duration objects. +// The definition of this constant is weak, which allows us to inject a +// different value for it at link time. +extern "C" bool ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)(); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/inlined_vector.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/inlined_vector.h new file mode 100644 index 00000000..b0d3f077 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/inlined_vector.h @@ -0,0 +1,1102 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_H_ +#define ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/identity.h" +#include "absl/base/macros.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace inlined_vector_internal { + +// GCC does not deal very well with the below code +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif + +template +using AllocatorTraits = std::allocator_traits; +template +using ValueType = typename AllocatorTraits::value_type; +template +using SizeType = typename AllocatorTraits::size_type; +template +using Pointer = typename AllocatorTraits::pointer; +template +using ConstPointer = typename AllocatorTraits::const_pointer; +template +using SizeType = typename AllocatorTraits::size_type; +template +using DifferenceType = typename AllocatorTraits::difference_type; +template +using Reference = ValueType&; +template +using ConstReference = const ValueType&; +template +using Iterator = Pointer; +template +using ConstIterator = ConstPointer; +template +using ReverseIterator = typename std::reverse_iterator>; +template +using ConstReverseIterator = typename std::reverse_iterator>; +template +using MoveIterator = typename std::move_iterator>; + +template +using IsMoveAssignOk = std::is_move_assignable>; +template +using IsSwapOk = absl::type_traits_internal::IsSwappable>; + +template >::value && + std::is_same>>::value> +struct DestroyAdapter; + +template +struct DestroyAdapter { + static void DestroyElements(A& allocator, Pointer destroy_first, + SizeType destroy_size) { + for (SizeType i = destroy_size; i != 0;) { + --i; + AllocatorTraits::destroy(allocator, destroy_first + i); + } + } +}; + +template +struct DestroyAdapter { + static void DestroyElements(A& allocator, Pointer destroy_first, + SizeType destroy_size) { + static_cast(allocator); + static_cast(destroy_first); + static_cast(destroy_size); + } +}; + +template +struct Allocation { + Pointer data = nullptr; + SizeType capacity = 0; +}; + +template ) > ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT)> +struct MallocAdapter { + static Allocation Allocate(A& allocator, SizeType requested_capacity) { + return {AllocatorTraits::allocate(allocator, requested_capacity), + requested_capacity}; + } + + static void Deallocate(A& allocator, Pointer pointer, + SizeType capacity) { + AllocatorTraits::deallocate(allocator, pointer, capacity); + } +}; + +template +void ConstructElements(absl::internal::type_identity_t& allocator, + Pointer construct_first, ValueAdapter& values, + SizeType construct_size) { + for (SizeType i = 0; i < construct_size; ++i) { + ABSL_INTERNAL_TRY { values.ConstructNext(allocator, construct_first + i); } + ABSL_INTERNAL_CATCH_ANY { + DestroyAdapter::DestroyElements(allocator, construct_first, i); + ABSL_INTERNAL_RETHROW; + } + } +} + +template +void AssignElements(Pointer assign_first, ValueAdapter& values, + SizeType assign_size) { + for (SizeType i = 0; i < assign_size; ++i) { + values.AssignNext(assign_first + i); + } +} + +template +struct StorageView { + Pointer data; + SizeType size; + SizeType capacity; +}; + +template +class IteratorValueAdapter { + public: + explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} + + void ConstructNext(A& allocator, Pointer construct_at) { + AllocatorTraits::construct(allocator, construct_at, *it_); + ++it_; + } + + void AssignNext(Pointer assign_at) { + *assign_at = *it_; + ++it_; + } + + private: + Iterator it_; +}; + +template +class CopyValueAdapter { + public: + explicit CopyValueAdapter(ConstPointer p) : ptr_(p) {} + + void ConstructNext(A& allocator, Pointer construct_at) { + AllocatorTraits::construct(allocator, construct_at, *ptr_); + } + + void AssignNext(Pointer assign_at) { *assign_at = *ptr_; } + + private: + ConstPointer ptr_; +}; + +template +class DefaultValueAdapter { + public: + explicit DefaultValueAdapter() {} + + void ConstructNext(A& allocator, Pointer construct_at) { + AllocatorTraits::construct(allocator, construct_at); + } + + void AssignNext(Pointer assign_at) { *assign_at = ValueType(); } +}; + +template +class AllocationTransaction { + public: + explicit AllocationTransaction(A& allocator) + : allocator_data_(allocator, nullptr), capacity_(0) {} + + ~AllocationTransaction() { + if (DidAllocate()) { + MallocAdapter::Deallocate(GetAllocator(), GetData(), GetCapacity()); + } + } + + AllocationTransaction(const AllocationTransaction&) = delete; + void operator=(const AllocationTransaction&) = delete; + + A& GetAllocator() { return allocator_data_.template get<0>(); } + Pointer& GetData() { return allocator_data_.template get<1>(); } + SizeType& GetCapacity() { return capacity_; } + + bool DidAllocate() { return GetData() != nullptr; } + + Pointer Allocate(SizeType requested_capacity) { + Allocation result = + MallocAdapter::Allocate(GetAllocator(), requested_capacity); + GetData() = result.data; + GetCapacity() = result.capacity; + return result.data; + } + + [[nodiscard]] Allocation Release() && { + Allocation result = {GetData(), GetCapacity()}; + Reset(); + return result; + } + + private: + void Reset() { + GetData() = nullptr; + GetCapacity() = 0; + } + + container_internal::CompressedTuple> allocator_data_; + SizeType capacity_; +}; + +template +class ConstructionTransaction { + public: + explicit ConstructionTransaction(A& allocator) + : allocator_data_(allocator, nullptr), size_(0) {} + + ~ConstructionTransaction() { + if (DidConstruct()) { + DestroyAdapter::DestroyElements(GetAllocator(), GetData(), GetSize()); + } + } + + ConstructionTransaction(const ConstructionTransaction&) = delete; + void operator=(const ConstructionTransaction&) = delete; + + A& GetAllocator() { return allocator_data_.template get<0>(); } + Pointer& GetData() { return allocator_data_.template get<1>(); } + SizeType& GetSize() { return size_; } + + bool DidConstruct() { return GetData() != nullptr; } + template + void Construct(Pointer data, ValueAdapter& values, SizeType size) { + ConstructElements(GetAllocator(), data, values, size); + GetData() = data; + GetSize() = size; + } + void Commit() && { + GetData() = nullptr; + GetSize() = 0; + } + + private: + container_internal::CompressedTuple> allocator_data_; + SizeType size_; +}; + +template +class Storage { + public: + struct MemcpyPolicy {}; + struct ElementwiseAssignPolicy {}; + struct ElementwiseSwapPolicy {}; + struct ElementwiseConstructPolicy {}; + + using MoveAssignmentPolicy = absl::conditional_t< + // Fast path: if the value type can be trivially move assigned and + // destroyed, and we know the allocator doesn't do anything fancy, then + // it's safe for us to simply adopt the contents of the storage for + // `other` and remove its own reference to them. It's as if we had + // individually move-assigned each value and then destroyed the original. + absl::conjunction>, + absl::is_trivially_destructible>, + std::is_same>>>::value, + MemcpyPolicy, + // Otherwise we use move assignment if possible. If not, we simulate + // move assignment using move construction. + // + // Note that this is in contrast to e.g. std::vector and std::optional, + // which are themselves not move-assignable when their contained type is + // not. + absl::conditional_t::value, ElementwiseAssignPolicy, + ElementwiseConstructPolicy>>; + + // The policy to be used specifically when swapping inlined elements. + using SwapInlinedElementsPolicy = absl::conditional_t< + // Fast path: if the value type can be trivially relocated, and we + // know the allocator doesn't do anything fancy, then it's safe for us + // to simply swap the bytes in the inline storage. It's as if we had + // relocated the first vector's elements into temporary storage, + // relocated the second's elements into the (now-empty) first's, + // and then relocated from temporary storage into the second. + absl::conjunction>, + std::is_same>>>::value, + MemcpyPolicy, + absl::conditional_t::value, ElementwiseSwapPolicy, + ElementwiseConstructPolicy>>; + + static SizeType NextCapacity(SizeType current_capacity) { + return current_capacity * 2; + } + + static SizeType ComputeCapacity(SizeType current_capacity, + SizeType requested_capacity) { + return (std::max)(NextCapacity(current_capacity), requested_capacity); + } + + // --------------------------------------------------------------------------- + // Storage Constructors and Destructor + // --------------------------------------------------------------------------- + + Storage() : metadata_(A(), /* size and is_allocated */ 0u) {} + + explicit Storage(const A& allocator) + : metadata_(allocator, /* size and is_allocated */ 0u) {} + + ~Storage() { + // Fast path: if we are empty and not allocated, there's nothing to do. + if (GetSizeAndIsAllocated() == 0) { + return; + } + + // Fast path: if no destructors need to be run and we know the allocator + // doesn't do anything fancy, then all we need to do is deallocate (and + // maybe not even that). + if (absl::is_trivially_destructible>::value && + std::is_same>>::value) { + DeallocateIfAllocated(); + return; + } + + DestroyContents(); + } + + // --------------------------------------------------------------------------- + // Storage Member Accessors + // --------------------------------------------------------------------------- + + SizeType& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } + + const SizeType& GetSizeAndIsAllocated() const { + return metadata_.template get<1>(); + } + + SizeType GetSize() const { return GetSizeAndIsAllocated() >> 1; } + + bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; } + + Pointer GetAllocatedData() { + // GCC 12 has a false-positive -Wmaybe-uninitialized warning here. +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + return data_.allocated.allocated_data; +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic pop +#endif + } + + ConstPointer GetAllocatedData() const { + return data_.allocated.allocated_data; + } + + // ABSL_ATTRIBUTE_NO_SANITIZE_CFI is used because the memory pointed to may be + // uninitialized, a common pattern in allocate()+construct() APIs. + // https://clang.llvm.org/docs/ControlFlowIntegrity.html#bad-cast-checking + // NOTE: When this was written, LLVM documentation did not explicitly + // mention that casting `char*` and using `reinterpret_cast` qualifies + // as a bad cast. + ABSL_ATTRIBUTE_NO_SANITIZE_CFI Pointer GetInlinedData() { + return reinterpret_cast>(data_.inlined.inlined_data); + } + + ABSL_ATTRIBUTE_NO_SANITIZE_CFI ConstPointer GetInlinedData() const { + return reinterpret_cast>(data_.inlined.inlined_data); + } + + SizeType GetAllocatedCapacity() const { + return data_.allocated.allocated_capacity; + } + + SizeType GetInlinedCapacity() const { + return static_cast>(kOptimalInlinedSize); + } + + StorageView MakeStorageView() { + return GetIsAllocated() ? StorageView{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()} + : StorageView{GetInlinedData(), GetSize(), + GetInlinedCapacity()}; + } + + A& GetAllocator() { return metadata_.template get<0>(); } + + const A& GetAllocator() const { return metadata_.template get<0>(); } + + // --------------------------------------------------------------------------- + // Storage Member Mutators + // --------------------------------------------------------------------------- + + ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other); + + template + void Initialize(ValueAdapter values, SizeType new_size); + + template + void Assign(ValueAdapter values, SizeType new_size); + + template + void Resize(ValueAdapter values, SizeType new_size); + + template + Iterator Insert(ConstIterator pos, ValueAdapter values, + SizeType insert_count); + + template + Reference EmplaceBack(Args&&... args); + + Iterator Erase(ConstIterator from, ConstIterator to); + + void Reserve(SizeType requested_capacity); + + void ShrinkToFit(); + + void Swap(Storage* other_storage_ptr); + + void SetIsAllocated() { + GetSizeAndIsAllocated() |= static_cast>(1); + } + + void UnsetIsAllocated() { + GetSizeAndIsAllocated() &= ((std::numeric_limits>::max)() - 1); + } + + void SetSize(SizeType size) { + GetSizeAndIsAllocated() = + (size << 1) | static_cast>(GetIsAllocated()); + } + + void SetAllocatedSize(SizeType size) { + GetSizeAndIsAllocated() = (size << 1) | static_cast>(1); + } + + void SetInlinedSize(SizeType size) { + GetSizeAndIsAllocated() = size << static_cast>(1); + } + + void AddSize(SizeType count) { + GetSizeAndIsAllocated() += count << static_cast>(1); + } + + void SubtractSize(SizeType count) { + ABSL_HARDENING_ASSERT(count <= GetSize()); + + GetSizeAndIsAllocated() -= count << static_cast>(1); + } + + void SetAllocation(Allocation allocation) { + data_.allocated.allocated_data = allocation.data; + data_.allocated.allocated_capacity = allocation.capacity; + } + + void MemcpyFrom(const Storage& other_storage) { + // Assumption check: it doesn't make sense to memcpy inlined elements unless + // we know the allocator doesn't do anything fancy, and one of the following + // holds: + // + // * The elements are trivially relocatable. + // + // * It's possible to trivially assign the elements and then destroy the + // source. + // + // * It's possible to trivially copy construct/assign the elements. + // + { + using V = ValueType; + ABSL_HARDENING_ASSERT( + other_storage.GetIsAllocated() || + (std::is_same>::value && + ( + // First case above + absl::is_trivially_relocatable::value || + // Second case above + (absl::is_trivially_move_assignable::value && + absl::is_trivially_destructible::value) || + // Third case above + (absl::is_trivially_copy_constructible::value || + absl::is_trivially_copy_assignable::value)))); + } + + GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); + data_ = other_storage.data_; + } + + void DeallocateIfAllocated() { + if (GetIsAllocated()) { + MallocAdapter::Deallocate(GetAllocator(), GetAllocatedData(), + GetAllocatedCapacity()); + } + } + + private: + ABSL_ATTRIBUTE_NOINLINE void DestroyContents(); + + using Metadata = container_internal::CompressedTuple>; + + struct Allocated { + Pointer allocated_data; + SizeType allocated_capacity; + }; + + // `kOptimalInlinedSize` is an automatically adjusted inlined capacity of the + // `InlinedVector`. Sometimes, it is possible to increase the capacity (from + // the user requested `N`) without increasing the size of the `InlinedVector`. + static constexpr size_t kOptimalInlinedSize = + (std::max)(N, sizeof(Allocated) / sizeof(ValueType)); + + struct Inlined { + alignas(ValueType) unsigned char inlined_data[sizeof( + ValueType[kOptimalInlinedSize])]; + }; + + union Data { + Allocated allocated; + Inlined inlined; + }; + + void SwapN(ElementwiseSwapPolicy, Storage* other, SizeType n); + void SwapN(ElementwiseConstructPolicy, Storage* other, SizeType n); + + void SwapInlinedElements(MemcpyPolicy, Storage* other); + template + void SwapInlinedElements(NotMemcpyPolicy, Storage* other); + + template + ABSL_ATTRIBUTE_NOINLINE Reference EmplaceBackSlow(Args&&... args); + + Metadata metadata_; + Data data_; +}; + +template +void Storage::DestroyContents() { + Pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); + DestroyAdapter::DestroyElements(GetAllocator(), data, GetSize()); + DeallocateIfAllocated(); +} + +template +void Storage::InitFrom(const Storage& other) { + const SizeType n = other.GetSize(); + ABSL_HARDENING_ASSERT(n > 0); // Empty sources handled handled in caller. + ConstPointer src; + Pointer dst; + if (!other.GetIsAllocated()) { + dst = GetInlinedData(); + src = other.GetInlinedData(); + } else { + // Because this is only called from the `InlinedVector` constructors, it's + // safe to take on the allocation with size `0`. If `ConstructElements(...)` + // throws, deallocation will be automatically handled by `~Storage()`. + SizeType requested_capacity = ComputeCapacity(GetInlinedCapacity(), n); + Allocation allocation = + MallocAdapter::Allocate(GetAllocator(), requested_capacity); + SetAllocation(allocation); + dst = allocation.data; + src = other.GetAllocatedData(); + } + + // Fast path: if the value type is trivially copy constructible and we know + // the allocator doesn't do anything fancy, then we know it is legal for us to + // simply memcpy the other vector's elements. + if (absl::is_trivially_copy_constructible>::value && + std::is_same>>::value) { + std::memcpy(reinterpret_cast(dst), + reinterpret_cast(src), n * sizeof(ValueType)); + } else { + auto values = IteratorValueAdapter>(src); + ConstructElements(GetAllocator(), dst, values, n); + } + + GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated(); +} + +template +template +auto Storage::Initialize(ValueAdapter values, + SizeType new_size) -> void { + // Only callable from constructors! + ABSL_HARDENING_ASSERT(!GetIsAllocated()); + ABSL_HARDENING_ASSERT(GetSize() == 0); + + Pointer construct_data; + if (new_size > GetInlinedCapacity()) { + // Because this is only called from the `InlinedVector` constructors, it's + // safe to take on the allocation with size `0`. If `ConstructElements(...)` + // throws, deallocation will be automatically handled by `~Storage()`. + SizeType requested_capacity = + ComputeCapacity(GetInlinedCapacity(), new_size); + Allocation allocation = + MallocAdapter::Allocate(GetAllocator(), requested_capacity); + construct_data = allocation.data; + SetAllocation(allocation); + SetIsAllocated(); + } else { + construct_data = GetInlinedData(); + } + + ConstructElements(GetAllocator(), construct_data, values, new_size); + + // Since the initial size was guaranteed to be `0` and the allocated bit is + // already correct for either case, *adding* `new_size` gives us the correct + // result faster than setting it directly. + AddSize(new_size); +} + +template +template +auto Storage::Assign(ValueAdapter values, + SizeType new_size) -> void { + StorageView storage_view = MakeStorageView(); + + AllocationTransaction allocation_tx(GetAllocator()); + + absl::Span> assign_loop; + absl::Span> construct_loop; + absl::Span> destroy_loop; + + if (new_size > storage_view.capacity) { + SizeType requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + construct_loop = {allocation_tx.Allocate(requested_capacity), new_size}; + destroy_loop = {storage_view.data, storage_view.size}; + } else if (new_size > storage_view.size) { + assign_loop = {storage_view.data, storage_view.size}; + construct_loop = {storage_view.data + storage_view.size, + new_size - storage_view.size}; + } else { + assign_loop = {storage_view.data, new_size}; + destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; + } + + AssignElements(assign_loop.data(), values, assign_loop.size()); + + ConstructElements(GetAllocator(), construct_loop.data(), values, + construct_loop.size()); + + DestroyAdapter::DestroyElements(GetAllocator(), destroy_loop.data(), + destroy_loop.size()); + + if (allocation_tx.DidAllocate()) { + DeallocateIfAllocated(); + SetAllocation(std::move(allocation_tx).Release()); + SetIsAllocated(); + } + + SetSize(new_size); +} + +template +template +auto Storage::Resize(ValueAdapter values, + SizeType new_size) -> void { + StorageView storage_view = MakeStorageView(); + Pointer const base = storage_view.data; + const SizeType size = storage_view.size; + A& alloc = GetAllocator(); + if (new_size <= size) { + // Destroy extra old elements. + DestroyAdapter::DestroyElements(alloc, base + new_size, size - new_size); + } else if (new_size <= storage_view.capacity) { + // Construct new elements in place. + ConstructElements(alloc, base + size, values, new_size - size); + } else { + // Steps: + // a. Allocate new backing store. + // b. Construct new elements in new backing store. + // c. Move existing elements from old backing store to new backing store. + // d. Destroy all elements in old backing store. + // Use transactional wrappers for the first two steps so we can roll + // back if necessary due to exceptions. + AllocationTransaction allocation_tx(alloc); + SizeType requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + Pointer new_data = allocation_tx.Allocate(requested_capacity); + + ConstructionTransaction construction_tx(alloc); + construction_tx.Construct(new_data + size, values, new_size - size); + + IteratorValueAdapter> move_values( + (MoveIterator(base))); + ConstructElements(alloc, new_data, move_values, size); + + DestroyAdapter::DestroyElements(alloc, base, size); + std::move(construction_tx).Commit(); + DeallocateIfAllocated(); + SetAllocation(std::move(allocation_tx).Release()); + SetIsAllocated(); + } + SetSize(new_size); +} + +template +template +auto Storage::Insert(ConstIterator pos, ValueAdapter values, + SizeType insert_count) -> Iterator { + StorageView storage_view = MakeStorageView(); + + auto insert_index = static_cast>( + std::distance(ConstIterator(storage_view.data), pos)); + SizeType insert_end_index = insert_index + insert_count; + SizeType new_size = storage_view.size + insert_count; + + if (new_size > storage_view.capacity) { + AllocationTransaction allocation_tx(GetAllocator()); + ConstructionTransaction construction_tx(GetAllocator()); + ConstructionTransaction move_construction_tx(GetAllocator()); + + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); + + SizeType requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + Pointer new_data = allocation_tx.Allocate(requested_capacity); + + construction_tx.Construct(new_data + insert_index, values, insert_count); + + move_construction_tx.Construct(new_data, move_values, insert_index); + + ConstructElements(GetAllocator(), new_data + insert_end_index, + move_values, storage_view.size - insert_index); + + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); + + std::move(construction_tx).Commit(); + std::move(move_construction_tx).Commit(); + DeallocateIfAllocated(); + SetAllocation(std::move(allocation_tx).Release()); + + SetAllocatedSize(new_size); + return Iterator(new_data + insert_index); + } else { + SizeType move_construction_destination_index = + (std::max)(insert_end_index, storage_view.size); + + ConstructionTransaction move_construction_tx(GetAllocator()); + + IteratorValueAdapter> move_construction_values( + MoveIterator(storage_view.data + + (move_construction_destination_index - insert_count))); + absl::Span> move_construction = { + storage_view.data + move_construction_destination_index, + new_size - move_construction_destination_index}; + + Pointer move_assignment_values = storage_view.data + insert_index; + absl::Span> move_assignment = { + storage_view.data + insert_end_index, + move_construction_destination_index - insert_end_index}; + + absl::Span> insert_assignment = {move_assignment_values, + move_construction.size()}; + + absl::Span> insert_construction = { + insert_assignment.data() + insert_assignment.size(), + insert_count - insert_assignment.size()}; + + move_construction_tx.Construct(move_construction.data(), + move_construction_values, + move_construction.size()); + + for (Pointer + destination = move_assignment.data() + move_assignment.size(), + last_destination = move_assignment.data(), + source = move_assignment_values + move_assignment.size(); + ;) { + --destination; + --source; + if (destination < last_destination) break; + *destination = std::move(*source); + } + + AssignElements(insert_assignment.data(), values, + insert_assignment.size()); + + ConstructElements(GetAllocator(), insert_construction.data(), values, + insert_construction.size()); + + std::move(move_construction_tx).Commit(); + + AddSize(insert_count); + return Iterator(storage_view.data + insert_index); + } +} + +template +template +auto Storage::EmplaceBack(Args&&... args) -> Reference { + StorageView storage_view = MakeStorageView(); + const SizeType n = storage_view.size; + if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) { + // Fast path; new element fits. + Pointer last_ptr = storage_view.data + n; + AllocatorTraits::construct(GetAllocator(), last_ptr, + std::forward(args)...); + AddSize(1); + return *last_ptr; + } + // TODO(b/173712035): Annotate with musttail attribute to prevent regression. + return EmplaceBackSlow(std::forward(args)...); +} + +template +template +auto Storage::EmplaceBackSlow(Args&&... args) -> Reference { + StorageView storage_view = MakeStorageView(); + AllocationTransaction allocation_tx(GetAllocator()); + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); + SizeType requested_capacity = NextCapacity(storage_view.capacity); + Pointer construct_data = allocation_tx.Allocate(requested_capacity); + Pointer last_ptr = construct_data + storage_view.size; + + // Construct new element. + AllocatorTraits::construct(GetAllocator(), last_ptr, + std::forward(args)...); + // Move elements from old backing store to new backing store. + ABSL_INTERNAL_TRY { + ConstructElements(GetAllocator(), allocation_tx.GetData(), move_values, + storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + AllocatorTraits::destroy(GetAllocator(), last_ptr); + ABSL_INTERNAL_RETHROW; + } + // Destroy elements in old backing store. + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + SetAllocation(std::move(allocation_tx).Release()); + SetIsAllocated(); + AddSize(1); + return *last_ptr; +} + +template +auto Storage::Erase(ConstIterator from, + ConstIterator to) -> Iterator { + StorageView storage_view = MakeStorageView(); + + auto erase_size = static_cast>(std::distance(from, to)); + auto erase_index = static_cast>( + std::distance(ConstIterator(storage_view.data), from)); + SizeType erase_end_index = erase_index + erase_size; + + // Fast path: if the value type is trivially relocatable and we know + // the allocator doesn't do anything fancy, then we know it is legal for us to + // simply destroy the elements in the "erasure window" (which cannot throw) + // and then memcpy downward to close the window. + if (absl::is_trivially_relocatable>::value && + std::is_nothrow_destructible>::value && + std::is_same>>::value) { + DestroyAdapter::DestroyElements( + GetAllocator(), storage_view.data + erase_index, erase_size); + std::memmove( + reinterpret_cast(storage_view.data + erase_index), + reinterpret_cast(storage_view.data + erase_end_index), + (storage_view.size - erase_end_index) * sizeof(ValueType)); + } else { + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data + erase_end_index)); + + AssignElements(storage_view.data + erase_index, move_values, + storage_view.size - erase_end_index); + + DestroyAdapter::DestroyElements( + GetAllocator(), storage_view.data + (storage_view.size - erase_size), + erase_size); + } + SubtractSize(erase_size); + return Iterator(storage_view.data + erase_index); +} + +template +auto Storage::Reserve(SizeType requested_capacity) -> void { + StorageView storage_view = MakeStorageView(); + + if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocator()); + + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); + + SizeType new_requested_capacity = + ComputeCapacity(storage_view.capacity, requested_capacity); + Pointer new_data = allocation_tx.Allocate(new_requested_capacity); + + ConstructElements(GetAllocator(), new_data, move_values, + storage_view.size); + + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + SetAllocation(std::move(allocation_tx).Release()); + SetIsAllocated(); +} + +template +auto Storage::ShrinkToFit() -> void { + // May only be called on allocated instances! + ABSL_HARDENING_ASSERT(GetIsAllocated()); + + StorageView storage_view{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()}; + + if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocator()); + + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); + + Pointer construct_data; + if (storage_view.size > GetInlinedCapacity()) { + SizeType requested_capacity = storage_view.size; + construct_data = allocation_tx.Allocate(requested_capacity); + if (allocation_tx.GetCapacity() >= storage_view.capacity) { + // Already using the smallest available heap allocation. + return; + } + } else { + construct_data = GetInlinedData(); + } + + ABSL_INTERNAL_TRY { + ConstructElements(GetAllocator(), construct_data, move_values, + storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + SetAllocation({storage_view.data, storage_view.capacity}); + ABSL_INTERNAL_RETHROW; + } + + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); + + MallocAdapter::Deallocate(GetAllocator(), storage_view.data, + storage_view.capacity); + + if (allocation_tx.DidAllocate()) { + SetAllocation(std::move(allocation_tx).Release()); + } else { + UnsetIsAllocated(); + } +} + +template +auto Storage::Swap(Storage* other_storage_ptr) -> void { + using std::swap; + ABSL_HARDENING_ASSERT(this != other_storage_ptr); + + if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) { + swap(data_.allocated, other_storage_ptr->data_.allocated); + } else if (!GetIsAllocated() && !other_storage_ptr->GetIsAllocated()) { + SwapInlinedElements(SwapInlinedElementsPolicy{}, other_storage_ptr); + } else { + Storage* allocated_ptr = this; + Storage* inlined_ptr = other_storage_ptr; + if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr); + + StorageView allocated_storage_view{ + allocated_ptr->GetAllocatedData(), allocated_ptr->GetSize(), + allocated_ptr->GetAllocatedCapacity()}; + + IteratorValueAdapter> move_values( + MoveIterator(inlined_ptr->GetInlinedData())); + + ABSL_INTERNAL_TRY { + ConstructElements(inlined_ptr->GetAllocator(), + allocated_ptr->GetInlinedData(), move_values, + inlined_ptr->GetSize()); + } + ABSL_INTERNAL_CATCH_ANY { + allocated_ptr->SetAllocation(Allocation{ + allocated_storage_view.data, allocated_storage_view.capacity}); + ABSL_INTERNAL_RETHROW; + } + + DestroyAdapter::DestroyElements(inlined_ptr->GetAllocator(), + inlined_ptr->GetInlinedData(), + inlined_ptr->GetSize()); + + inlined_ptr->SetAllocation(Allocation{allocated_storage_view.data, + allocated_storage_view.capacity}); + } + + swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); + swap(GetAllocator(), other_storage_ptr->GetAllocator()); +} + +template +void Storage::SwapN(ElementwiseSwapPolicy, Storage* other, + SizeType n) { + std::swap_ranges(GetInlinedData(), GetInlinedData() + n, + other->GetInlinedData()); +} + +template +void Storage::SwapN(ElementwiseConstructPolicy, Storage* other, + SizeType n) { + Pointer a = GetInlinedData(); + Pointer b = other->GetInlinedData(); + // see note on allocators in `SwapInlinedElements`. + A& allocator_a = GetAllocator(); + A& allocator_b = other->GetAllocator(); + for (SizeType i = 0; i < n; ++i, ++a, ++b) { + ValueType tmp(std::move(*a)); + + AllocatorTraits::destroy(allocator_a, a); + AllocatorTraits::construct(allocator_b, a, std::move(*b)); + + AllocatorTraits::destroy(allocator_b, b); + AllocatorTraits::construct(allocator_a, b, std::move(tmp)); + } +} + +template +void Storage::SwapInlinedElements(MemcpyPolicy, Storage* other) { + Data tmp = data_; + data_ = other->data_; + other->data_ = tmp; +} + +template +template +void Storage::SwapInlinedElements(NotMemcpyPolicy policy, + Storage* other) { + // Note: `destroy` needs to use pre-swap allocator while `construct` - + // post-swap allocator. Allocators will be swapped later on outside of + // `SwapInlinedElements`. + Storage* small_ptr = this; + Storage* large_ptr = other; + if (small_ptr->GetSize() > large_ptr->GetSize()) { + std::swap(small_ptr, large_ptr); + } + + auto small_size = small_ptr->GetSize(); + auto diff = large_ptr->GetSize() - small_size; + SwapN(policy, other, small_size); + + IteratorValueAdapter> move_values( + MoveIterator(large_ptr->GetInlinedData() + small_size)); + + ConstructElements(large_ptr->GetAllocator(), + small_ptr->GetInlinedData() + small_size, move_values, + diff); + + DestroyAdapter::DestroyElements(large_ptr->GetAllocator(), + large_ptr->GetInlinedData() + small_size, + diff); +} + +// End ignore "array-bounds" +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +} // namespace inlined_vector_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/layout.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/layout.h new file mode 100644 index 00000000..58c8d4f1 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/layout.h @@ -0,0 +1,828 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// MOTIVATION AND TUTORIAL +// +// If you want to put in a single heap allocation N doubles followed by M ints, +// it's easy if N and M are known at compile time. +// +// struct S { +// double a[N]; +// int b[M]; +// }; +// +// S* p = new S; +// +// But what if N and M are known only in run time? Class template Layout to the +// rescue! It's a portable generalization of the technique known as struct hack. +// +// // This object will tell us everything we need to know about the memory +// // layout of double[N] followed by int[M]. It's structurally identical to +// // size_t[2] that stores N and M. It's very cheap to create. +// const Layout layout(N, M); +// +// // Allocate enough memory for both arrays. `AllocSize()` tells us how much +// // memory is needed. We are free to use any allocation function we want as +// // long as it returns aligned memory. +// std::unique_ptr p(new unsigned char[layout.AllocSize()]); +// +// // Obtain the pointer to the array of doubles. +// // Equivalent to `reinterpret_cast(p.get())`. +// // +// // We could have written layout.Pointer<0>(p) instead. If all the types are +// // unique you can use either form, but if some types are repeated you must +// // use the index form. +// double* a = layout.Pointer(p.get()); +// +// // Obtain the pointer to the array of ints. +// // Equivalent to `reinterpret_cast(p.get() + N * 8)`. +// int* b = layout.Pointer(p); +// +// If we are unable to specify sizes of all fields, we can pass as many sizes as +// we can to `Partial()`. In return, it'll allow us to access the fields whose +// locations and sizes can be computed from the provided information. +// `Partial()` comes in handy when the array sizes are embedded into the +// allocation. +// +// // size_t[0] containing N, size_t[1] containing M, double[N], int[M]. +// using L = Layout; +// +// unsigned char* Allocate(size_t n, size_t m) { +// const L layout(1, 1, n, m); +// unsigned char* p = new unsigned char[layout.AllocSize()]; +// *layout.Pointer<0>(p) = n; +// *layout.Pointer<1>(p) = m; +// return p; +// } +// +// void Use(unsigned char* p) { +// // First, extract N and M. +// // Specify that the first array has only one element. Using `prefix` we +// // can access the first two arrays but not more. +// constexpr auto prefix = L::Partial(1); +// size_t n = *prefix.Pointer<0>(p); +// size_t m = *prefix.Pointer<1>(p); +// +// // Now we can get pointers to the payload. +// const L layout(1, 1, n, m); +// double* a = layout.Pointer(p); +// int* b = layout.Pointer(p); +// } +// +// The layout we used above combines fixed-size with dynamically-sized fields. +// This is quite common. Layout is optimized for this use case and attempts to +// generate optimal code. To help the compiler do that in more cases, you can +// specify the fixed sizes using `WithStaticSizes`. This ensures that all +// computations that can be performed at compile time are indeed performed at +// compile time. Note that sometimes the `template` keyword is needed. E.g.: +// +// using SL = L::template WithStaticSizes<1, 1>; +// +// void Use(unsigned char* p) { +// // First, extract N and M. +// // Using `prefix` we can access the first three arrays but not more. +// // +// // More details: The first element always has offset 0. `SL` +// // has offsets for the second and third array based on sizes of +// // the first and second array, specified via `WithStaticSizes`. +// constexpr auto prefix = SL::Partial(); +// size_t n = *prefix.Pointer<0>(p); +// size_t m = *prefix.Pointer<1>(p); +// +// // Now we can get a pointer to the final payload. +// const SL layout(n, m); +// double* a = layout.Pointer(p); +// int* b = layout.Pointer(p); +// } +// +// Efficiency tip: The order of fields matters. In `Layout` try to +// ensure that `alignof(T1) >= ... >= alignof(TN)`. This way you'll have no +// padding in between arrays. +// +// You can manually override the alignment of an array by wrapping the type in +// `Aligned`. `Layout<..., Aligned, ...>` has exactly the same API +// and behavior as `Layout<..., T, ...>` except that the first element of the +// array of `T` is aligned to `N` (the rest of the elements follow without +// padding). `N` cannot be less than `alignof(T)`. +// +// `AllocSize()` and `Pointer()` are the most basic methods for dealing with +// memory layouts. Check out the reference or code below to discover more. +// +// EXAMPLE +// +// // Immutable move-only string with sizeof equal to sizeof(void*). The +// // string size and the characters are kept in the same heap allocation. +// class CompactString { +// public: +// CompactString(const char* s = "") { +// const size_t size = strlen(s); +// // size_t[1] followed by char[size + 1]. +// const L layout(size + 1); +// p_.reset(new unsigned char[layout.AllocSize()]); +// // If running under ASAN, mark the padding bytes, if any, to catch +// // memory errors. +// layout.PoisonPadding(p_.get()); +// // Store the size in the allocation. +// *layout.Pointer(p_.get()) = size; +// // Store the characters in the allocation. +// memcpy(layout.Pointer(p_.get()), s, size + 1); +// } +// +// size_t size() const { +// // Equivalent to reinterpret_cast(*p). +// return *L::Partial().Pointer(p_.get()); +// } +// +// const char* c_str() const { +// // Equivalent to reinterpret_cast(p.get() + sizeof(size_t)). +// return L::Partial().Pointer(p_.get()); +// } +// +// private: +// // Our heap allocation contains a single size_t followed by an array of +// // chars. +// using L = Layout::WithStaticSizes<1>; +// std::unique_ptr p_; +// }; +// +// int main() { +// CompactString s = "hello"; +// assert(s.size() == 5); +// assert(strcmp(s.c_str(), "hello") == 0); +// } +// +// DOCUMENTATION +// +// The interface exported by this file consists of: +// - class `Layout<>` and its public members. +// - The public members of classes `internal_layout::LayoutWithStaticSizes<>` +// and `internal_layout::LayoutImpl<>`. Those classes aren't intended to be +// used directly, and their name and template parameter list are internal +// implementation details, but the classes themselves provide most of the +// functionality in this file. See comments on their members for detailed +// documentation. +// +// `Layout::Partial(count1,..., countm)` (where `m` <= `n`) returns a +// `LayoutImpl<>` object. `Layout layout(count1,..., countn)` +// creates a `Layout` object, which exposes the same functionality by inheriting +// from `LayoutImpl<>`. + +#ifndef ABSL_CONTAINER_INTERNAL_LAYOUT_H_ +#define ABSL_CONTAINER_INTERNAL_LAYOUT_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/debugging/internal/demangle.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/str_cat.h" +#include "absl/types/span.h" +#include "absl/utility/utility.h" + +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +#include +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A type wrapper that instructs `Layout` to use the specific alignment for the +// array. `Layout<..., Aligned, ...>` has exactly the same API +// and behavior as `Layout<..., T, ...>` except that the first element of the +// array of `T` is aligned to `N` (the rest of the elements follow without +// padding). +// +// Requires: `N >= alignof(T)` and `N` is a power of 2. +template +struct Aligned; + +namespace internal_layout { + +template +struct NotAligned {}; + +template +struct NotAligned> { + static_assert(sizeof(T) == 0, "Aligned cannot be const-qualified"); +}; + +template +using IntToSize = size_t; + +template +struct Type : NotAligned { + using type = T; +}; + +template +struct Type> { + using type = T; +}; + +template +struct SizeOf : NotAligned, std::integral_constant {}; + +template +struct SizeOf> : std::integral_constant {}; + +// Note: workaround for https://gcc.gnu.org/PR88115 +template +struct AlignOf : NotAligned { + static constexpr size_t value = alignof(T); +}; + +template +struct AlignOf> { + static_assert(N % alignof(T) == 0, + "Custom alignment can't be lower than the type's alignment"); + static constexpr size_t value = N; +}; + +// Does `Ts...` contain `T`? +template +using Contains = absl::disjunction...>; + +template +using CopyConst = + typename std::conditional::value, const To, To>::type; + +// Note: We're not qualifying this with absl:: because it doesn't compile under +// MSVC. +template +using SliceType = Span; + +// This namespace contains no types. It prevents functions defined in it from +// being found by ADL. +namespace adl_barrier { + +template +constexpr size_t Find(Needle, Needle, Ts...) { + static_assert(!Contains(), "Duplicate element type"); + return 0; +} + +template +constexpr size_t Find(Needle, T, Ts...) { + return adl_barrier::Find(Needle(), Ts()...) + 1; +} + +constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); } + +// Returns `q * m` for the smallest `q` such that `q * m >= n`. +// Requires: `m` is a power of two. It's enforced by IsLegalElementType below. +constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); } + +constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; } + +constexpr size_t Max(size_t a) { return a; } + +template +constexpr size_t Max(size_t a, size_t b, Ts... rest) { + return adl_barrier::Max(b < a ? a : b, rest...); +} + +template +std::string TypeName() { + std::string out; +#ifdef ABSL_INTERNAL_HAS_RTTI + absl::StrAppend(&out, "<", + absl::debugging_internal::DemangleString(typeid(T).name()), + ">"); +#endif + return out; +} + +} // namespace adl_barrier + +// Can `T` be a template argument of `Layout`? +template +using IsLegalElementType = std::integral_constant< + bool, !std::is_reference::value && !std::is_volatile::value && + !std::is_reference::type>::value && + !std::is_volatile::type>::value && + adl_barrier::IsPow2(AlignOf::value)>; + +template +class LayoutImpl; + +// Public base class of `Layout` and the result type of `Layout::Partial()`. +// +// `Elements...` contains all template arguments of `Layout` that created this +// instance. +// +// `StaticSizeSeq...` is an index_sequence containing the sizes specified at +// compile-time. +// +// `RuntimeSizeSeq...` is `[0, NumRuntimeSizes)`, where `NumRuntimeSizes` is the +// number of arguments passed to `Layout::Partial()` or `Layout::Layout()`. +// +// `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is `NumRuntimeSizes` plus +// the number of sizes in `StaticSizeSeq`. +// +// `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is +// `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we +// can compute offsets). +template +class LayoutImpl< + std::tuple, absl::index_sequence, + absl::index_sequence, absl::index_sequence, + absl::index_sequence> { + private: + static_assert(sizeof...(Elements) > 0, "At least one field is required"); + static_assert(absl::conjunction...>::value, + "Invalid element type (see IsLegalElementType)"); + static_assert(sizeof...(StaticSizeSeq) <= sizeof...(Elements), + "Too many static sizes specified"); + + enum { + NumTypes = sizeof...(Elements), + NumStaticSizes = sizeof...(StaticSizeSeq), + NumRuntimeSizes = sizeof...(RuntimeSizeSeq), + NumSizes = sizeof...(SizeSeq), + NumOffsets = sizeof...(OffsetSeq), + }; + + // These are guaranteed by `Layout`. + static_assert(NumStaticSizes + NumRuntimeSizes == NumSizes, "Internal error"); + static_assert(NumSizes <= NumTypes, "Internal error"); + static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1), + "Internal error"); + static_assert(NumTypes > 0, "Internal error"); + + static constexpr std::array kStaticSizes = { + StaticSizeSeq...}; + + // Returns the index of `T` in `Elements...`. Results in a compilation error + // if `Elements...` doesn't contain exactly one instance of `T`. + template + static constexpr size_t ElementIndex() { + static_assert(Contains, Type::type>...>(), + "Type not found"); + return adl_barrier::Find(Type(), + Type::type>()...); + } + + template + using ElementAlignment = + AlignOf>::type>; + + public: + // Element types of all arrays packed in a tuple. + using ElementTypes = std::tuple::type...>; + + // Element type of the Nth array. + template + using ElementType = typename std::tuple_element::type; + + constexpr explicit LayoutImpl(IntToSize... sizes) + : size_{sizes...} {} + + // Alignment of the layout, equal to the strictest alignment of all elements. + // All pointers passed to the methods of layout must be aligned to this value. + static constexpr size_t Alignment() { + return adl_barrier::Max(AlignOf::value...); + } + + // Offset in bytes of the Nth array. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Offset<0>() == 0); // The ints starts from 0. + // assert(x.Offset<1>() == 16); // The doubles starts from 16. + // + // Requires: `N <= NumSizes && N < sizeof...(Ts)`. + template + constexpr size_t Offset() const { + if constexpr (N == 0) { + return 0; + } else { + static_assert(N < NumOffsets, "Index out of bounds"); + return adl_barrier::Align( + Offset() + SizeOf>::value * Size(), + ElementAlignment::value); + } + } + + // Offset in bytes of the array with the specified element type. There must + // be exactly one such array and its zero-based index must be at most + // `NumSizes`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Offset() == 0); // The ints starts from 0. + // assert(x.Offset() == 16); // The doubles starts from 16. + template + constexpr size_t Offset() const { + return Offset()>(); + } + + // Offsets in bytes of all arrays for which the offsets are known. + constexpr std::array Offsets() const { + return {{Offset()...}}; + } + + // The number of elements in the Nth array (zero-based). + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Size<0>() == 3); + // assert(x.Size<1>() == 4); + // + // Requires: `N < NumSizes`. + template + constexpr size_t Size() const { + if constexpr (N < NumStaticSizes) { + return kStaticSizes[N]; + } else { + static_assert(N < NumSizes, "Index out of bounds"); + return size_[N - NumStaticSizes]; + } + } + + // The number of elements in the array with the specified element type. + // There must be exactly one such array and its zero-based index must be + // at most `NumSizes`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Size() == 3); + // assert(x.Size() == 4); + template + constexpr size_t Size() const { + return Size()>(); + } + + // The number of elements of all arrays for which they are known. + constexpr std::array Sizes() const { + return {{Size()...}}; + } + + // Pointer to the beginning of the Nth array. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // int* ints = x.Pointer<0>(p); + // double* doubles = x.Pointer<1>(p); + // + // Requires: `N <= NumSizes && N < sizeof...(Ts)`. + // Requires: `p` is aligned to `Alignment()`. + template + CopyConst>* Pointer(Char* p) const { + using C = typename std::remove_const::type; + static_assert( + std::is_same() || std::is_same() || + std::is_same(), + "The argument must be a pointer to [const] [signed|unsigned] char"); + constexpr size_t alignment = Alignment(); + (void)alignment; + assert(reinterpret_cast(p) % alignment == 0); + return reinterpret_cast>*>(p + Offset()); + } + + // Pointer to the beginning of the array with the specified element type. + // There must be exactly one such array and its zero-based index must be at + // most `NumSizes`. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // int* ints = x.Pointer(p); + // double* doubles = x.Pointer(p); + // + // Requires: `p` is aligned to `Alignment()`. + template + CopyConst* Pointer(Char* p) const { + return Pointer()>(p); + } + + // Pointers to all arrays for which pointers are known. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // + // int* ints; + // double* doubles; + // std::tie(ints, doubles) = x.Pointers(p); + // + // Requires: `p` is aligned to `Alignment()`. + template + auto Pointers(Char* p) const { + return std::tuple>*...>( + Pointer(p)...); + } + + // The Nth array. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // Span ints = x.Slice<0>(p); + // Span doubles = x.Slice<1>(p); + // + // Requires: `N < NumSizes`. + // Requires: `p` is aligned to `Alignment()`. + template + SliceType>> Slice(Char* p) const { + return SliceType>>(Pointer(p), Size()); + } + + // The array with the specified element type. There must be exactly one + // such array and its zero-based index must be less than `NumSizes`. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // Span ints = x.Slice(p); + // Span doubles = x.Slice(p); + // + // Requires: `p` is aligned to `Alignment()`. + template + SliceType> Slice(Char* p) const { + return Slice()>(p); + } + + // All arrays with known sizes. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // + // Span ints; + // Span doubles; + // std::tie(ints, doubles) = x.Slices(p); + // + // Requires: `p` is aligned to `Alignment()`. + // + // Note: We mark the parameter as maybe_unused because GCC detects it is not + // used when `SizeSeq` is empty [-Werror=unused-but-set-parameter]. + template + auto Slices([[maybe_unused]] Char* p) const { + return std::tuple>>...>( + Slice(p)...); + } + + // The size of the allocation that fits all arrays. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; // 48 bytes + // + // Requires: `NumSizes == sizeof...(Ts)`. + constexpr size_t AllocSize() const { + static_assert(NumTypes == NumSizes, "You must specify sizes of all fields"); + return Offset() + + SizeOf>::value * Size(); + } + + // If built with --config=asan, poisons padding bytes (if any) in the + // allocation. The pointer must point to a memory block at least + // `AllocSize()` bytes in length. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // Requires: `p` is aligned to `Alignment()`. + template + void PoisonPadding(const Char* p) const { + if constexpr (N == 0) { + Pointer<0>(p); // verify the requirements on `Char` and `p` + } else { + static_assert(N < NumOffsets, "Index out of bounds"); + (void)p; +#ifdef ABSL_HAVE_ADDRESS_SANITIZER + PoisonPadding(p); + // The `if` is an optimization. It doesn't affect the observable behaviour. + if (ElementAlignment::value % ElementAlignment::value) { + size_t start = + Offset() + SizeOf>::value * Size(); + ASAN_POISON_MEMORY_REGION(p + start, Offset() - start); + } +#endif + } + } + + // Human-readable description of the memory layout. Useful for debugging. + // Slow. + // + // // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed + // // by an unknown number of doubles. + // auto x = Layout::Partial(5, 3); + // assert(x.DebugString() == + // "@0(1)[5]; @8(4)[3]; @24(8)"); + // + // Each field is in the following format: @offset(sizeof)[size] ( + // may be missing depending on the target platform). For example, + // @8(4)[3] means that at offset 8 we have an array of ints, where each + // int is 4 bytes, and we have 3 of those ints. The size of the last field may + // be missing (as in the example above). Only fields with known offsets are + // described. Type names may differ across platforms: one compiler might + // produce "unsigned*" where another produces "unsigned int *". + std::string DebugString() const { + const auto offsets = Offsets(); + const size_t sizes[] = {SizeOf>::value...}; + const std::string types[] = { + adl_barrier::TypeName>()...}; + std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")"); + for (size_t i = 0; i != NumOffsets - 1; ++i) { + absl::StrAppend(&res, "[", DebugSize(i), "]; @", offsets[i + 1], + types[i + 1], "(", sizes[i + 1], ")"); + } + // NumSizes is a constant that may be zero. Some compilers cannot see that + // inside the if statement "size_[NumSizes - 1]" must be valid. + int last = static_cast(NumSizes) - 1; + if (NumTypes == NumSizes && last >= 0) { + absl::StrAppend(&res, "[", DebugSize(static_cast(last)), "]"); + } + return res; + } + + private: + size_t DebugSize(size_t n) const { + if (n < NumStaticSizes) { + return kStaticSizes[n]; + } else { + return size_[n - NumStaticSizes]; + } + } + + // Arguments of `Layout::Partial()` or `Layout::Layout()`. + size_t size_[NumRuntimeSizes > 0 ? NumRuntimeSizes : 1]; +}; + +template +using LayoutType = LayoutImpl< + std::tuple, StaticSizeSeq, + absl::make_index_sequence, + absl::make_index_sequence, + absl::make_index_sequence>; + +template +class LayoutWithStaticSizes + : public LayoutType { + private: + using Super = + LayoutType; + + public: + // The result type of `Partial()` with `NumSizes` arguments. + template + using PartialType = + internal_layout::LayoutType; + + // `Layout` knows the element types of the arrays we want to lay out in + // memory but not the number of elements in each array. + // `Partial(size1, ..., sizeN)` allows us to specify the latter. The + // resulting immutable object can be used to obtain pointers to the + // individual arrays. + // + // It's allowed to pass fewer array sizes than the number of arrays. E.g., + // if all you need is to the offset of the second array, you only need to + // pass one argument -- the number of elements in the first array. + // + // // int[3] followed by 4 bytes of padding and an unknown number of + // // doubles. + // auto x = Layout::Partial(3); + // // doubles start at byte 16. + // assert(x.Offset<1>() == 16); + // + // If you know the number of elements in all arrays, you can still call + // `Partial()` but it's more convenient to use the constructor of `Layout`. + // + // Layout x(3, 5); + // + // Note: The sizes of the arrays must be specified in number of elements, + // not in bytes. + // + // Requires: `sizeof...(Sizes) + NumStaticSizes <= sizeof...(Ts)`. + // Requires: all arguments are convertible to `size_t`. + template + static constexpr PartialType Partial(Sizes&&... sizes) { + static_assert(sizeof...(Sizes) + StaticSizeSeq::size() <= sizeof...(Ts), + ""); + return PartialType( + static_cast(std::forward(sizes))...); + } + + // Inherit LayoutType's constructor. + // + // Creates a layout with the sizes of all arrays specified. If you know + // only the sizes of the first N arrays (where N can be zero), you can use + // `Partial()` defined above. The constructor is essentially equivalent to + // calling `Partial()` and passing in all array sizes; the constructor is + // provided as a convenient abbreviation. + // + // Note: The sizes of the arrays must be specified in number of elements, + // not in bytes. + // + // Implementation note: we do this via a `using` declaration instead of + // defining our own explicit constructor because the signature of LayoutType's + // constructor depends on RuntimeSizeSeq, which we don't have access to here. + // If we defined our own constructor here, it would have to use a parameter + // pack and then cast the arguments to size_t when calling the superclass + // constructor, similar to what Partial() does. But that would suffer from the + // same problem that Partial() has, which is that the parameter types are + // inferred from the arguments, which may be signed types, which must then be + // cast to size_t. This can lead to negative values being silently (i.e. with + // no compiler warnings) cast to an unsigned type. Having a constructor with + // size_t parameters helps the compiler generate better warnings about + // potential bad casts, while avoiding false warnings when positive literal + // arguments are used. If an argument is a positive literal integer (e.g. + // `1`), the compiler will understand that it can be safely converted to + // size_t, and hence not generate a warning. But if a negative literal (e.g. + // `-1`) or a variable with signed type is used, then it can generate a + // warning about a potentially unsafe implicit cast. It would be great if we + // could do this for Partial() too, but unfortunately as of C++23 there seems + // to be no way to define a function with a variable number of parameters of a + // certain type, a.k.a. homogeneous function parameter packs. So we're forced + // to choose between explicitly casting the arguments to size_t, which + // suppresses all warnings, even potentially valid ones, or implicitly casting + // them to size_t, which generates bogus warnings whenever literal arguments + // are used, even if they're positive. + using Super::Super; +}; + +} // namespace internal_layout + +// Descriptor of arrays of various types and sizes laid out in memory one after +// another. See the top of the file for documentation. +// +// Check out the public API of internal_layout::LayoutWithStaticSizes and +// internal_layout::LayoutImpl above. Those types are internal to the library +// but their methods are public, and they are inherited by `Layout`. +template +class Layout : public internal_layout::LayoutWithStaticSizes< + absl::make_index_sequence<0>, Ts...> { + private: + using Super = + internal_layout::LayoutWithStaticSizes, + Ts...>; + + public: + // If you know the sizes of some or all of the arrays at compile time, you can + // use `WithStaticSizes` or `WithStaticSizeSequence` to create a `Layout` type + // with those sizes baked in. This can help the compiler generate optimal code + // for calculating array offsets and AllocSize(). + // + // Like `Partial()`, the N sizes you specify are for the first N arrays, and + // they specify the number of elements in each array, not the number of bytes. + template + using WithStaticSizeSequence = + internal_layout::LayoutWithStaticSizes; + + template + using WithStaticSizes = + WithStaticSizeSequence>; + + // Inherit LayoutWithStaticSizes's constructor, which requires you to specify + // all the array sizes. + using Super::Super; +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/node_slot_policy.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/node_slot_policy.h new file mode 100644 index 00000000..3f1874d4 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/node_slot_policy.h @@ -0,0 +1,95 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Adapts a policy for nodes. +// +// The node policy should model: +// +// struct Policy { +// // Returns a new node allocated and constructed using the allocator, using +// // the specified arguments. +// template +// value_type* new_element(Alloc* alloc, Args&&... args) const; +// +// // Destroys and deallocates node using the allocator. +// template +// void delete_element(Alloc* alloc, value_type* node) const; +// }; +// +// It may also optionally define `value()` and `apply()`. For documentation on +// these, see hash_policy_traits.h. + +#ifndef ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ +#define ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct node_slot_policy { + static_assert(std::is_lvalue_reference::value, ""); + + using slot_type = typename std::remove_cv< + typename std::remove_reference::type>::type*; + + template + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + *slot = Policy::new_element(alloc, std::forward(args)...); + } + + template + static void destroy(Alloc* alloc, slot_type* slot) { + Policy::delete_element(alloc, *slot); + } + + // Returns true_type to indicate that transfer can use memcpy. + template + static std::true_type transfer(Alloc*, slot_type* new_slot, + slot_type* old_slot) { + *new_slot = *old_slot; + return {}; + } + + static size_t space_used(const slot_type* slot) { + if (slot == nullptr) return Policy::element_space_used(nullptr); + return Policy::element_space_used(*slot); + } + + static Reference element(slot_type* slot) { return **slot; } + + template + static auto value(T* elem) -> decltype(P::value(elem)) { + return P::value(elem); + } + + template + static auto apply(Ts&&... ts) -> decltype(P::apply(std::forward(ts)...)) { + return P::apply(std::forward(ts)...); + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_map.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_map.h new file mode 100644 index 00000000..b42a4f22 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_map.h @@ -0,0 +1,357 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ +#define ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/throw_delegate.h" +#include "absl/container/internal/common_policy_traits.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class raw_hash_map : public raw_hash_set { + // P is Policy. It's passed as a template argument to support maps that have + // incomplete types as values, as in unordered_map. + // MappedReference<> may be a non-reference type. + template + using MappedReference = decltype(P::value( + std::addressof(std::declval()))); + + // MappedConstReference<> may be a non-reference type. + template + using MappedConstReference = decltype(P::value( + std::addressof(std::declval()))); + + template + using key_arg = + typename KeyArg::value && IsTransparent::value>:: + template type; + + // NOTE: The mess here is to shorten the code for the (very repetitive) + // function overloads, and to allow the lifetime-bound overloads to dispatch + // to the non-lifetime-bound overloads, to ensure there is a single source of + // truth for each overload set. + // + // Enabled if an assignment from the given type would require the + // source object to remain alive for the life of the element. + // + // TODO(b/402804213): Remove these traits and simplify the overloads whenever + // we have a better mechanism available to handle lifetime analysis. + template + using LifetimeBoundK = HasValue< + Value, std::conditional_t::value, + std::false_type, + type_traits_internal::IsLifetimeBoundAssignment< + typename Policy::key_type, K>>>; + template + using LifetimeBoundV = + HasValue>; + template + using LifetimeBoundKV = + absl::conjunction>, + LifetimeBoundV>; + + public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + + static_assert(!std::is_reference::value, ""); + + // TODO(b/187807849): Evaluate whether to support reference mapped_type and + // remove this assertion if/when it is supported. + static_assert(!std::is_reference::value, ""); + + using iterator = typename raw_hash_map::raw_hash_set::iterator; + using const_iterator = typename raw_hash_map::raw_hash_set::const_iterator; + + raw_hash_map() {} + using raw_hash_map::raw_hash_set::raw_hash_set; + + // The last two template parameters ensure that both arguments are rvalues + // (lvalue arguments are handled by the overloads below). This is necessary + // for supporting bitfield arguments. + // + // union { int n : 1; }; + // flat_hash_map m; + // m.insert_or_assign(n, n); + // + // TODO(b/402804213): Remove these macros whenever we have a better mechanism + // available to handle lifetime analysis. +#define ABSL_INTERNAL_X(Func, Callee, KQual, VQual, KValue, VValue, Tail, ...) \ + template < \ + typename K = key_type, class V = mapped_type, \ + ABSL_INTERNAL_IF_##KValue##_NOR_##VValue( \ + int = (EnableIf::AddPtr, \ + IfRRef::AddPtr>>()), \ + ABSL_INTERNAL_SINGLE_ARG( \ + int &..., \ + decltype(EnableIf>()) = \ + 0))> \ + decltype(auto) Func( \ + __VA_ARGS__ key_arg KQual k ABSL_INTERNAL_IF_##KValue( \ + ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)), \ + V VQual v ABSL_INTERNAL_IF_##VValue(ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY( \ + this))) ABSL_ATTRIBUTE_LIFETIME_BOUND { \ + return ABSL_INTERNAL_IF_##KValue##_OR_##VValue( \ + (this->template Func), Callee)( \ + std::forward(k), std::forward(v)) Tail; \ + } \ + static_assert(true, "This is to force a semicolon.") + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, true, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, true, ABSL_INTERNAL_SINGLE_ARG()); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + true, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + true, ABSL_INTERNAL_SINGLE_ARG()); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + true, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + true, ABSL_INTERNAL_SINGLE_ARG()); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, false, + ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, true, + ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, false, + ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, true, + ABSL_INTERNAL_SINGLE_ARG()); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, true, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, true, .first, const_iterator ABSL_INTERNAL_COMMA); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + true, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + true, .first, const_iterator ABSL_INTERNAL_COMMA); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + true, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + true, .first, const_iterator ABSL_INTERNAL_COMMA); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, false, + .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, true, + .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, false, + .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, true, + .first, const_iterator ABSL_INTERNAL_COMMA); +#undef ABSL_INTERNAL_X + + // All `try_emplace()` overloads make the same guarantees regarding rvalue + // arguments as `std::unordered_map::try_emplace()`, namely that these + // functions will not move from rvalue arguments if insertions do not happen. + template >(), + class... Args, + typename std::enable_if< + !std::is_convertible::value, int>::type = 0> + std::pair try_emplace(key_arg &&k, Args &&...args) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return try_emplace_impl(std::forward(k), std::forward(args)...); + } + + template > = 0, + typename std::enable_if< + !std::is_convertible::value, int>::type = 0> + std::pair try_emplace( + key_arg &&k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this), + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template try_emplace(std::forward(k), + std::forward(args)...); + } + + template >(), + class... Args, + typename std::enable_if< + !std::is_convertible::value, int>::type = 0> + std::pair try_emplace(const key_arg &k, Args &&...args) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return try_emplace_impl(k, std::forward(args)...); + } + template > = 0, + typename std::enable_if< + !std::is_convertible::value, int>::type = 0> + std::pair try_emplace( + const key_arg &k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this), + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template try_emplace(k, std::forward(args)...); + } + + template >(), + class... Args> + iterator try_emplace(const_iterator, key_arg &&k, + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return try_emplace(std::forward(k), std::forward(args)...).first; + } + template > = 0> + iterator try_emplace(const_iterator hint, + key_arg &&k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this), + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template try_emplace(hint, std::forward(k), + std::forward(args)...); + } + + template >(), + class... Args> + iterator try_emplace(const_iterator, const key_arg &k, + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return try_emplace(k, std::forward(args)...).first; + } + template > = 0> + iterator try_emplace(const_iterator hint, + const key_arg &k + ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this), + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template try_emplace(hint, std::forward(k), + std::forward(args)...); + } + + template + MappedReference

at(const key_arg& key) ABSL_ATTRIBUTE_LIFETIME_BOUND { + auto it = this->find(key); + if (it == this->end()) { + base_internal::ThrowStdOutOfRange( + "absl::container_internal::raw_hash_map<>::at"); + } + return Policy::value(&*it); + } + + template + MappedConstReference

at(const key_arg& key) const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + auto it = this->find(key); + if (it == this->end()) { + base_internal::ThrowStdOutOfRange( + "absl::container_internal::raw_hash_map<>::at"); + } + return Policy::value(&*it); + } + + template >()> + MappedReference

operator[](key_arg &&key) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // It is safe to use unchecked_deref here because try_emplace + // will always return an iterator pointing to a valid item in the table, + // since it inserts if nothing is found for the given key. + return Policy::value( + &this->unchecked_deref(try_emplace(std::forward(key)).first)); + } + template > = 0> + MappedReference

operator[]( + key_arg &&key ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template operator[](std::forward(key)); + } + + template >()> + MappedReference

operator[](const key_arg &key) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // It is safe to use unchecked_deref here because try_emplace + // will always return an iterator pointing to a valid item in the table, + // since it inserts if nothing is found for the given key. + return Policy::value(&this->unchecked_deref(try_emplace(key).first)); + } + template > = 0> + MappedReference

operator[]( + const key_arg &key ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template operator[](key); + } + + private: + template + std::pair insert_or_assign_impl(K&& k, V&& v) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + auto res = this->find_or_prepare_insert(k); + if (res.second) { + this->emplace_at(res.first, std::forward(k), std::forward(v)); + } else { + Policy::value(&*res.first) = std::forward(v); + } + return res; + } + + template + std::pair try_emplace_impl(K&& k, Args&&... args) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + auto res = this->find_or_prepare_insert(k); + if (res.second) { + this->emplace_at(res.first, std::piecewise_construct, + std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)); + } + return res; + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h new file mode 100644 index 00000000..b22f8177 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h @@ -0,0 +1,3816 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// An open-addressing +// hashtable with quadratic probing. +// +// This is a low level hashtable on top of which different interfaces can be +// implemented, like flat_hash_set, node_hash_set, string_hash_set, etc. +// +// The table interface is similar to that of std::unordered_set. Notable +// differences are that most member functions support heterogeneous keys when +// BOTH the hash and eq functions are marked as transparent. They do so by +// providing a typedef called `is_transparent`. +// +// When heterogeneous lookup is enabled, functions that take key_type act as if +// they have an overload set like: +// +// iterator find(const key_type& key); +// template +// iterator find(const K& key); +// +// size_type erase(const key_type& key); +// template +// size_type erase(const K& key); +// +// std::pair equal_range(const key_type& key); +// template +// std::pair equal_range(const K& key); +// +// When heterogeneous lookup is disabled, only the explicit `key_type` overloads +// exist. +// +// In addition the pointer to element and iterator stability guarantees are +// weaker: all iterators and pointers are invalidated after a new element is +// inserted. +// +// IMPLEMENTATION DETAILS +// +// # Table Layout +// +// A raw_hash_set's backing array consists of control bytes followed by slots +// that may or may not contain objects. +// +// The layout of the backing array, for `capacity` slots, is thus, as a +// pseudo-struct: +// +// struct BackingArray { +// // Sampling handler. This field isn't present when the sampling is +// // disabled or this allocation hasn't been selected for sampling. +// HashtablezInfoHandle infoz_; +// // The number of elements we can insert before growing the capacity. +// size_t growth_left; +// // Control bytes for the "real" slots. +// ctrl_t ctrl[capacity]; +// // Always `ctrl_t::kSentinel`. This is used by iterators to find when to +// // stop and serves no other purpose. +// ctrl_t sentinel; +// // A copy of the first `kWidth - 1` elements of `ctrl`. This is used so +// // that if a probe sequence picks a value near the end of `ctrl`, +// // `Group` will have valid control bytes to look at. +// ctrl_t clones[kWidth - 1]; +// // The actual slot data. +// slot_type slots[capacity]; +// }; +// +// The length of this array is computed by `RawHashSetLayout::alloc_size` below. +// +// Control bytes (`ctrl_t`) are bytes (collected into groups of a +// platform-specific size) that define the state of the corresponding slot in +// the slot array. Group manipulation is tightly optimized to be as efficient +// as possible: SSE and friends on x86, clever bit operations on other arches. +// +// Group 1 Group 2 Group 3 +// +---------------+---------------+---------------+ +// | | | | | | | | | | | | | | | | | | | | | | | | | +// +---------------+---------------+---------------+ +// +// Each control byte is either a special value for empty slots, deleted slots +// (sometimes called *tombstones*), and a special end-of-table marker used by +// iterators, or, if occupied, seven bits (H2) from the hash of the value in the +// corresponding slot. +// +// Storing control bytes in a separate array also has beneficial cache effects, +// since more logical slots will fit into a cache line. +// +// # Small Object Optimization (SOO) +// +// When the size/alignment of the value_type and the capacity of the table are +// small, we enable small object optimization and store the values inline in +// the raw_hash_set object. This optimization allows us to avoid +// allocation/deallocation as well as cache/dTLB misses. +// +// # Hashing +// +// We compute two separate hashes, `H1` and `H2`, from the hash of an object. +// `H1(hash(x))` is an index into `slots`, and essentially the starting point +// for the probe sequence. `H2(hash(x))` is a 7-bit value used to filter out +// objects that cannot possibly be the one we are looking for. +// +// # Table operations. +// +// The key operations are `insert`, `find`, and `erase`. +// +// Since `insert` and `erase` are implemented in terms of `find`, we describe +// `find` first. To `find` a value `x`, we compute `hash(x)`. From +// `H1(hash(x))` and the capacity, we construct a `probe_seq` that visits every +// group of slots in some interesting order. +// +// We now walk through these indices. At each index, we select the entire group +// starting with that index and extract potential candidates: occupied slots +// with a control byte equal to `H2(hash(x))`. If we find an empty slot in the +// group, we stop and return an error. Each candidate slot `y` is compared with +// `x`; if `x == y`, we are done and return `&y`; otherwise we continue to the +// next probe index. Tombstones effectively behave like full slots that never +// match the value we're looking for. +// +// The `H2` bits ensure when we compare a slot to an object with `==`, we are +// likely to have actually found the object. That is, the chance is low that +// `==` is called and returns `false`. Thus, when we search for an object, we +// are unlikely to call `==` many times. This likelyhood can be analyzed as +// follows (assuming that H2 is a random enough hash function). +// +// Let's assume that there are `k` "wrong" objects that must be examined in a +// probe sequence. For example, when doing a `find` on an object that is in the +// table, `k` is the number of objects between the start of the probe sequence +// and the final found object (not including the final found object). The +// expected number of objects with an H2 match is then `k/128`. Measurements +// and analysis indicate that even at high load factors, `k` is less than 32, +// meaning that the number of "false positive" comparisons we must perform is +// less than 1/8 per `find`. + +// `insert` is implemented in terms of `unchecked_insert`, which inserts a +// value presumed to not be in the table (violating this requirement will cause +// the table to behave erratically). Given `x` and its hash `hash(x)`, to insert +// it, we construct a `probe_seq` once again, and use it to find the first +// group with an unoccupied (empty *or* deleted) slot. We place `x` into the +// first such slot in the group and mark it as full with `x`'s H2. +// +// To `insert`, we compose `unchecked_insert` with `find`. We compute `h(x)` and +// perform a `find` to see if it's already present; if it is, we're done. If +// it's not, we may decide the table is getting overcrowded (i.e. the load +// factor is greater than 7/8 for big tables; `is_small()` tables use a max load +// factor of 1); in this case, we allocate a bigger array, `unchecked_insert` +// each element of the table into the new array (we know that no insertion here +// will insert an already-present value), and discard the old backing array. At +// this point, we may `unchecked_insert` the value `x`. +// +// Below, `unchecked_insert` is partly implemented by `prepare_insert`, which +// presents a viable, initialized slot pointee to the caller. +// +// `erase` is implemented in terms of `erase_at`, which takes an index to a +// slot. Given an offset, we simply create a tombstone and destroy its contents. +// If we can prove that the slot would not appear in a probe sequence, we can +// make the slot as empty, instead. We can prove this by observing that if a +// group has any empty slots, it has never been full (assuming we never create +// an empty slot in a group with no empties, which this heuristic guarantees we +// never do) and find would stop at this group anyways (since it does not probe +// beyond groups with empties). +// +// `erase` is `erase_at` composed with `find`: if we +// have a value `x`, we can perform a `find`, and then `erase_at` the resulting +// slot. +// +// To iterate, we simply traverse the array, skipping empty and deleted slots +// and stopping when we hit a `kSentinel`. + +#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ +#define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/endian.h" +#include "absl/base/internal/iterator_traits.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/base/options.h" +#include "absl/base/port.h" +#include "absl/base/prefetch.h" +#include "absl/container/internal/common.h" // IWYU pragma: export // for node_handle +#include "absl/container/internal/common_policy_traits.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hash_function_defaults.h" +#include "absl/container/internal/hash_policy_traits.h" +#include "absl/container/internal/hashtable_control_bytes.h" +#include "absl/container/internal/hashtable_debug_hooks.h" +#include "absl/container/internal/hashtablez_sampler.h" +#include "absl/functional/function_ref.h" +#include "absl/hash/hash.h" +#include "absl/hash/internal/weakly_mixed_integer.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/numeric/bits.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +#ifdef ABSL_SWISSTABLE_ENABLE_GENERATIONS +#error ABSL_SWISSTABLE_ENABLE_GENERATIONS cannot be directly set +#elif (defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_HWADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_MEMORY_SANITIZER)) && \ + !defined(NDEBUG_SANITIZER) // If defined, performance is important. +// When compiled in sanitizer mode, we add generation integers to the backing +// array and iterators. In the backing array, we store the generation between +// the control bytes and the slots. When iterators are dereferenced, we assert +// that the container has not been mutated in a way that could cause iterator +// invalidation since the iterator was initialized. +#define ABSL_SWISSTABLE_ENABLE_GENERATIONS +#endif + +#ifdef ABSL_SWISSTABLE_ASSERT +#error ABSL_SWISSTABLE_ASSERT cannot be directly set +#else +// We use this macro for assertions that users may see when the table is in an +// invalid state that sanitizers may help diagnose. +#define ABSL_SWISSTABLE_ASSERT(CONDITION) \ + assert((CONDITION) && "Try enabling sanitizers.") +#endif + +// We use uint8_t so we don't need to worry about padding. +using GenerationType = uint8_t; + +// A sentinel value for empty generations. Using 0 makes it easy to constexpr +// initialize an array of this value. +constexpr GenerationType SentinelEmptyGeneration() { return 0; } + +constexpr GenerationType NextGeneration(GenerationType generation) { + return ++generation == SentinelEmptyGeneration() ? ++generation : generation; +} + +#ifdef ABSL_SWISSTABLE_ENABLE_GENERATIONS +constexpr bool SwisstableGenerationsEnabled() { return true; } +constexpr size_t NumGenerationBytes() { return sizeof(GenerationType); } +#else +constexpr bool SwisstableGenerationsEnabled() { return false; } +constexpr size_t NumGenerationBytes() { return 0; } +#endif + +// Returns true if we should assert that the table is not accessed after it has +// been destroyed or during the destruction of the table. +constexpr bool SwisstableAssertAccessToDestroyedTable() { +#ifndef NDEBUG + return true; +#endif + return SwisstableGenerationsEnabled(); +} + +template +void SwapAlloc(AllocType& lhs, AllocType& rhs, + std::true_type /* propagate_on_container_swap */) { + using std::swap; + swap(lhs, rhs); +} +template +void SwapAlloc(AllocType& lhs, AllocType& rhs, + std::false_type /* propagate_on_container_swap */) { + (void)lhs; + (void)rhs; + assert(lhs == rhs && + "It's UB to call swap with unequal non-propagating allocators."); +} + +template +void CopyAlloc(AllocType& lhs, AllocType& rhs, + std::true_type /* propagate_alloc */) { + lhs = rhs; +} +template +void CopyAlloc(AllocType&, AllocType&, std::false_type /* propagate_alloc */) {} + +// The state for a probe sequence. +// +// Currently, the sequence is a triangular progression of the form +// +// p(i) := Width * (i^2 + i)/2 + hash (mod mask + 1) +// +// The use of `Width` ensures that each probe step does not overlap groups; +// the sequence effectively outputs the addresses of *groups* (although not +// necessarily aligned to any boundary). The `Group` machinery allows us +// to check an entire group with minimal branching. +// +// Wrapping around at `mask + 1` is important, but not for the obvious reason. +// As described above, the first few entries of the control byte array +// are mirrored at the end of the array, which `Group` will find and use +// for selecting candidates. However, when those candidates' slots are +// actually inspected, there are no corresponding slots for the cloned bytes, +// so we need to make sure we've treated those offsets as "wrapping around". +// +// It turns out that this probe sequence visits every group exactly once if the +// number of groups is a power of two, since (i^2+i)/2 is a bijection in +// Z/(2^m). See https://en.wikipedia.org/wiki/Quadratic_probing +template +class probe_seq { + public: + // Creates a new probe sequence using `hash` as the initial value of the + // sequence and `mask` (usually the capacity of the table) as the mask to + // apply to each value in the progression. + probe_seq(size_t hash, size_t mask) { + ABSL_SWISSTABLE_ASSERT(((mask + 1) & mask) == 0 && "not a mask"); + mask_ = mask; + offset_ = hash & mask_; + } + + // The offset within the table, i.e., the value `p(i)` above. + size_t offset() const { return offset_; } + size_t offset(size_t i) const { return (offset_ + i) & mask_; } + + void next() { + index_ += Width; + offset_ += index_; + offset_ &= mask_; + } + // 0-based probe index, a multiple of `Width`. + size_t index() const { return index_; } + + private: + size_t mask_; + size_t offset_; + size_t index_ = 0; +}; + +template +struct RequireUsableKey { + template + std::pair< + decltype(std::declval()(std::declval())), + decltype(std::declval()(std::declval(), + std::declval()))>* + operator()(const PassedKey&, const Args&...) const; +}; + +template +struct IsDecomposable : std::false_type {}; + +template +struct IsDecomposable< + absl::void_t(), + std::declval()...))>, + Policy, Hash, Eq, Ts...> : std::true_type {}; + +// TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it. +template +constexpr bool IsNoThrowSwappable(std::true_type = {} /* is_swappable */) { + using std::swap; + return noexcept(swap(std::declval(), std::declval())); +} +template +constexpr bool IsNoThrowSwappable(std::false_type /* is_swappable */) { + return false; +} + +// See definition comment for why this is size 32. +ABSL_DLL extern const ctrl_t kEmptyGroup[32]; + +// We use these sentinel capacity values in debug mode to indicate different +// classes of bugs. +enum InvalidCapacity : size_t { + kAboveMaxValidCapacity = ~size_t{} - 100, + kReentrance, + kDestroyed, + + // These two must be last because we use `>= kMovedFrom` to mean moved-from. + kMovedFrom, + kSelfMovedFrom, +}; + +// Returns a pointer to a control byte group that can be used by empty tables. +inline ctrl_t* EmptyGroup() { + // Const must be cast away here; no uses of this function will actually write + // to it because it is only used for empty tables. + return const_cast(kEmptyGroup + 16); +} + +// For use in SOO iterators. +// TODO(b/289225379): we could potentially get rid of this by adding an is_soo +// bit in iterators. This would add branches but reduce cache misses. +ABSL_DLL extern const ctrl_t kSooControl[17]; + +// Returns a pointer to a full byte followed by a sentinel byte. +inline ctrl_t* SooControl() { + // Const must be cast away here; no uses of this function will actually write + // to it because it is only used for SOO iterators. + return const_cast(kSooControl); +} +// Whether ctrl is from the SooControl array. +inline bool IsSooControl(const ctrl_t* ctrl) { return ctrl == SooControl(); } + +// Returns a pointer to a generation to use for an empty hashtable. +GenerationType* EmptyGeneration(); + +// Returns whether `generation` is a generation for an empty hashtable that +// could be returned by EmptyGeneration(). +inline bool IsEmptyGeneration(const GenerationType* generation) { + return *generation == SentinelEmptyGeneration(); +} + +// We only allow a maximum of 1 SOO element, which makes the implementation +// much simpler. Complications with multiple SOO elements include: +// - Satisfying the guarantee that erasing one element doesn't invalidate +// iterators to other elements means we would probably need actual SOO +// control bytes. +// - In order to prevent user code from depending on iteration order for small +// tables, we would need to randomize the iteration order somehow. +constexpr size_t SooCapacity() { return 1; } +// Sentinel type to indicate SOO CommonFields construction. +struct soo_tag_t {}; +// Sentinel type to indicate SOO CommonFields construction with full size. +struct full_soo_tag_t {}; +// Sentinel type to indicate non-SOO CommonFields construction. +struct non_soo_tag_t {}; +// Sentinel value to indicate an uninitialized value explicitly. +struct uninitialized_tag_t {}; +// Sentinel value to indicate creation of an empty table without a seed. +struct no_seed_empty_tag_t {}; + +// Per table hash salt. This gets mixed into H1 to randomize iteration order +// per-table. +// The seed is needed to ensure non-determinism of iteration order. +class PerTableSeed { + public: + // The number of bits in the seed. + // It is big enough to ensure non-determinism of iteration order. + // We store the seed inside a uint64_t together with size and other metadata. + // Using 16 bits allows us to save one `and` instruction in H1 (we use movzwl + // instead of movq+and). + static constexpr size_t kBitCount = 16; + + // Returns the seed for the table. Only the lowest kBitCount are non zero. + size_t seed() const { return seed_; } + + private: + friend class HashtableSize; + explicit PerTableSeed(size_t seed) : seed_(seed) {} + + const size_t seed_; +}; + +// Returns next per-table seed. +inline uint16_t NextSeed() { + static_assert(PerTableSeed::kBitCount == 16); + thread_local uint16_t seed = + static_cast(reinterpret_cast(&seed)); + seed += uint16_t{0xad53}; + return seed; +} + +// The size and also has additionally +// 1) one bit that stores whether we have infoz. +// 2) PerTableSeed::kBitCount bits for the seed. +class HashtableSize { + public: + static constexpr size_t kSizeBitCount = 64 - PerTableSeed::kBitCount - 1; + + explicit HashtableSize(uninitialized_tag_t) {} + explicit HashtableSize(no_seed_empty_tag_t) : data_(0) {} + explicit HashtableSize(full_soo_tag_t) : data_(kSizeOneNoMetadata) {} + + // Returns actual size of the table. + size_t size() const { return static_cast(data_ >> kSizeShift); } + void increment_size() { data_ += kSizeOneNoMetadata; } + void increment_size(size_t size) { + data_ += static_cast(size) * kSizeOneNoMetadata; + } + void decrement_size() { data_ -= kSizeOneNoMetadata; } + // Returns true if the table is empty. + bool empty() const { return data_ < kSizeOneNoMetadata; } + // Sets the size to zero, but keeps all the metadata bits. + void set_size_to_zero_keep_metadata() { data_ = data_ & kMetadataMask; } + + PerTableSeed seed() const { + return PerTableSeed(static_cast(data_) & kSeedMask); + } + + void generate_new_seed() { + data_ = (data_ & ~kSeedMask) ^ uint64_t{NextSeed()}; + } + + // Returns true if the table has infoz. + bool has_infoz() const { + return ABSL_PREDICT_FALSE((data_ & kHasInfozMask) != 0); + } + + // Sets the has_infoz bit. + void set_has_infoz() { data_ |= kHasInfozMask; } + + private: + static constexpr size_t kSizeShift = 64 - kSizeBitCount; + static constexpr uint64_t kSizeOneNoMetadata = uint64_t{1} << kSizeShift; + static constexpr uint64_t kMetadataMask = kSizeOneNoMetadata - 1; + static constexpr uint64_t kSeedMask = + (uint64_t{1} << PerTableSeed::kBitCount) - 1; + // The next bit after the seed. + static constexpr uint64_t kHasInfozMask = kSeedMask + 1; + uint64_t data_; +}; + +// Extracts the H1 portion of a hash: 57 bits mixed with a per-table seed. +inline size_t H1(size_t hash, PerTableSeed seed) { + return (hash >> 7) ^ seed.seed(); +} + +// Extracts the H2 portion of a hash: the 7 bits not used for H1. +// +// These are used as an occupied control byte. +inline h2_t H2(size_t hash) { return hash & 0x7F; } + +// When there is an insertion with no reserved growth, we rehash with +// probability `min(1, RehashProbabilityConstant() / capacity())`. Using a +// constant divided by capacity ensures that inserting N elements is still O(N) +// in the average case. Using the constant 16 means that we expect to rehash ~8 +// times more often than when generations are disabled. We are adding expected +// rehash_probability * #insertions/capacity_growth = 16/capacity * ((7/8 - +// 7/16) * capacity)/capacity_growth = ~7 extra rehashes per capacity growth. +inline size_t RehashProbabilityConstant() { return 16; } + +class CommonFieldsGenerationInfoEnabled { + // A sentinel value for reserved_growth_ indicating that we just ran out of + // reserved growth on the last insertion. When reserve is called and then + // insertions take place, reserved_growth_'s state machine is N, ..., 1, + // kReservedGrowthJustRanOut, 0. + static constexpr size_t kReservedGrowthJustRanOut = + (std::numeric_limits::max)(); + + public: + CommonFieldsGenerationInfoEnabled() = default; + CommonFieldsGenerationInfoEnabled(CommonFieldsGenerationInfoEnabled&& that) + : reserved_growth_(that.reserved_growth_), + reservation_size_(that.reservation_size_), + generation_(that.generation_) { + that.reserved_growth_ = 0; + that.reservation_size_ = 0; + that.generation_ = EmptyGeneration(); + } + CommonFieldsGenerationInfoEnabled& operator=( + CommonFieldsGenerationInfoEnabled&&) = default; + + // Whether we should rehash on insert in order to detect bugs of using invalid + // references. We rehash on the first insertion after reserved_growth_ reaches + // 0 after a call to reserve. We also do a rehash with low probability + // whenever reserved_growth_ is zero. + bool should_rehash_for_bug_detection_on_insert(PerTableSeed seed, + size_t capacity) const; + // Similar to above, except that we don't depend on reserved_growth_. + bool should_rehash_for_bug_detection_on_move(PerTableSeed seed, + size_t capacity) const; + void maybe_increment_generation_on_insert() { + if (reserved_growth_ == kReservedGrowthJustRanOut) reserved_growth_ = 0; + + if (reserved_growth_ > 0) { + if (--reserved_growth_ == 0) reserved_growth_ = kReservedGrowthJustRanOut; + } else { + increment_generation(); + } + } + void increment_generation() { *generation_ = NextGeneration(*generation_); } + void reset_reserved_growth(size_t reservation, size_t size) { + reserved_growth_ = reservation - size; + } + size_t reserved_growth() const { return reserved_growth_; } + void set_reserved_growth(size_t r) { reserved_growth_ = r; } + size_t reservation_size() const { return reservation_size_; } + void set_reservation_size(size_t r) { reservation_size_ = r; } + GenerationType generation() const { return *generation_; } + void set_generation(GenerationType g) { *generation_ = g; } + GenerationType* generation_ptr() const { return generation_; } + void set_generation_ptr(GenerationType* g) { generation_ = g; } + + private: + // The number of insertions remaining that are guaranteed to not rehash due to + // a prior call to reserve. Note: we store reserved growth in addition to + // reservation size because calls to erase() decrease size_ but don't decrease + // reserved growth. + size_t reserved_growth_ = 0; + // The maximum argument to reserve() since the container was cleared. We need + // to keep track of this, in addition to reserved growth, because we reset + // reserved growth to this when erase(begin(), end()) is called. + size_t reservation_size_ = 0; + // Pointer to the generation counter, which is used to validate iterators and + // is stored in the backing array between the control bytes and the slots. + // Note that we can't store the generation inside the container itself and + // keep a pointer to the container in the iterators because iterators must + // remain valid when the container is moved. + // Note: we could derive this pointer from the control pointer, but it makes + // the code more complicated, and there's a benefit in having the sizes of + // raw_hash_set in sanitizer mode and non-sanitizer mode a bit more different, + // which is that tests are less likely to rely on the size remaining the same. + GenerationType* generation_ = EmptyGeneration(); +}; + +class CommonFieldsGenerationInfoDisabled { + public: + CommonFieldsGenerationInfoDisabled() = default; + CommonFieldsGenerationInfoDisabled(CommonFieldsGenerationInfoDisabled&&) = + default; + CommonFieldsGenerationInfoDisabled& operator=( + CommonFieldsGenerationInfoDisabled&&) = default; + + bool should_rehash_for_bug_detection_on_insert(PerTableSeed, size_t) const { + return false; + } + bool should_rehash_for_bug_detection_on_move(PerTableSeed, size_t) const { + return false; + } + void maybe_increment_generation_on_insert() {} + void increment_generation() {} + void reset_reserved_growth(size_t, size_t) {} + size_t reserved_growth() const { return 0; } + void set_reserved_growth(size_t) {} + size_t reservation_size() const { return 0; } + void set_reservation_size(size_t) {} + GenerationType generation() const { return 0; } + void set_generation(GenerationType) {} + GenerationType* generation_ptr() const { return nullptr; } + void set_generation_ptr(GenerationType*) {} +}; + +class HashSetIteratorGenerationInfoEnabled { + public: + HashSetIteratorGenerationInfoEnabled() = default; + explicit HashSetIteratorGenerationInfoEnabled( + const GenerationType* generation_ptr) + : generation_ptr_(generation_ptr), generation_(*generation_ptr) {} + + GenerationType generation() const { return generation_; } + void reset_generation() { generation_ = *generation_ptr_; } + const GenerationType* generation_ptr() const { return generation_ptr_; } + void set_generation_ptr(const GenerationType* ptr) { generation_ptr_ = ptr; } + + private: + const GenerationType* generation_ptr_ = EmptyGeneration(); + GenerationType generation_ = *generation_ptr_; +}; + +class HashSetIteratorGenerationInfoDisabled { + public: + HashSetIteratorGenerationInfoDisabled() = default; + explicit HashSetIteratorGenerationInfoDisabled(const GenerationType*) {} + + GenerationType generation() const { return 0; } + void reset_generation() {} + const GenerationType* generation_ptr() const { return nullptr; } + void set_generation_ptr(const GenerationType*) {} +}; + +#ifdef ABSL_SWISSTABLE_ENABLE_GENERATIONS +using CommonFieldsGenerationInfo = CommonFieldsGenerationInfoEnabled; +using HashSetIteratorGenerationInfo = HashSetIteratorGenerationInfoEnabled; +#else +using CommonFieldsGenerationInfo = CommonFieldsGenerationInfoDisabled; +using HashSetIteratorGenerationInfo = HashSetIteratorGenerationInfoDisabled; +#endif + +// Stored the information regarding number of slots we can still fill +// without needing to rehash. +// +// We want to ensure sufficient number of empty slots in the table in order +// to keep probe sequences relatively short. Empty slot in the probe group +// is required to stop probing. +// +// Tombstones (kDeleted slots) are not included in the growth capacity, +// because we'd like to rehash when the table is filled with tombstones and/or +// full slots. +// +// GrowthInfo also stores a bit that encodes whether table may have any +// deleted slots. +// Most of the tables (>95%) have no deleted slots, so some functions can +// be more efficient with this information. +// +// Callers can also force a rehash via the standard `rehash(0)`, +// which will recompute this value as a side-effect. +// +// See also `CapacityToGrowth()`. +class GrowthInfo { + public: + // Leaves data member uninitialized. + GrowthInfo() = default; + + // Initializes the GrowthInfo assuming we can grow `growth_left` elements + // and there are no kDeleted slots in the table. + void InitGrowthLeftNoDeleted(size_t growth_left) { + growth_left_info_ = growth_left; + } + + // Overwrites single full slot with an empty slot. + void OverwriteFullAsEmpty() { ++growth_left_info_; } + + // Overwrites single empty slot with a full slot. + void OverwriteEmptyAsFull() { + ABSL_SWISSTABLE_ASSERT(GetGrowthLeft() > 0); + --growth_left_info_; + } + + // Overwrites several empty slots with full slots. + void OverwriteManyEmptyAsFull(size_t count) { + ABSL_SWISSTABLE_ASSERT(GetGrowthLeft() >= count); + growth_left_info_ -= count; + } + + // Overwrites specified control element with full slot. + void OverwriteControlAsFull(ctrl_t ctrl) { + ABSL_SWISSTABLE_ASSERT(GetGrowthLeft() >= + static_cast(IsEmpty(ctrl))); + growth_left_info_ -= static_cast(IsEmpty(ctrl)); + } + + // Overwrites single full slot with a deleted slot. + void OverwriteFullAsDeleted() { growth_left_info_ |= kDeletedBit; } + + // Returns true if table satisfies two properties: + // 1. Guaranteed to have no kDeleted slots. + // 2. There is a place for at least one element to grow. + bool HasNoDeletedAndGrowthLeft() const { + return static_cast>(growth_left_info_) > 0; + } + + // Returns true if the table satisfies two properties: + // 1. Guaranteed to have no kDeleted slots. + // 2. There is no growth left. + bool HasNoGrowthLeftAndNoDeleted() const { return growth_left_info_ == 0; } + + // Returns true if GetGrowthLeft() == 0, but must be called only if + // HasNoDeleted() is false. It is slightly more efficient. + bool HasNoGrowthLeftAssumingMayHaveDeleted() const { + ABSL_SWISSTABLE_ASSERT(!HasNoDeleted()); + return growth_left_info_ == kDeletedBit; + } + + // Returns true if table guaranteed to have no kDeleted slots. + bool HasNoDeleted() const { + return static_cast>(growth_left_info_) >= 0; + } + + // Returns the number of elements left to grow. + size_t GetGrowthLeft() const { return growth_left_info_ & kGrowthLeftMask; } + + private: + static constexpr size_t kGrowthLeftMask = ((~size_t{}) >> 1); + static constexpr size_t kDeletedBit = ~kGrowthLeftMask; + // Topmost bit signal whenever there are deleted slots. + size_t growth_left_info_; +}; + +static_assert(sizeof(GrowthInfo) == sizeof(size_t), ""); +static_assert(alignof(GrowthInfo) == alignof(size_t), ""); + +// Returns whether `n` is a valid capacity (i.e., number of slots). +// +// A valid capacity is a non-zero integer `2^m - 1`. +constexpr bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; } + +// Returns the number of "cloned control bytes". +// +// This is the number of control bytes that are present both at the beginning +// of the control byte array and at the end, such that we can create a +// `Group::kWidth`-width probe window starting from any control byte. +constexpr size_t NumClonedBytes() { return Group::kWidth - 1; } + +// Returns the number of control bytes including cloned. +constexpr size_t NumControlBytes(size_t capacity) { + return capacity + 1 + NumClonedBytes(); +} + +// Computes the offset from the start of the backing allocation of control. +// infoz and growth_info are stored at the beginning of the backing array. +constexpr size_t ControlOffset(bool has_infoz) { + return (has_infoz ? sizeof(HashtablezInfoHandle) : 0) + sizeof(GrowthInfo); +} + +// Returns the offset of the next item after `offset` that is aligned to `align` +// bytes. `align` must be a power of two. +constexpr size_t AlignUpTo(size_t offset, size_t align) { + return (offset + align - 1) & (~align + 1); +} + +// Helper class for computing offsets and allocation size of hash set fields. +class RawHashSetLayout { + public: + explicit RawHashSetLayout(size_t capacity, size_t slot_size, + size_t slot_align, bool has_infoz) + : control_offset_(ControlOffset(has_infoz)), + generation_offset_(control_offset_ + NumControlBytes(capacity)), + slot_offset_( + AlignUpTo(generation_offset_ + NumGenerationBytes(), slot_align)), + alloc_size_(slot_offset_ + capacity * slot_size) { + ABSL_SWISSTABLE_ASSERT(IsValidCapacity(capacity)); + ABSL_SWISSTABLE_ASSERT( + slot_size <= + ((std::numeric_limits::max)() - slot_offset_) / capacity); + } + + // Returns precomputed offset from the start of the backing allocation of + // control. + size_t control_offset() const { return control_offset_; } + + // Given the capacity of a table, computes the offset (from the start of the + // backing allocation) of the generation counter (if it exists). + size_t generation_offset() const { return generation_offset_; } + + // Given the capacity of a table, computes the offset (from the start of the + // backing allocation) at which the slots begin. + size_t slot_offset() const { return slot_offset_; } + + // Given the capacity of a table, computes the total size of the backing + // array. + size_t alloc_size() const { return alloc_size_; } + + private: + size_t control_offset_; + size_t generation_offset_; + size_t slot_offset_; + size_t alloc_size_; +}; + +struct HashtableFreeFunctionsAccess; + +// Suppress erroneous uninitialized memory errors on GCC. For example, GCC +// thinks that the call to slot_array() in find_or_prepare_insert() is reading +// uninitialized memory, but slot_array is only called there when the table is +// non-empty and this memory is initialized when the table is non-empty. +#if !defined(__clang__) && defined(__GNUC__) +#define ABSL_SWISSTABLE_IGNORE_UNINITIALIZED(x) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") \ + _Pragma("GCC diagnostic ignored \"-Wuninitialized\"") x; \ + _Pragma("GCC diagnostic pop") +#define ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(x) \ + ABSL_SWISSTABLE_IGNORE_UNINITIALIZED(return x) +#else +#define ABSL_SWISSTABLE_IGNORE_UNINITIALIZED(x) x +#define ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(x) return x +#endif + +// This allows us to work around an uninitialized memory warning when +// constructing begin() iterators in empty hashtables. +union MaybeInitializedPtr { + void* get() const { ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(p); } + void set(void* ptr) { p = ptr; } + + void* p; +}; + +struct HeapPtrs { + explicit HeapPtrs(uninitialized_tag_t) {} + explicit HeapPtrs(ctrl_t* c) : control(c) {} + + // The control bytes (and, also, a pointer near to the base of the backing + // array). + // + // This contains `capacity + 1 + NumClonedBytes()` entries, even + // when the table is empty (hence EmptyGroup). + // + // Note that growth_info is stored immediately before this pointer. + // May be uninitialized for SOO tables. + ctrl_t* control; + + // The beginning of the slots, located at `SlotOffset()` bytes after + // `control`. May be uninitialized for empty tables. + // Note: we can't use `slots` because Qt defines "slots" as a macro. + MaybeInitializedPtr slot_array; +}; + +// Returns the maximum size of the SOO slot. +constexpr size_t MaxSooSlotSize() { return sizeof(HeapPtrs); } + +// Manages the backing array pointers or the SOO slot. When raw_hash_set::is_soo +// is true, the SOO slot is stored in `soo_data`. Otherwise, we use `heap`. +union HeapOrSoo { + explicit HeapOrSoo(uninitialized_tag_t) : heap(uninitialized_tag_t{}) {} + explicit HeapOrSoo(ctrl_t* c) : heap(c) {} + + ctrl_t*& control() { + ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(heap.control); + } + ctrl_t* control() const { + ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(heap.control); + } + MaybeInitializedPtr& slot_array() { + ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(heap.slot_array); + } + MaybeInitializedPtr slot_array() const { + ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(heap.slot_array); + } + void* get_soo_data() { + ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(soo_data); + } + const void* get_soo_data() const { + ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(soo_data); + } + + HeapPtrs heap; + unsigned char soo_data[MaxSooSlotSize()]; +}; + +// Returns a reference to the GrowthInfo object stored immediately before +// `control`. +inline GrowthInfo& GetGrowthInfoFromControl(ctrl_t* control) { + auto* gl_ptr = reinterpret_cast(control) - 1; + ABSL_SWISSTABLE_ASSERT( + reinterpret_cast(gl_ptr) % alignof(GrowthInfo) == 0); + return *gl_ptr; +} + +// CommonFields hold the fields in raw_hash_set that do not depend +// on template parameters. This allows us to conveniently pass all +// of this state to helper functions as a single argument. +class CommonFields : public CommonFieldsGenerationInfo { + public: + explicit CommonFields(soo_tag_t) + : capacity_(SooCapacity()), + size_(no_seed_empty_tag_t{}), + heap_or_soo_(uninitialized_tag_t{}) {} + explicit CommonFields(full_soo_tag_t) + : capacity_(SooCapacity()), + size_(full_soo_tag_t{}), + heap_or_soo_(uninitialized_tag_t{}) {} + explicit CommonFields(non_soo_tag_t) + : capacity_(0), + size_(no_seed_empty_tag_t{}), + heap_or_soo_(EmptyGroup()) {} + // For use in swapping. + explicit CommonFields(uninitialized_tag_t) + : size_(uninitialized_tag_t{}), heap_or_soo_(uninitialized_tag_t{}) {} + + // Not copyable + CommonFields(const CommonFields&) = delete; + CommonFields& operator=(const CommonFields&) = delete; + + // Copy with guarantee that it is not SOO. + CommonFields(non_soo_tag_t, const CommonFields& that) + : capacity_(that.capacity_), + size_(that.size_), + heap_or_soo_(that.heap_or_soo_) { + } + + // Movable + CommonFields(CommonFields&& that) = default; + CommonFields& operator=(CommonFields&&) = default; + + template + static CommonFields CreateDefault() { + return kSooEnabled ? CommonFields{soo_tag_t{}} + : CommonFields{non_soo_tag_t{}}; + } + + // The inline data for SOO is written on top of control_/slots_. + const void* soo_data() const { return heap_or_soo_.get_soo_data(); } + void* soo_data() { return heap_or_soo_.get_soo_data(); } + + ctrl_t* control() const { return heap_or_soo_.control(); } + + // When we set the control bytes, we also often want to generate a new seed. + // So we bundle these two operations together to make sure we don't forget to + // generate a new seed. + // The table will be invalidated if + // `kGenerateSeed && !empty() && !is_single_group(capacity())` because H1 is + // being changed. In such cases, we will need to rehash the table. + template + void set_control(ctrl_t* c) { + heap_or_soo_.control() = c; + if constexpr (kGenerateSeed) { + generate_new_seed(); + } + } + void* backing_array_start() const { + // growth_info (and maybe infoz) is stored before control bytes. + ABSL_SWISSTABLE_ASSERT( + reinterpret_cast(control()) % alignof(size_t) == 0); + return control() - ControlOffset(has_infoz()); + } + + // Note: we can't use slots() because Qt defines "slots" as a macro. + void* slot_array() const { return heap_or_soo_.slot_array().get(); } + MaybeInitializedPtr slots_union() const { return heap_or_soo_.slot_array(); } + void set_slots(void* s) { heap_or_soo_.slot_array().set(s); } + + // The number of filled slots. + size_t size() const { return size_.size(); } + // Sets the size to zero, but keeps hashinfoz bit and seed. + void set_size_to_zero() { size_.set_size_to_zero_keep_metadata(); } + void set_empty_soo() { + AssertInSooMode(); + size_ = HashtableSize(no_seed_empty_tag_t{}); + } + void set_full_soo() { + AssertInSooMode(); + size_ = HashtableSize(full_soo_tag_t{}); + } + void increment_size() { + ABSL_SWISSTABLE_ASSERT(size() < capacity()); + size_.increment_size(); + } + void increment_size(size_t n) { + ABSL_SWISSTABLE_ASSERT(size() + n <= capacity()); + size_.increment_size(n); + } + void decrement_size() { + ABSL_SWISSTABLE_ASSERT(!empty()); + size_.decrement_size(); + } + bool empty() const { return size_.empty(); } + + // The seed used for the H1 part of the hash function. + PerTableSeed seed() const { return size_.seed(); } + // Generates a new seed for the H1 part of the hash function. + // The table will be invalidated if + // `kGenerateSeed && !empty() && !is_single_group(capacity())` because H1 is + // being changed. In such cases, we will need to rehash the table. + void generate_new_seed() { size_.generate_new_seed(); } + + // The total number of available slots. + size_t capacity() const { return capacity_; } + void set_capacity(size_t c) { + // We allow setting above the max valid capacity for debugging purposes. + ABSL_SWISSTABLE_ASSERT(c == 0 || IsValidCapacity(c) || + c > kAboveMaxValidCapacity); + capacity_ = c; + } + + // The number of slots we can still fill without needing to rehash. + // This is stored in the heap allocation before the control bytes. + // TODO(b/289225379): experiment with moving growth_info back inline to + // increase room for SOO. + size_t growth_left() const { return growth_info().GetGrowthLeft(); } + + GrowthInfo& growth_info() { + return GetGrowthInfoFromControl(control()); + } + GrowthInfo growth_info() const { + return const_cast(this)->growth_info(); + } + + bool has_infoz() const { return size_.has_infoz(); } + void set_has_infoz() { size_.set_has_infoz(); } + + HashtablezInfoHandle infoz() { + return has_infoz() + ? *reinterpret_cast(backing_array_start()) + : HashtablezInfoHandle(); + } + void set_infoz(HashtablezInfoHandle infoz) { + ABSL_SWISSTABLE_ASSERT(has_infoz()); + *reinterpret_cast(backing_array_start()) = infoz; + } + + bool should_rehash_for_bug_detection_on_insert() const { + if constexpr (!SwisstableGenerationsEnabled()) { + return false; + } + // As an optimization, we avoid calling ShouldRehashForBugDetection if we + // will end up rehashing anyways. + if (growth_left() == 0) return false; + return CommonFieldsGenerationInfo:: + should_rehash_for_bug_detection_on_insert(seed(), capacity()); + } + bool should_rehash_for_bug_detection_on_move() const { + return CommonFieldsGenerationInfo::should_rehash_for_bug_detection_on_move( + seed(), capacity()); + } + void reset_reserved_growth(size_t reservation) { + CommonFieldsGenerationInfo::reset_reserved_growth(reservation, size()); + } + + // The size of the backing array allocation. + size_t alloc_size(size_t slot_size, size_t slot_align) const { + return RawHashSetLayout(capacity(), slot_size, slot_align, has_infoz()) + .alloc_size(); + } + + // Move fields other than heap_or_soo_. + void move_non_heap_or_soo_fields(CommonFields& that) { + static_cast(*this) = + std::move(static_cast(that)); + capacity_ = that.capacity_; + size_ = that.size_; + } + + // Returns the number of control bytes set to kDeleted. For testing only. + size_t TombstonesCount() const { + return static_cast( + std::count(control(), control() + capacity(), ctrl_t::kDeleted)); + } + + // Helper to enable sanitizer mode validation to protect against reentrant + // calls during element constructor/destructor. + template + void RunWithReentrancyGuard(F f) { +#ifdef NDEBUG + f(); + return; +#endif + const size_t cap = capacity(); + set_capacity(InvalidCapacity::kReentrance); + f(); + set_capacity(cap); + } + + private: + // We store the has_infoz bit in the lowest bit of size_. + static constexpr size_t HasInfozShift() { return 1; } + static constexpr size_t HasInfozMask() { + return (size_t{1} << HasInfozShift()) - 1; + } + + // We can't assert that SOO is enabled because we don't have SooEnabled(), but + // we assert what we can. + void AssertInSooMode() const { + ABSL_SWISSTABLE_ASSERT(capacity() == SooCapacity()); + ABSL_SWISSTABLE_ASSERT(!has_infoz()); + } + + // The number of slots in the backing array. This is always 2^N-1 for an + // integer N. NOTE: we tried experimenting with compressing the capacity and + // storing it together with size_: (a) using 6 bits to store the corresponding + // power (N in 2^N-1), and (b) storing 2^N as the most significant bit of + // size_ and storing size in the low bits. Both of these experiments were + // regressions, presumably because we need capacity to do find operations. + size_t capacity_; + + // TODO(b/289225379): we could put size_ into HeapOrSoo and make capacity_ + // encode the size in SOO case. We would be making size()/capacity() more + // expensive in order to have more SOO space. + HashtableSize size_; + + // Either the control/slots pointers or the SOO slot. + HeapOrSoo heap_or_soo_; +}; + +template +class raw_hash_set; + +// Returns the next valid capacity after `n`. +constexpr size_t NextCapacity(size_t n) { + ABSL_SWISSTABLE_ASSERT(IsValidCapacity(n) || n == 0); + return n * 2 + 1; +} + +// Returns the previous valid capacity before `n`. +constexpr size_t PreviousCapacity(size_t n) { + ABSL_SWISSTABLE_ASSERT(IsValidCapacity(n)); + return n / 2; +} + +// Applies the following mapping to every byte in the control array: +// * kDeleted -> kEmpty +// * kEmpty -> kEmpty +// * _ -> kDeleted +// PRECONDITION: +// IsValidCapacity(capacity) +// ctrl[capacity] == ctrl_t::kSentinel +// ctrl[i] != ctrl_t::kSentinel for all i < capacity +void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity); + +// Converts `n` into the next valid capacity, per `IsValidCapacity`. +constexpr size_t NormalizeCapacity(size_t n) { + return n ? ~size_t{} >> countl_zero(n) : 1; +} + +// General notes on capacity/growth methods below: +// - We use 7/8th as maximum load factor. For 16-wide groups, that gives an +// average of two empty slots per group. +// - For (capacity+1) >= Group::kWidth, growth is 7/8*capacity. +// - For (capacity+1) < Group::kWidth, growth == capacity. In this case, we +// never need to probe (the whole table fits in one group) so we don't need a +// load factor less than 1. + +// Given `capacity`, applies the load factor; i.e., it returns the maximum +// number of values we should put into the table before a resizing rehash. +constexpr size_t CapacityToGrowth(size_t capacity) { + ABSL_SWISSTABLE_ASSERT(IsValidCapacity(capacity)); + // `capacity*7/8` + if (Group::kWidth == 8 && capacity == 7) { + // x-x/8 does not work when x==7. + return 6; + } + return capacity - capacity / 8; +} + +// Given `size`, "unapplies" the load factor to find how large the capacity +// should be to stay within the load factor. +// +// For size == 0, returns 0. +// For other values, returns the same as `NormalizeCapacity(size*8/7)`. +constexpr size_t SizeToCapacity(size_t size) { + if (size == 0) { + return 0; + } + // The minimum possible capacity is NormalizeCapacity(size). + // Shifting right `~size_t{}` by `leading_zeros` yields + // NormalizeCapacity(size). + int leading_zeros = absl::countl_zero(size); + constexpr size_t kLast3Bits = size_t{7} << (sizeof(size_t) * 8 - 3); + size_t max_size_for_next_capacity = kLast3Bits >> leading_zeros; + // Decrease shift if size is too big for the minimum capacity. + leading_zeros -= static_cast(size > max_size_for_next_capacity); + if constexpr (Group::kWidth == 8) { + // Formula doesn't work when size==7 for 8-wide groups. + leading_zeros -= (size == 7); + } + return (~size_t{}) >> leading_zeros; +} + +template +size_t SelectBucketCountForIterRange(InputIter first, InputIter last, + size_t bucket_count) { + if (bucket_count != 0) { + return bucket_count; + } + if (base_internal::IsAtLeastIterator()) { + return SizeToCapacity(static_cast(std::distance(first, last))); + } + return 0; +} + +constexpr bool SwisstableDebugEnabled() { +#if defined(ABSL_SWISSTABLE_ENABLE_GENERATIONS) || \ + ABSL_OPTION_HARDENED == 1 || !defined(NDEBUG) + return true; +#else + return false; +#endif +} + +inline void AssertIsFull(const ctrl_t* ctrl, GenerationType generation, + const GenerationType* generation_ptr, + const char* operation) { + if (!SwisstableDebugEnabled()) return; + // `SwisstableDebugEnabled()` is also true for release builds with hardening + // enabled. To minimize their impact in those builds: + // - use `ABSL_PREDICT_FALSE()` to provide a compiler hint for code layout + // - use `ABSL_RAW_LOG()` with a format string to reduce code size and improve + // the chances that the hot paths will be inlined. + if (ABSL_PREDICT_FALSE(ctrl == nullptr)) { + ABSL_RAW_LOG(FATAL, "%s called on end() iterator.", operation); + } + if (ABSL_PREDICT_FALSE(ctrl == EmptyGroup())) { + ABSL_RAW_LOG(FATAL, "%s called on default-constructed iterator.", + operation); + } + if (SwisstableGenerationsEnabled()) { + if (ABSL_PREDICT_FALSE(generation != *generation_ptr)) { + ABSL_RAW_LOG(FATAL, + "%s called on invalid iterator. The table could have " + "rehashed or moved since this iterator was initialized.", + operation); + } + if (ABSL_PREDICT_FALSE(!IsFull(*ctrl))) { + ABSL_RAW_LOG( + FATAL, + "%s called on invalid iterator. The element was likely erased.", + operation); + } + } else { + if (ABSL_PREDICT_FALSE(!IsFull(*ctrl))) { + ABSL_RAW_LOG( + FATAL, + "%s called on invalid iterator. The element might have been erased " + "or the table might have rehashed. Consider running with " + "--config=asan to diagnose rehashing issues.", + operation); + } + } +} + +// Note that for comparisons, null/end iterators are valid. +inline void AssertIsValidForComparison(const ctrl_t* ctrl, + GenerationType generation, + const GenerationType* generation_ptr) { + if (!SwisstableDebugEnabled()) return; + const bool ctrl_is_valid_for_comparison = + ctrl == nullptr || ctrl == EmptyGroup() || IsFull(*ctrl); + if (SwisstableGenerationsEnabled()) { + if (ABSL_PREDICT_FALSE(generation != *generation_ptr)) { + ABSL_RAW_LOG(FATAL, + "Invalid iterator comparison. The table could have rehashed " + "or moved since this iterator was initialized."); + } + if (ABSL_PREDICT_FALSE(!ctrl_is_valid_for_comparison)) { + ABSL_RAW_LOG( + FATAL, "Invalid iterator comparison. The element was likely erased."); + } + } else { + ABSL_HARDENING_ASSERT_SLOW( + ctrl_is_valid_for_comparison && + "Invalid iterator comparison. The element might have been erased or " + "the table might have rehashed. Consider running with --config=asan to " + "diagnose rehashing issues."); + } +} + +// If the two iterators come from the same container, then their pointers will +// interleave such that ctrl_a <= ctrl_b < slot_a <= slot_b or vice/versa. +// Note: we take slots by reference so that it's not UB if they're uninitialized +// as long as we don't read them (when ctrl is null). +inline bool AreItersFromSameContainer(const ctrl_t* ctrl_a, + const ctrl_t* ctrl_b, + const void* const& slot_a, + const void* const& slot_b) { + // If either control byte is null, then we can't tell. + if (ctrl_a == nullptr || ctrl_b == nullptr) return true; + const bool a_is_soo = IsSooControl(ctrl_a); + if (a_is_soo != IsSooControl(ctrl_b)) return false; + if (a_is_soo) return slot_a == slot_b; + + const void* low_slot = slot_a; + const void* hi_slot = slot_b; + if (ctrl_a > ctrl_b) { + std::swap(ctrl_a, ctrl_b); + std::swap(low_slot, hi_slot); + } + return ctrl_b < low_slot && low_slot <= hi_slot; +} + +// Asserts that two iterators come from the same container. +// Note: we take slots by reference so that it's not UB if they're uninitialized +// as long as we don't read them (when ctrl is null). +inline void AssertSameContainer(const ctrl_t* ctrl_a, const ctrl_t* ctrl_b, + const void* const& slot_a, + const void* const& slot_b, + const GenerationType* generation_ptr_a, + const GenerationType* generation_ptr_b) { + if (!SwisstableDebugEnabled()) return; + // `SwisstableDebugEnabled()` is also true for release builds with hardening + // enabled. To minimize their impact in those builds: + // - use `ABSL_PREDICT_FALSE()` to provide a compiler hint for code layout + // - use `ABSL_RAW_LOG()` with a format string to reduce code size and improve + // the chances that the hot paths will be inlined. + + // fail_if(is_invalid, message) crashes when is_invalid is true and provides + // an error message based on `message`. + const auto fail_if = [](bool is_invalid, const char* message) { + if (ABSL_PREDICT_FALSE(is_invalid)) { + ABSL_RAW_LOG(FATAL, "Invalid iterator comparison. %s", message); + } + }; + + const bool a_is_default = ctrl_a == EmptyGroup(); + const bool b_is_default = ctrl_b == EmptyGroup(); + if (a_is_default && b_is_default) return; + fail_if(a_is_default != b_is_default, + "Comparing default-constructed hashtable iterator with a " + "non-default-constructed hashtable iterator."); + + if (SwisstableGenerationsEnabled()) { + if (ABSL_PREDICT_TRUE(generation_ptr_a == generation_ptr_b)) return; + // Users don't need to know whether the tables are SOO so don't mention SOO + // in the debug message. + const bool a_is_soo = IsSooControl(ctrl_a); + const bool b_is_soo = IsSooControl(ctrl_b); + fail_if(a_is_soo != b_is_soo || (a_is_soo && b_is_soo), + "Comparing iterators from different hashtables."); + + const bool a_is_empty = IsEmptyGeneration(generation_ptr_a); + const bool b_is_empty = IsEmptyGeneration(generation_ptr_b); + fail_if(a_is_empty != b_is_empty, + "Comparing an iterator from an empty hashtable with an iterator " + "from a non-empty hashtable."); + fail_if(a_is_empty && b_is_empty, + "Comparing iterators from different empty hashtables."); + + const bool a_is_end = ctrl_a == nullptr; + const bool b_is_end = ctrl_b == nullptr; + fail_if(a_is_end || b_is_end, + "Comparing iterator with an end() iterator from a different " + "hashtable."); + fail_if(true, "Comparing non-end() iterators from different hashtables."); + } else { + ABSL_HARDENING_ASSERT_SLOW( + AreItersFromSameContainer(ctrl_a, ctrl_b, slot_a, slot_b) && + "Invalid iterator comparison. The iterators may be from different " + "containers or the container might have rehashed or moved. Consider " + "running with --config=asan to diagnose issues."); + } +} + +struct FindInfo { + size_t offset; + size_t probe_length; +}; + +// Whether a table fits entirely into a probing group. +// Arbitrary order of elements in such tables is correct. +constexpr bool is_single_group(size_t capacity) { + return capacity <= Group::kWidth; +} + +// Begins a probing operation on `common.control`, using `hash`. +inline probe_seq probe(size_t h1, size_t capacity) { + return probe_seq(h1, capacity); +} +inline probe_seq probe(PerTableSeed seed, size_t capacity, + size_t hash) { + return probe(H1(hash, seed), capacity); +} +inline probe_seq probe(const CommonFields& common, size_t hash) { + return probe(common.seed(), common.capacity(), hash); +} + +// Probes an array of control bits using a probe sequence derived from `hash`, +// and returns the offset corresponding to the first deleted or empty slot. +// +// Behavior when the entire table is full is undefined. +// +// NOTE: this function must work with tables having both empty and deleted +// slots in the same group. Such tables appear during `erase()`. +FindInfo find_first_non_full(const CommonFields& common, size_t hash); + +constexpr size_t kProbedElementIndexSentinel = ~size_t{}; + +// Implementation detail of transfer_unprobed_elements_to_next_capacity_fn. +// Tries to find the new index for an element whose hash corresponds to +// `h1` for growth to the next capacity. +// Returns kProbedElementIndexSentinel if full probing is required. +// +// If element is located in the first probing group in the table before growth, +// returns one of two positions: `old_index` or `old_index + old_capacity + 1`. +// +// Otherwise, we will try to insert it into the first probe group of the new +// table. We only attempt to do so if the first probe group is already +// initialized. +template +inline size_t TryFindNewIndexWithoutProbing(size_t h1, size_t old_index, + size_t old_capacity, + ctrl_t* new_ctrl, + size_t new_capacity) { + size_t index_diff = old_index - h1; + // The first probe group starts with h1 & capacity. + // All following groups start at (h1 + Group::kWidth * K) & capacity. + // We can find an index within the floating group as index_diff modulo + // Group::kWidth. + // Both old and new capacity are larger than Group::kWidth so we can avoid + // computing `& capacity`. + size_t in_floating_group_index = index_diff & (Group::kWidth - 1); + // By subtracting we will get the difference between the first probe group + // and the probe group corresponding to old_index. + index_diff -= in_floating_group_index; + if (ABSL_PREDICT_TRUE((index_diff & old_capacity) == 0)) { + size_t new_index = (h1 + in_floating_group_index) & new_capacity; + ABSL_ASSUME(new_index != kProbedElementIndexSentinel); + return new_index; + } + ABSL_SWISSTABLE_ASSERT(((old_index - h1) & old_capacity) >= Group::kWidth); + // Try to insert element into the first probe group. + // new_ctrl is not yet fully initialized so we can't use regular search via + // find_first_non_full. + + // We can search in the first probe group only if it is located in already + // initialized part of the table. + if (ABSL_PREDICT_FALSE((h1 & old_capacity) >= old_index)) { + return kProbedElementIndexSentinel; + } + size_t offset = h1 & new_capacity; + Group new_g(new_ctrl + offset); + if (auto mask = new_g.MaskNonFull(); ABSL_PREDICT_TRUE(mask)) { + size_t result = offset + mask.LowestBitSet(); + ABSL_ASSUME(result != kProbedElementIndexSentinel); + return result; + } + return kProbedElementIndexSentinel; +} + +// Extern template for inline function keeps possibility of inlining. +// When compiler decided to not inline, no symbols will be added to the +// corresponding translation unit. +extern template size_t TryFindNewIndexWithoutProbing(size_t h1, + size_t old_index, + size_t old_capacity, + ctrl_t* new_ctrl, + size_t new_capacity); + +// Sets sanitizer poisoning for slot corresponding to control byte being set. +inline void DoSanitizeOnSetCtrl(const CommonFields& c, size_t i, ctrl_t h, + size_t slot_size) { + ABSL_SWISSTABLE_ASSERT(i < c.capacity()); + auto* slot_i = static_cast(c.slot_array()) + i * slot_size; + if (IsFull(h)) { + SanitizerUnpoisonMemoryRegion(slot_i, slot_size); + } else { + SanitizerPoisonMemoryRegion(slot_i, slot_size); + } +} + +// Sets `ctrl[i]` to `h`. +// +// Unlike setting it directly, this function will perform bounds checks and +// mirror the value to the cloned tail if necessary. +inline void SetCtrl(const CommonFields& c, size_t i, ctrl_t h, + size_t slot_size) { + DoSanitizeOnSetCtrl(c, i, h, slot_size); + ctrl_t* ctrl = c.control(); + ctrl[i] = h; + ctrl[((i - NumClonedBytes()) & c.capacity()) + + (NumClonedBytes() & c.capacity())] = h; +} +// Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`. +inline void SetCtrl(const CommonFields& c, size_t i, h2_t h, size_t slot_size) { + SetCtrl(c, i, static_cast(h), slot_size); +} + +// Like SetCtrl, but in a single group table, we can save some operations when +// setting the cloned control byte. +inline void SetCtrlInSingleGroupTable(const CommonFields& c, size_t i, ctrl_t h, + size_t slot_size) { + ABSL_SWISSTABLE_ASSERT(is_single_group(c.capacity())); + DoSanitizeOnSetCtrl(c, i, h, slot_size); + ctrl_t* ctrl = c.control(); + ctrl[i] = h; + ctrl[i + c.capacity() + 1] = h; +} +// Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`. +inline void SetCtrlInSingleGroupTable(const CommonFields& c, size_t i, h2_t h, + size_t slot_size) { + SetCtrlInSingleGroupTable(c, i, static_cast(h), slot_size); +} + +// Like SetCtrl, but in a table with capacity >= Group::kWidth - 1, +// we can save some operations when setting the cloned control byte. +inline void SetCtrlInLargeTable(const CommonFields& c, size_t i, ctrl_t h, + size_t slot_size) { + ABSL_SWISSTABLE_ASSERT(c.capacity() >= Group::kWidth - 1); + DoSanitizeOnSetCtrl(c, i, h, slot_size); + ctrl_t* ctrl = c.control(); + ctrl[i] = h; + ctrl[((i - NumClonedBytes()) & c.capacity()) + NumClonedBytes()] = h; +} +// Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`. +inline void SetCtrlInLargeTable(const CommonFields& c, size_t i, h2_t h, + size_t slot_size) { + SetCtrlInLargeTable(c, i, static_cast(h), slot_size); +} + +// growth_info (which is a size_t) is stored with the backing array. +constexpr size_t BackingArrayAlignment(size_t align_of_slot) { + return (std::max)(align_of_slot, alignof(GrowthInfo)); +} + +// Returns the address of the ith slot in slots where each slot occupies +// slot_size. +inline void* SlotAddress(void* slot_array, size_t slot, size_t slot_size) { + return static_cast(static_cast(slot_array) + + (slot * slot_size)); +} + +// Iterates over all full slots and calls `cb(const ctrl_t*, void*)`. +// No insertion to the table is allowed during `cb` call. +// Erasure is allowed only for the element passed to the callback. +// The table must not be in SOO mode. +void IterateOverFullSlots(const CommonFields& c, size_t slot_size, + absl::FunctionRef cb); + +template +constexpr bool ShouldSampleHashtablezInfoForAlloc() { + // Folks with custom allocators often make unwarranted assumptions about the + // behavior of their classes vis-a-vis trivial destructability and what + // calls they will or won't make. Avoid sampling for people with custom + // allocators to get us out of this mess. This is not a hard guarantee but + // a workaround while we plan the exact guarantee we want to provide. + return std::is_same_v>; +} + +template +bool ShouldSampleHashtablezInfoOnResize(bool force_sampling, + bool is_hashtablez_eligible, + size_t old_capacity, CommonFields& c) { + if (!is_hashtablez_eligible) return false; + // Force sampling is only allowed for SOO tables. + ABSL_SWISSTABLE_ASSERT(kSooEnabled || !force_sampling); + if (kSooEnabled && force_sampling) { + return true; + } + // In SOO, we sample on the first insertion so if this is an empty SOO case + // (e.g. when reserve is called), then we still need to sample. + if (kSooEnabled && old_capacity == SooCapacity() && c.empty()) { + return ShouldSampleNextTable(); + } + if (!kSooEnabled && old_capacity == 0) { + return ShouldSampleNextTable(); + } + return false; +} + +// Allocates `n` bytes for a backing array. +template +ABSL_ATTRIBUTE_NOINLINE void* AllocateBackingArray(void* alloc, size_t n) { + return Allocate(static_cast(alloc), n); +} + +template +ABSL_ATTRIBUTE_NOINLINE void DeallocateBackingArray( + void* alloc, size_t capacity, ctrl_t* ctrl, size_t slot_size, + size_t slot_align, bool had_infoz) { + RawHashSetLayout layout(capacity, slot_size, slot_align, had_infoz); + void* backing_array = ctrl - layout.control_offset(); + // Unpoison before returning the memory to the allocator. + SanitizerUnpoisonMemoryRegion(backing_array, layout.alloc_size()); + Deallocate(static_cast(alloc), backing_array, + layout.alloc_size()); +} + +// PolicyFunctions bundles together some information for a particular +// raw_hash_set instantiation. This information is passed to +// type-erased functions that want to do small amounts of type-specific +// work. +struct PolicyFunctions { + uint32_t key_size; + uint32_t value_size; + uint32_t slot_size; + uint16_t slot_align; + uint8_t soo_capacity; + bool is_hashtablez_eligible; + + // Returns the pointer to the hash function stored in the set. + void* (*hash_fn)(CommonFields& common); + + // Returns the hash of the pointed-to slot. + size_t (*hash_slot)(const void* hash_fn, void* slot); + + // Transfers the contents of `count` slots from src_slot to dst_slot. + // We use ability to transfer several slots in single group table growth. + void (*transfer_n)(void* set, void* dst_slot, void* src_slot, size_t count); + + // Returns the pointer to the CharAlloc stored in the set. + void* (*get_char_alloc)(CommonFields& common); + + // Allocates n bytes for the backing store for common. + void* (*alloc)(void* alloc, size_t n); + + // Deallocates the backing store from common. + void (*dealloc)(void* alloc, size_t capacity, ctrl_t* ctrl, size_t slot_size, + size_t slot_align, bool had_infoz); + + // Implementation detail of GrowToNextCapacity. + // Iterates over all full slots and transfers unprobed elements. + // Initializes the new control bytes except mirrored bytes and kSentinel. + // Caller must finish the initialization. + // All slots corresponding to the full control bytes are transferred. + // Probed elements are reported by `encode_probed_element` callback. + // encode_probed_element may overwrite old_ctrl buffer till source_offset. + // Different encoding is used depending on the capacity of the table. + // See ProbedItem*Bytes classes for details. + void (*transfer_unprobed_elements_to_next_capacity)( + CommonFields& common, const ctrl_t* old_ctrl, void* old_slots, + // TODO(b/382423690): Try to use absl::FunctionRef here. + void* probed_storage, + void (*encode_probed_element)(void* probed_storage, h2_t h2, + size_t source_offset, size_t h1)); +}; + +// Returns the maximum valid size for a table with 1-byte slots. +// This function is an utility shared by MaxValidSize and IsAboveValidSize. +// Template parameter is only used to enable testing. +template +constexpr size_t MaxValidSizeFor1ByteSlot() { + if constexpr (kSizeOfSizeT == 8) { + return CapacityToGrowth( + static_cast(uint64_t{1} << HashtableSize::kSizeBitCount) - 1); + } else { + static_assert(kSizeOfSizeT == 4); + return CapacityToGrowth((size_t{1} << (kSizeOfSizeT * 8 - 2)) - 1); + } +} + +// Returns the maximum valid size for a table with provided slot size. +// Template parameter is only used to enable testing. +template +constexpr size_t MaxValidSize(size_t slot_size) { + if constexpr (kSizeOfSizeT == 8) { + // For small slot sizes we are limited by HashtableSize::kSizeBitCount. + if (slot_size < size_t{1} << (64 - HashtableSize::kSizeBitCount)) { + return MaxValidSizeFor1ByteSlot(); + } + return (size_t{1} << (kSizeOfSizeT * 8 - 2)) / slot_size; + } else { + return MaxValidSizeFor1ByteSlot() / slot_size; + } +} + +// Returns true if size is larger than the maximum valid size. +// It is an optimization to avoid the division operation in the common case. +// Template parameter is only used to enable testing. +template +constexpr bool IsAboveValidSize(size_t size, size_t slot_size) { + if constexpr (kSizeOfSizeT == 8) { + // For small slot sizes we are limited by HashtableSize::kSizeBitCount. + if (ABSL_PREDICT_TRUE(slot_size < + (size_t{1} << (64 - HashtableSize::kSizeBitCount)))) { + return size > MaxValidSizeFor1ByteSlot(); + } + return size > MaxValidSize(slot_size); + } else { + return uint64_t{size} * slot_size > + MaxValidSizeFor1ByteSlot(); + } +} + +// Returns the index of the SOO slot when growing from SOO to non-SOO in a +// single group. See also InitializeSmallControlBytesAfterSoo(). It's important +// to use index 1 so that when resizing from capacity 1 to 3, we can still have +// random iteration order between the first two inserted elements. +// I.e. it allows inserting the second element at either index 0 or 2. +constexpr size_t SooSlotIndex() { return 1; } + +// Maximum capacity for the algorithm for small table after SOO. +// Note that typical size after SOO is 3, but we allow up to 7. +// Allowing till 16 would require additional store that can be avoided. +constexpr size_t MaxSmallAfterSooCapacity() { return 7; } + +// Type erased version of raw_hash_set::reserve. +// Requires: `new_size > policy.soo_capacity`. +void ReserveTableToFitNewSize(CommonFields& common, + const PolicyFunctions& policy, size_t new_size); + +// Resizes empty non-allocated table to the next valid capacity after +// `bucket_count`. Requires: +// 1. `c.capacity() == policy.soo_capacity`. +// 2. `c.empty()`. +// 3. `new_size > policy.soo_capacity`. +// The table will be attempted to be sampled. +void ReserveEmptyNonAllocatedTableToFitBucketCount( + CommonFields& common, const PolicyFunctions& policy, size_t bucket_count); + +// Type erased version of raw_hash_set::rehash. +void Rehash(CommonFields& common, const PolicyFunctions& policy, size_t n); + +// Returns the optimal size for memcpy when transferring SOO slot. +// Otherwise, returns the optimal size for memcpy SOO slot transfer +// to SooSlotIndex(). +// At the destination we are allowed to copy upto twice more bytes, +// because there is at least one more slot after SooSlotIndex(). +// The result must not exceed MaxSooSlotSize(). +// Some of the cases are merged to minimize the number of function +// instantiations. +constexpr size_t OptimalMemcpySizeForSooSlotTransfer( + size_t slot_size, size_t max_soo_slot_size = MaxSooSlotSize()) { + static_assert(MaxSooSlotSize() >= 8, "unexpectedly small SOO slot size"); + if (slot_size == 1) { + return 1; + } + if (slot_size <= 3) { + return 4; + } + // We are merging 4 and 8 into one case because we expect them to be the + // hottest cases. Copying 8 bytes is as fast on common architectures. + if (slot_size <= 8) { + return 8; + } + if (max_soo_slot_size <= 16) { + return max_soo_slot_size; + } + if (slot_size <= 16) { + return 16; + } + if (max_soo_slot_size <= 24) { + return max_soo_slot_size; + } + static_assert(MaxSooSlotSize() <= 24, "unexpectedly large SOO slot size"); + return 24; +} + +// Resizes SOO table to the NextCapacity(SooCapacity()) and prepares insert for +// the given new_hash. Returns the offset of the new element. +// `soo_slot_ctrl` is the control byte of the SOO slot. +// If soo_slot_ctrl is kEmpty +// 1. The table must be empty. +// 2. Table will be forced to be sampled. +// All possible template combinations are defined in cc file to improve +// compilation time. +template +size_t GrowSooTableToNextCapacityAndPrepareInsert(CommonFields& common, + const PolicyFunctions& policy, + size_t new_hash, + ctrl_t soo_slot_ctrl); + +// As `ResizeFullSooTableToNextCapacity`, except that we also force the SOO +// table to be sampled. SOO tables need to switch from SOO to heap in order to +// store the infoz. No-op if sampling is disabled or not possible. +void GrowFullSooTableToNextCapacityForceSampling(CommonFields& common, + const PolicyFunctions& policy); + +// Resizes table with allocated slots and change the table seed. +// Tables with SOO enabled must have capacity > policy.soo_capacity. +// No sampling will be performed since table is already allocated. +void ResizeAllocatedTableWithSeedChange(CommonFields& common, + const PolicyFunctions& policy, + size_t new_capacity); + +inline void PrepareInsertCommon(CommonFields& common) { + common.increment_size(); + common.maybe_increment_generation_on_insert(); +} + +// ClearBackingArray clears the backing array, either modifying it in place, +// or creating a new one based on the value of "reuse". +// REQUIRES: c.capacity > 0 +void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy, + void* alloc, bool reuse, bool soo_enabled); + +// Type-erased version of raw_hash_set::erase_meta_only. +void EraseMetaOnly(CommonFields& c, size_t index, size_t slot_size); + +// For trivially relocatable types we use memcpy directly. This allows us to +// share the same function body for raw_hash_set instantiations that have the +// same slot size as long as they are relocatable. +// Separate function for relocating single slot cause significant binary bloat. +template +ABSL_ATTRIBUTE_NOINLINE void TransferNRelocatable(void*, void* dst, void* src, + size_t count) { + // TODO(b/382423690): Experiment with making specialization for power of 2 and + // non power of 2. This would require passing the size of the slot. + memcpy(dst, src, SizeOfSlot * count); +} + +// Returns a pointer to `common`. This is used to implement type erased +// raw_hash_set::get_hash_ref_fn and raw_hash_set::get_alloc_ref_fn for the +// empty class cases. +void* GetRefForEmptyClass(CommonFields& common); + +// Given the hash of a value not currently in the table and the first empty +// slot in the probe sequence, finds a viable slot index to insert it at. +// +// In case there's no space left, the table can be resized or rehashed +// (for tables with deleted slots, see FindInsertPositionWithGrowthOrRehash). +// +// In the case of absence of deleted slots and positive growth_left, the element +// can be inserted in the provided `target` position. +// +// When the table has deleted slots (according to GrowthInfo), the target +// position will be searched one more time using `find_first_non_full`. +// +// REQUIRES: Table is not SOO. +// REQUIRES: At least one non-full slot available. +// REQUIRES: `target` is a valid empty position to insert. +size_t PrepareInsertNonSoo(CommonFields& common, const PolicyFunctions& policy, + size_t hash, FindInfo target); + +// A SwissTable. +// +// Policy: a policy defines how to perform different operations on +// the slots of the hashtable (see hash_policy_traits.h for the full interface +// of policy). +// +// Hash: a (possibly polymorphic) functor that hashes keys of the hashtable. The +// functor should accept a key and return size_t as hash. For best performance +// it is important that the hash function provides high entropy across all bits +// of the hash. +// +// Eq: a (possibly polymorphic) functor that compares two keys for equality. It +// should accept two (of possibly different type) keys and return a bool: true +// if they are equal, false if they are not. If two keys compare equal, then +// their hash values as defined by Hash MUST be equal. +// +// Allocator: an Allocator +// [https://en.cppreference.com/w/cpp/named_req/Allocator] with which +// the storage of the hashtable will be allocated and the elements will be +// constructed and destroyed. +template +class raw_hash_set { + using PolicyTraits = hash_policy_traits; + using KeyArgImpl = + KeyArg::value && IsTransparent::value>; + + public: + using init_type = typename PolicyTraits::init_type; + using key_type = typename PolicyTraits::key_type; + using allocator_type = Alloc; + using size_type = size_t; + using difference_type = ptrdiff_t; + using hasher = Hash; + using key_equal = Eq; + using policy_type = Policy; + using value_type = typename PolicyTraits::value_type; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = typename absl::allocator_traits< + allocator_type>::template rebind_traits::pointer; + using const_pointer = typename absl::allocator_traits< + allocator_type>::template rebind_traits::const_pointer; + + private: + // Alias used for heterogeneous lookup functions. + // `key_arg` evaluates to `K` when the functors are transparent and to + // `key_type` otherwise. It permits template argument deduction on `K` for the + // transparent case. + template + using key_arg = typename KeyArgImpl::template type; + + using slot_type = typename PolicyTraits::slot_type; + + // TODO(b/289225379): we could add extra SOO space inside raw_hash_set + // after CommonFields to allow inlining larger slot_types (e.g. std::string), + // but it's a bit complicated if we want to support incomplete mapped_type in + // flat_hash_map. We could potentially do this for flat_hash_set and for an + // allowlist of `mapped_type`s of flat_hash_map that includes e.g. arithmetic + // types, strings, cords, and pairs/tuples of allowlisted types. + constexpr static bool SooEnabled() { + return PolicyTraits::soo_enabled() && + sizeof(slot_type) <= sizeof(HeapOrSoo) && + alignof(slot_type) <= alignof(HeapOrSoo); + } + + constexpr static size_t DefaultCapacity() { + return SooEnabled() ? SooCapacity() : 0; + } + + // Whether `size` fits in the SOO capacity of this table. + bool fits_in_soo(size_t size) const { + return SooEnabled() && size <= SooCapacity(); + } + // Whether this table is in SOO mode or non-SOO mode. + bool is_soo() const { return fits_in_soo(capacity()); } + bool is_full_soo() const { return is_soo() && !empty(); } + + // Give an early error when key_type is not hashable/eq. + auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k)); + auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k)); + + using AllocTraits = absl::allocator_traits; + using SlotAlloc = typename absl::allocator_traits< + allocator_type>::template rebind_alloc; + // People are often sloppy with the exact type of their allocator (sometimes + // it has an extra const or is missing the pair, but rebinds made it work + // anyway). + using CharAlloc = + typename absl::allocator_traits::template rebind_alloc; + using SlotAllocTraits = typename absl::allocator_traits< + allocator_type>::template rebind_traits; + + static_assert(std::is_lvalue_reference::value, + "Policy::element() must return a reference"); + + // An enabler for insert(T&&): T must be convertible to init_type or be the + // same as [cv] value_type [ref]. + template + using Insertable = absl::disjunction< + std::is_same, absl::remove_cvref_t>, + std::is_convertible>; + template + using IsNotBitField = std::is_pointer; + + // RequiresNotInit is a workaround for gcc prior to 7.1. + // See https://godbolt.org/g/Y4xsUh. + template + using RequiresNotInit = + typename std::enable_if::value, int>::type; + + template + using IsDecomposable = IsDecomposable; + + template + using IsDecomposableAndInsertable = + IsDecomposable::value, T>>; + + // Evaluates to true if an assignment from the given type would require the + // source object to remain alive for the life of the element. + template + using IsLifetimeBoundAssignmentFrom = std::conditional_t< + policy_trait_element_is_owner::value, std::false_type, + type_traits_internal::IsLifetimeBoundAssignment>; + + public: + static_assert(std::is_same::value, + "Allocators with custom pointer types are not supported"); + static_assert(std::is_same::value, + "Allocators with custom pointer types are not supported"); + + class iterator : private HashSetIteratorGenerationInfo { + friend class raw_hash_set; + friend struct HashtableFreeFunctionsAccess; + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = typename raw_hash_set::value_type; + using reference = + absl::conditional_t; + using pointer = absl::remove_reference_t*; + using difference_type = typename raw_hash_set::difference_type; + + iterator() {} + + // PRECONDITION: not an end() iterator. + reference operator*() const { + AssertIsFull(ctrl_, generation(), generation_ptr(), "operator*()"); + return unchecked_deref(); + } + + // PRECONDITION: not an end() iterator. + pointer operator->() const { + AssertIsFull(ctrl_, generation(), generation_ptr(), "operator->"); + return &operator*(); + } + + // PRECONDITION: not an end() iterator. + iterator& operator++() { + AssertIsFull(ctrl_, generation(), generation_ptr(), "operator++"); + ++ctrl_; + ++slot_; + skip_empty_or_deleted(); + if (ABSL_PREDICT_FALSE(*ctrl_ == ctrl_t::kSentinel)) ctrl_ = nullptr; + return *this; + } + // PRECONDITION: not an end() iterator. + iterator operator++(int) { + auto tmp = *this; + ++*this; + return tmp; + } + + friend bool operator==(const iterator& a, const iterator& b) { + AssertIsValidForComparison(a.ctrl_, a.generation(), a.generation_ptr()); + AssertIsValidForComparison(b.ctrl_, b.generation(), b.generation_ptr()); + AssertSameContainer(a.ctrl_, b.ctrl_, a.slot_, b.slot_, + a.generation_ptr(), b.generation_ptr()); + return a.ctrl_ == b.ctrl_; + } + friend bool operator!=(const iterator& a, const iterator& b) { + return !(a == b); + } + + private: + iterator(ctrl_t* ctrl, slot_type* slot, + const GenerationType* generation_ptr) + : HashSetIteratorGenerationInfo(generation_ptr), + ctrl_(ctrl), + slot_(slot) { + // This assumption helps the compiler know that any non-end iterator is + // not equal to any end iterator. + ABSL_ASSUME(ctrl != nullptr); + } + // This constructor is used in begin() to avoid an MSan + // use-of-uninitialized-value error. Delegating from this constructor to + // the previous one doesn't avoid the error. + iterator(ctrl_t* ctrl, MaybeInitializedPtr slot, + const GenerationType* generation_ptr) + : HashSetIteratorGenerationInfo(generation_ptr), + ctrl_(ctrl), + slot_(to_slot(slot.get())) { + // This assumption helps the compiler know that any non-end iterator is + // not equal to any end iterator. + ABSL_ASSUME(ctrl != nullptr); + } + // For end() iterators. + explicit iterator(const GenerationType* generation_ptr) + : HashSetIteratorGenerationInfo(generation_ptr), ctrl_(nullptr) {} + + // Fixes up `ctrl_` to point to a full or sentinel by advancing `ctrl_` and + // `slot_` until they reach one. + void skip_empty_or_deleted() { + while (IsEmptyOrDeleted(*ctrl_)) { + uint32_t shift = + GroupFullEmptyOrDeleted{ctrl_}.CountLeadingEmptyOrDeleted(); + ctrl_ += shift; + slot_ += shift; + } + } + + ctrl_t* control() const { return ctrl_; } + slot_type* slot() const { return slot_; } + + // We use EmptyGroup() for default-constructed iterators so that they can + // be distinguished from end iterators, which have nullptr ctrl_. + ctrl_t* ctrl_ = EmptyGroup(); + // To avoid uninitialized member warnings, put slot_ in an anonymous union. + // The member is not initialized on singleton and end iterators. + union { + slot_type* slot_; + }; + + // An equality check which skips ABSL Hardening iterator invalidation + // checks. + // Should be used when the lifetimes of the iterators are well-enough + // understood to prove that they cannot be invalid. + bool unchecked_equals(const iterator& b) { return ctrl_ == b.control(); } + + // Dereferences the iterator without ABSL Hardening iterator invalidation + // checks. + reference unchecked_deref() const { return PolicyTraits::element(slot_); } + }; + + class const_iterator { + friend class raw_hash_set; + template + friend struct absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess; + + public: + using iterator_category = typename iterator::iterator_category; + using value_type = typename raw_hash_set::value_type; + using reference = typename raw_hash_set::const_reference; + using pointer = typename raw_hash_set::const_pointer; + using difference_type = typename raw_hash_set::difference_type; + + const_iterator() = default; + // Implicit construction from iterator. + const_iterator(iterator i) : inner_(std::move(i)) {} // NOLINT + + reference operator*() const { return *inner_; } + pointer operator->() const { return inner_.operator->(); } + + const_iterator& operator++() { + ++inner_; + return *this; + } + const_iterator operator++(int) { return inner_++; } + + friend bool operator==(const const_iterator& a, const const_iterator& b) { + return a.inner_ == b.inner_; + } + friend bool operator!=(const const_iterator& a, const const_iterator& b) { + return !(a == b); + } + + private: + const_iterator(const ctrl_t* ctrl, const slot_type* slot, + const GenerationType* gen) + : inner_(const_cast(ctrl), const_cast(slot), gen) { + } + ctrl_t* control() const { return inner_.control(); } + slot_type* slot() const { return inner_.slot(); } + + iterator inner_; + + bool unchecked_equals(const const_iterator& b) { + return inner_.unchecked_equals(b.inner_); + } + }; + + using node_type = node_handle, Alloc>; + using insert_return_type = InsertReturnType; + + // Note: can't use `= default` due to non-default noexcept (causes + // problems for some compilers). NOLINTNEXTLINE + raw_hash_set() noexcept( + std::is_nothrow_default_constructible::value && + std::is_nothrow_default_constructible::value && + std::is_nothrow_default_constructible::value) {} + + ABSL_ATTRIBUTE_NOINLINE explicit raw_hash_set( + size_t bucket_count, const hasher& hash = hasher(), + const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : settings_(CommonFields::CreateDefault(), hash, eq, + alloc) { + if (bucket_count > DefaultCapacity()) { + ReserveEmptyNonAllocatedTableToFitBucketCount( + common(), GetPolicyFunctions(), bucket_count); + } + } + + raw_hash_set(size_t bucket_count, const hasher& hash, + const allocator_type& alloc) + : raw_hash_set(bucket_count, hash, key_equal(), alloc) {} + + raw_hash_set(size_t bucket_count, const allocator_type& alloc) + : raw_hash_set(bucket_count, hasher(), key_equal(), alloc) {} + + explicit raw_hash_set(const allocator_type& alloc) + : raw_hash_set(0, hasher(), key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count = 0, + const hasher& hash = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(SelectBucketCountForIterRange(first, last, bucket_count), + hash, eq, alloc) { + insert(first, last); + } + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count, + const hasher& hash, const allocator_type& alloc) + : raw_hash_set(first, last, bucket_count, hash, key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(first, last, bucket_count, hasher(), key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, const allocator_type& alloc) + : raw_hash_set(first, last, 0, hasher(), key_equal(), alloc) {} + + // Instead of accepting std::initializer_list as the first + // argument like std::unordered_set does, we have two overloads + // that accept std::initializer_list and std::initializer_list. + // This is advantageous for performance. + // + // // Turns {"abc", "def"} into std::initializer_list, then + // // copies the strings into the set. + // std::unordered_set s = {"abc", "def"}; + // + // // Turns {"abc", "def"} into std::initializer_list, then + // // copies the strings into the set. + // absl::flat_hash_set s = {"abc", "def"}; + // + // The same trick is used in insert(). + // + // The enabler is necessary to prevent this constructor from triggering where + // the copy constructor is meant to be called. + // + // absl::flat_hash_set a, b{a}; + // + // RequiresNotInit is a workaround for gcc prior to 7.1. + template = 0, + std::enable_if_t::value, int> = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count = 0, + const hasher& hash = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(init.begin(), init.end(), bucket_count, hash, eq, alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count = 0, + const hasher& hash = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(init.begin(), init.end(), bucket_count, hash, eq, alloc) {} + + template = 0, + std::enable_if_t::value, int> = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count, + const hasher& hash, const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hash, key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count, + const hasher& hash, const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hash, key_equal(), alloc) {} + + template = 0, + std::enable_if_t::value, int> = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {} + + template = 0, + std::enable_if_t::value, int> = 0> + raw_hash_set(std::initializer_list init, const allocator_type& alloc) + : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, + const allocator_type& alloc) + : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {} + + raw_hash_set(const raw_hash_set& that) + : raw_hash_set(that, AllocTraits::select_on_container_copy_construction( + allocator_type(that.char_alloc_ref()))) {} + + raw_hash_set(const raw_hash_set& that, const allocator_type& a) + : raw_hash_set(SizeToCapacity(that.size()), that.hash_ref(), + that.eq_ref(), a) { + that.AssertNotDebugCapacity(); + const size_t size = that.size(); + if (size == 0) { + return; + } + // We don't use `that.is_soo()` here because `that` can have non-SOO + // capacity but have a size that fits into SOO capacity. + if (fits_in_soo(size)) { + ABSL_SWISSTABLE_ASSERT(size == 1); + common().set_full_soo(); + emplace_at(soo_iterator(), *that.begin()); + if (should_sample_soo()) { + GrowFullSooTableToNextCapacityForceSampling(common(), + GetPolicyFunctions()); + } + return; + } + ABSL_SWISSTABLE_ASSERT(!that.is_soo()); + const size_t cap = capacity(); + // Note about single group tables: + // 1. It is correct to have any order of elements. + // 2. Order has to be non deterministic. + // 3. We are assigning elements with arbitrary `shift` starting from + // `capacity + shift` position. + // 4. `shift` must be coprime with `capacity + 1` in order to be able to use + // modular arithmetic to traverse all positions, instead if cycling + // through a subset of positions. Odd numbers are coprime with any + // `capacity + 1` (2^N). + size_t offset = cap; + const size_t shift = + is_single_group(cap) ? (common().seed().seed() | 1) : 0; + IterateOverFullSlots( + that.common(), sizeof(slot_type), + [&](const ctrl_t* that_ctrl, void* that_slot_void) { + slot_type* that_slot = static_cast(that_slot_void); + if (shift == 0) { + // Big tables case. Position must be searched via probing. + // The table is guaranteed to be empty, so we can do faster than + // a full `insert`. + const size_t hash = PolicyTraits::apply( + HashElement{hash_ref()}, PolicyTraits::element(that_slot)); + FindInfo target = find_first_non_full(common(), hash); + infoz().RecordInsert(hash, target.probe_length); + offset = target.offset; + } else { + // Small tables case. Next position is computed via shift. + offset = (offset + shift) & cap; + } + const h2_t h2 = static_cast(*that_ctrl); + ABSL_SWISSTABLE_ASSERT( // We rely that hash is not changed for small + // tables. + H2(PolicyTraits::apply(HashElement{hash_ref()}, + PolicyTraits::element(that_slot))) == h2 && + "hash function value changed unexpectedly during the copy"); + SetCtrl(common(), offset, h2, sizeof(slot_type)); + emplace_at(iterator_at(offset), PolicyTraits::element(that_slot)); + common().maybe_increment_generation_on_insert(); + }); + if (shift != 0) { + // On small table copy we do not record individual inserts. + // RecordInsert requires hash, but it is unknown for small tables. + infoz().RecordStorageChanged(size, cap); + } + common().increment_size(size); + growth_info().OverwriteManyEmptyAsFull(size); + } + + ABSL_ATTRIBUTE_NOINLINE raw_hash_set(raw_hash_set&& that) noexcept( + std::is_nothrow_copy_constructible::value && + std::is_nothrow_copy_constructible::value && + std::is_nothrow_copy_constructible::value) + : // Hash, equality and allocator are copied instead of moved because + // `that` must be left valid. If Hash is std::function, moving it + // would create a nullptr functor that cannot be called. + // Note: we avoid using exchange for better generated code. + settings_(PolicyTraits::transfer_uses_memcpy() || !that.is_full_soo() + ? std::move(that.common()) + : CommonFields{full_soo_tag_t{}}, + that.hash_ref(), that.eq_ref(), that.char_alloc_ref()) { + if (!PolicyTraits::transfer_uses_memcpy() && that.is_full_soo()) { + transfer(soo_slot(), that.soo_slot()); + } + that.common() = CommonFields::CreateDefault(); + annotate_for_bug_detection_on_move(that); + } + + raw_hash_set(raw_hash_set&& that, const allocator_type& a) + : settings_(CommonFields::CreateDefault(), that.hash_ref(), + that.eq_ref(), a) { + if (CharAlloc(a) == that.char_alloc_ref()) { + swap_common(that); + annotate_for_bug_detection_on_move(that); + } else { + move_elements_allocs_unequal(std::move(that)); + } + } + + raw_hash_set& operator=(const raw_hash_set& that) { + that.AssertNotDebugCapacity(); + if (ABSL_PREDICT_FALSE(this == &that)) return *this; + constexpr bool propagate_alloc = + AllocTraits::propagate_on_container_copy_assignment::value; + // TODO(ezb): maybe avoid allocating a new backing array if this->capacity() + // is an exact match for that.size(). If this->capacity() is too big, then + // it would make iteration very slow to reuse the allocation. Maybe we can + // do the same heuristic as clear() and reuse if it's small enough. + allocator_type alloc(propagate_alloc ? that.char_alloc_ref() + : char_alloc_ref()); + raw_hash_set tmp(that, alloc); + // NOLINTNEXTLINE: not returning *this for performance. + return assign_impl(std::move(tmp)); + } + + raw_hash_set& operator=(raw_hash_set&& that) noexcept( + absl::allocator_traits::is_always_equal::value && + std::is_nothrow_move_assignable::value && + std::is_nothrow_move_assignable::value) { + // TODO(sbenza): We should only use the operations from the noexcept clause + // to make sure we actually adhere to that contract. + // NOLINTNEXTLINE: not returning *this for performance. + return move_assign( + std::move(that), + typename AllocTraits::propagate_on_container_move_assignment()); + } + + ~raw_hash_set() { + destructor_impl(); + if constexpr (SwisstableAssertAccessToDestroyedTable()) { + common().set_capacity(InvalidCapacity::kDestroyed); + } + } + + iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (ABSL_PREDICT_FALSE(empty())) return end(); + if (is_soo()) return soo_iterator(); + iterator it = {control(), common().slots_union(), + common().generation_ptr()}; + it.skip_empty_or_deleted(); + ABSL_SWISSTABLE_ASSERT(IsFull(*it.control())); + return it; + } + iterator end() ABSL_ATTRIBUTE_LIFETIME_BOUND { + AssertNotDebugCapacity(); + return iterator(common().generation_ptr()); + } + + const_iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return const_cast(this)->begin(); + } + const_iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return const_cast(this)->end(); + } + const_iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return begin(); + } + const_iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return end(); } + + bool empty() const { return !size(); } + size_t size() const { + AssertNotDebugCapacity(); + return common().size(); + } + size_t capacity() const { + const size_t cap = common().capacity(); + // Compiler complains when using functions in ASSUME so use local variable. + ABSL_ATTRIBUTE_UNUSED static constexpr size_t kDefaultCapacity = + DefaultCapacity(); + ABSL_ASSUME(cap >= kDefaultCapacity); + return cap; + } + size_t max_size() const { return MaxValidSize(sizeof(slot_type)); } + + ABSL_ATTRIBUTE_REINITIALIZES void clear() { + if (SwisstableGenerationsEnabled() && + capacity() >= InvalidCapacity::kMovedFrom) { + common().set_capacity(DefaultCapacity()); + } + AssertNotDebugCapacity(); + // Iterating over this container is O(bucket_count()). When bucket_count() + // is much greater than size(), iteration becomes prohibitively expensive. + // For clear() it is more important to reuse the allocated array when the + // container is small because allocation takes comparatively long time + // compared to destruction of the elements of the container. So we pick the + // largest bucket_count() threshold for which iteration is still fast and + // past that we simply deallocate the array. + const size_t cap = capacity(); + if (cap == 0) { + // Already guaranteed to be empty; so nothing to do. + } else if (is_soo()) { + if (!empty()) destroy(soo_slot()); + common().set_empty_soo(); + } else { + destroy_slots(); + clear_backing_array(/*reuse=*/cap < 128); + } + common().set_reserved_growth(0); + common().set_reservation_size(0); + } + + // This overload kicks in when the argument is an rvalue of insertable and + // decomposable type other than init_type. + // + // flat_hash_map m; + // m.insert(std::make_pair("abc", 42)); + template ::value && + IsNotBitField::value && + !IsLifetimeBoundAssignmentFrom::value, + int>()> + std::pair insert(T&& value) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return emplace(std::forward(value)); + } + + template ::value && + IsNotBitField::value && + IsLifetimeBoundAssignmentFrom::value, + int> = 0> + std::pair insert( + T&& value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template insert(std::forward(value)); + } + + // This overload kicks in when the argument is a bitfield or an lvalue of + // insertable and decomposable type. + // + // union { int n : 1; }; + // flat_hash_set s; + // s.insert(n); + // + // flat_hash_set s; + // const char* p = "hello"; + // s.insert(p); + // + template ::value && + !IsLifetimeBoundAssignmentFrom::value, + int>()> + std::pair insert(const T& value) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return emplace(value); + } + template ::value && + IsLifetimeBoundAssignmentFrom::value, + int> = 0> + std::pair insert( + const T& value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template insert(value); + } + + // This overload kicks in when the argument is an rvalue of init_type. Its + // purpose is to handle brace-init-list arguments. + // + // flat_hash_map s; + // s.insert({"abc", 42}); + std::pair insert(init_type&& value) + ABSL_ATTRIBUTE_LIFETIME_BOUND +#if __cplusplus >= 202002L + requires(!IsLifetimeBoundAssignmentFrom::value) +#endif + { + return emplace(std::move(value)); + } +#if __cplusplus >= 202002L + std::pair insert( + init_type&& value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_ATTRIBUTE_LIFETIME_BOUND + requires(IsLifetimeBoundAssignmentFrom::value) + { + return emplace(std::move(value)); + } +#endif + + template ::value && + IsNotBitField::value && + !IsLifetimeBoundAssignmentFrom::value, + int>()> + iterator insert(const_iterator, T&& value) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return insert(std::forward(value)).first; + } + template ::value && + IsNotBitField::value && + IsLifetimeBoundAssignmentFrom::value, + int> = 0> + iterator insert(const_iterator hint, + T&& value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template insert(hint, std::forward(value)); + } + + template ::value, int> = 0> + iterator insert(const_iterator, + const T& value) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return insert(value).first; + } + + iterator insert(const_iterator, + init_type&& value) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return insert(std::move(value)).first; + } + + template + void insert(InputIt first, InputIt last) { + for (; first != last; ++first) emplace(*first); + } + + template = 0, + std::enable_if_t::value, int> = 0> + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + insert_return_type insert(node_type&& node) ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (!node) return {end(), false, node_type()}; + const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node)); + auto res = PolicyTraits::apply( + InsertSlot{*this, std::move(*CommonAccess::GetSlot(node))}, + elem); + if (res.second) { + CommonAccess::Reset(&node); + return {res.first, true, node_type()}; + } else { + return {res.first, false, std::move(node)}; + } + } + + iterator insert(const_iterator, + node_type&& node) ABSL_ATTRIBUTE_LIFETIME_BOUND { + auto res = insert(std::move(node)); + node = std::move(res.node); + return res.position; + } + + // This overload kicks in if we can deduce the key from args. This enables us + // to avoid constructing value_type if an entry with the same key already + // exists. + // + // For example: + // + // flat_hash_map m = {{"abc", "def"}}; + // // Creates no std::string copies and makes no heap allocations. + // m.emplace("abc", "xyz"); + template ::value, int> = 0> + std::pair emplace(Args&&... args) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return PolicyTraits::apply(EmplaceDecomposable{*this}, + std::forward(args)...); + } + + // This overload kicks in if we cannot deduce the key from args. It constructs + // value_type unconditionally and then either moves it into the table or + // destroys. + template ::value, int> = 0> + std::pair emplace(Args&&... args) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + alignas(slot_type) unsigned char raw[sizeof(slot_type)]; + slot_type* slot = to_slot(&raw); + + construct(slot, std::forward(args)...); + const auto& elem = PolicyTraits::element(slot); + return PolicyTraits::apply(InsertSlot{*this, std::move(*slot)}, elem); + } + + template + iterator emplace_hint(const_iterator, + Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return emplace(std::forward(args)...).first; + } + + // Extension API: support for lazy emplace. + // + // Looks up key in the table. If found, returns the iterator to the element. + // Otherwise calls `f` with one argument of type `raw_hash_set::constructor`, + // and returns an iterator to the new element. + // + // `f` must abide by several restrictions: + // - it MUST call `raw_hash_set::constructor` with arguments as if a + // `raw_hash_set::value_type` is constructed, + // - it MUST NOT access the container before the call to + // `raw_hash_set::constructor`, and + // - it MUST NOT erase the lazily emplaced element. + // Doing any of these is undefined behavior. + // + // For example: + // + // std::unordered_set s; + // // Makes ArenaStr even if "abc" is in the map. + // s.insert(ArenaString(&arena, "abc")); + // + // flat_hash_set s; + // // Makes ArenaStr only if "abc" is not in the map. + // s.lazy_emplace("abc", [&](const constructor& ctor) { + // ctor(&arena, "abc"); + // }); + // + // WARNING: This API is currently experimental. If there is a way to implement + // the same thing with the rest of the API, prefer that. + class constructor { + friend class raw_hash_set; + + public: + template + void operator()(Args&&... args) const { + ABSL_SWISSTABLE_ASSERT(*slot_); + PolicyTraits::construct(alloc_, *slot_, std::forward(args)...); + *slot_ = nullptr; + } + + private: + constructor(allocator_type* a, slot_type** slot) : alloc_(a), slot_(slot) {} + + allocator_type* alloc_; + slot_type** slot_; + }; + + template + iterator lazy_emplace(const key_arg& key, + F&& f) ABSL_ATTRIBUTE_LIFETIME_BOUND { + auto res = find_or_prepare_insert(key); + if (res.second) { + slot_type* slot = res.first.slot(); + allocator_type alloc(char_alloc_ref()); + std::forward(f)(constructor(&alloc, &slot)); + ABSL_SWISSTABLE_ASSERT(!slot); + } + return res.first; + } + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.erase("abc"); + // + // flat_hash_set s; + // // Uses "abc" directly without copying it into std::string. + // s.erase("abc"); + template + size_type erase(const key_arg& key) { + auto it = find(key); + if (it == end()) return 0; + erase(it); + return 1; + } + + // Erases the element pointed to by `it`. Unlike `std::unordered_set::erase`, + // this method returns void to reduce algorithmic complexity to O(1). The + // iterator is invalidated so any increment should be done before calling + // erase (e.g. `erase(it++)`). + void erase(const_iterator cit) { erase(cit.inner_); } + + // This overload is necessary because otherwise erase(const K&) would be + // a better match if non-const iterator is passed as an argument. + void erase(iterator it) { + AssertNotDebugCapacity(); + AssertIsFull(it.control(), it.generation(), it.generation_ptr(), "erase()"); + destroy(it.slot()); + if (is_soo()) { + common().set_empty_soo(); + } else { + erase_meta_only(it); + } + } + + iterator erase(const_iterator first, + const_iterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND { + AssertNotDebugCapacity(); + // We check for empty first because clear_backing_array requires that + // capacity() > 0 as a precondition. + if (empty()) return end(); + if (first == last) return last.inner_; + if (is_soo()) { + destroy(soo_slot()); + common().set_empty_soo(); + return end(); + } + if (first == begin() && last == end()) { + // TODO(ezb): we access control bytes in destroy_slots so it could make + // sense to combine destroy_slots and clear_backing_array to avoid cache + // misses when the table is large. Note that we also do this in clear(). + destroy_slots(); + clear_backing_array(/*reuse=*/true); + common().set_reserved_growth(common().reservation_size()); + return end(); + } + while (first != last) { + erase(first++); + } + return last.inner_; + } + + // Moves elements from `src` into `this`. + // If the element already exists in `this`, it is left unmodified in `src`. + template + void merge(raw_hash_set& src) { // NOLINT + AssertNotDebugCapacity(); + src.AssertNotDebugCapacity(); + assert(this != &src); + // Returns whether insertion took place. + const auto insert_slot = [this](slot_type* src_slot) { + return PolicyTraits::apply(InsertSlot{*this, std::move(*src_slot)}, + PolicyTraits::element(src_slot)) + .second; + }; + + if (src.is_soo()) { + if (src.empty()) return; + if (insert_slot(src.soo_slot())) src.common().set_empty_soo(); + return; + } + for (auto it = src.begin(), e = src.end(); it != e;) { + auto next = std::next(it); + if (insert_slot(it.slot())) src.erase_meta_only(it); + it = next; + } + } + + template + void merge(raw_hash_set&& src) { + merge(src); + } + + node_type extract(const_iterator position) { + AssertNotDebugCapacity(); + AssertIsFull(position.control(), position.inner_.generation(), + position.inner_.generation_ptr(), "extract()"); + allocator_type alloc(char_alloc_ref()); + auto node = CommonAccess::Transfer(alloc, position.slot()); + if (is_soo()) { + common().set_empty_soo(); + } else { + erase_meta_only(position); + } + return node; + } + + template ::value, int> = 0> + node_type extract(const key_arg& key) { + auto it = find(key); + return it == end() ? node_type() : extract(const_iterator{it}); + } + + void swap(raw_hash_set& that) noexcept( + IsNoThrowSwappable() && IsNoThrowSwappable() && + IsNoThrowSwappable( + typename AllocTraits::propagate_on_container_swap{})) { + AssertNotDebugCapacity(); + that.AssertNotDebugCapacity(); + using std::swap; + swap_common(that); + swap(hash_ref(), that.hash_ref()); + swap(eq_ref(), that.eq_ref()); + SwapAlloc(char_alloc_ref(), that.char_alloc_ref(), + typename AllocTraits::propagate_on_container_swap{}); + } + + void rehash(size_t n) { Rehash(common(), GetPolicyFunctions(), n); } + + void reserve(size_t n) { + if (ABSL_PREDICT_TRUE(n > DefaultCapacity())) { + ReserveTableToFitNewSize(common(), GetPolicyFunctions(), n); + } + } + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.count("abc"); + // + // ch_set s; + // // Uses "abc" directly without copying it into std::string. + // s.count("abc"); + template + size_t count(const key_arg& key) const { + return find(key) == end() ? 0 : 1; + } + + // Issues CPU prefetch instructions for the memory needed to find or insert + // a key. Like all lookup functions, this support heterogeneous keys. + // + // NOTE: This is a very low level operation and should not be used without + // specific benchmarks indicating its importance. + template + void prefetch(const key_arg& key) const { + if (capacity() == DefaultCapacity()) return; + (void)key; + // Avoid probing if we won't be able to prefetch the addresses received. +#ifdef ABSL_HAVE_PREFETCH + prefetch_heap_block(); + auto seq = probe(common(), hash_ref()(key)); + PrefetchToLocalCache(control() + seq.offset()); + PrefetchToLocalCache(slot_array() + seq.offset()); +#endif // ABSL_HAVE_PREFETCH + } + + template + ABSL_DEPRECATE_AND_INLINE() + iterator find(const key_arg& key, + size_t) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return find(key); + } + // The API of find() has one extension: the type of the key argument doesn't + // have to be key_type. This is so called heterogeneous key support. + template + iterator find(const key_arg& key) ABSL_ATTRIBUTE_LIFETIME_BOUND { + AssertOnFind(key); + if (is_soo()) return find_soo(key); + prefetch_heap_block(); + return find_non_soo(key, hash_ref()(key)); + } + + template + ABSL_DEPRECATE_AND_INLINE() + const_iterator find(const key_arg& key, + size_t) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return find(key); + } + template + const_iterator find(const key_arg& key) const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return const_cast(this)->find(key); + } + + template + bool contains(const key_arg& key) const { + // Here neither the iterator returned by `find()` nor `end()` can be invalid + // outside of potential thread-safety issues. + // `find()`'s return value is constructed, used, and then destructed + // all in this context. + return !find(key).unchecked_equals(end()); + } + + template + std::pair equal_range(const key_arg& key) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + auto it = find(key); + if (it != end()) return {it, std::next(it)}; + return {it, it}; + } + template + std::pair equal_range( + const key_arg& key) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + auto it = find(key); + if (it != end()) return {it, std::next(it)}; + return {it, it}; + } + + size_t bucket_count() const { return capacity(); } + float load_factor() const { + return capacity() ? static_cast(size()) / capacity() : 0.0; + } + float max_load_factor() const { return 1.0f; } + void max_load_factor(float) { + // Does nothing. + } + + hasher hash_function() const { return hash_ref(); } + key_equal key_eq() const { return eq_ref(); } + allocator_type get_allocator() const { + return allocator_type(char_alloc_ref()); + } + + friend bool operator==(const raw_hash_set& a, const raw_hash_set& b) { + if (a.size() != b.size()) return false; + const raw_hash_set* outer = &a; + const raw_hash_set* inner = &b; + if (outer->capacity() > inner->capacity()) std::swap(outer, inner); + for (const value_type& elem : *outer) { + auto it = PolicyTraits::apply(FindElement{*inner}, elem); + if (it == inner->end()) return false; + // Note: we used key_equal to check for key equality in FindElement, but + // we may need to do an additional comparison using + // value_type::operator==. E.g. the keys could be equal and the + // mapped_types could be unequal in a map or even in a set, key_equal + // could ignore some fields that aren't ignored by operator==. + static constexpr bool kKeyEqIsValueEq = + std::is_same::value && + std::is_same>::value; + if (!kKeyEqIsValueEq && !(*it == elem)) return false; + } + return true; + } + + friend bool operator!=(const raw_hash_set& a, const raw_hash_set& b) { + return !(a == b); + } + + template + friend typename std::enable_if::value, + H>::type + AbslHashValue(H h, const raw_hash_set& s) { + return H::combine(H::combine_unordered(std::move(h), s.begin(), s.end()), + hash_internal::WeaklyMixedInteger{s.size()}); + } + + friend void swap(raw_hash_set& a, + raw_hash_set& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); + } + + private: + template + friend struct absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess; + + friend struct absl::container_internal::HashtableFreeFunctionsAccess; + + struct FindElement { + template + const_iterator operator()(const K& key, Args&&...) const { + return s.find(key); + } + const raw_hash_set& s; + }; + + struct HashElement { + template + size_t operator()(const K& key, Args&&...) const { + return h(key); + } + const hasher& h; + }; + + template + struct EqualElement { + template + bool operator()(const K2& lhs, Args&&...) const { + ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(eq(lhs, rhs)); + } + const K1& rhs; + const key_equal& eq; + }; + + struct EmplaceDecomposable { + template + std::pair operator()(const K& key, Args&&... args) const { + auto res = s.find_or_prepare_insert(key); + if (res.second) { + s.emplace_at(res.first, std::forward(args)...); + } + return res; + } + raw_hash_set& s; + }; + + template + struct InsertSlot { + template + std::pair operator()(const K& key, Args&&...) && { + auto res = s.find_or_prepare_insert(key); + if (res.second) { + s.transfer(res.first.slot(), &slot); + } else if (do_destroy) { + s.destroy(&slot); + } + return res; + } + raw_hash_set& s; + // Constructed slot. Either moved into place or destroyed. + slot_type&& slot; + }; + + template + inline void construct(slot_type* slot, Args&&... args) { + common().RunWithReentrancyGuard([&] { + allocator_type alloc(char_alloc_ref()); + PolicyTraits::construct(&alloc, slot, std::forward(args)...); + }); + } + inline void destroy(slot_type* slot) { + common().RunWithReentrancyGuard([&] { + allocator_type alloc(char_alloc_ref()); + PolicyTraits::destroy(&alloc, slot); + }); + } + inline void transfer(slot_type* to, slot_type* from) { + common().RunWithReentrancyGuard([&] { + allocator_type alloc(char_alloc_ref()); + PolicyTraits::transfer(&alloc, to, from); + }); + } + + // TODO(b/289225379): consider having a helper class that has the impls for + // SOO functionality. + template + iterator find_soo(const key_arg& key) { + ABSL_SWISSTABLE_ASSERT(is_soo()); + return empty() || !PolicyTraits::apply(EqualElement{key, eq_ref()}, + PolicyTraits::element(soo_slot())) + ? end() + : soo_iterator(); + } + + template + iterator find_non_soo(const key_arg& key, size_t hash) { + ABSL_SWISSTABLE_ASSERT(!is_soo()); + auto seq = probe(common(), hash); + const h2_t h2 = H2(hash); + const ctrl_t* ctrl = control(); + while (true) { +#ifndef ABSL_HAVE_MEMORY_SANITIZER + absl::PrefetchToLocalCache(slot_array() + seq.offset()); +#endif + Group g{ctrl + seq.offset()}; + for (uint32_t i : g.Match(h2)) { + if (ABSL_PREDICT_TRUE(PolicyTraits::apply( + EqualElement{key, eq_ref()}, + PolicyTraits::element(slot_array() + seq.offset(i))))) + return iterator_at(seq.offset(i)); + } + if (ABSL_PREDICT_TRUE(g.MaskEmpty())) return end(); + seq.next(); + ABSL_SWISSTABLE_ASSERT(seq.index() <= capacity() && "full table!"); + } + } + + // Returns true if the table needs to be sampled. + // This should be called on insertion into an empty SOO table and in copy + // construction when the size can fit in SOO capacity. + bool should_sample_soo() const { + ABSL_SWISSTABLE_ASSERT(is_soo()); + if (!ShouldSampleHashtablezInfoForAlloc()) return false; + return ABSL_PREDICT_FALSE(ShouldSampleNextTable()); + } + + void clear_backing_array(bool reuse) { + ABSL_SWISSTABLE_ASSERT(capacity() > DefaultCapacity()); + ClearBackingArray(common(), GetPolicyFunctions(), &char_alloc_ref(), reuse, + SooEnabled()); + } + + void destroy_slots() { + ABSL_SWISSTABLE_ASSERT(!is_soo()); + if (PolicyTraits::template destroy_is_trivial()) return; + auto destroy_slot = [&](const ctrl_t*, void* slot) { + this->destroy(static_cast(slot)); + }; + if constexpr (SwisstableAssertAccessToDestroyedTable()) { + CommonFields common_copy(non_soo_tag_t{}, this->common()); + common().set_capacity(InvalidCapacity::kDestroyed); + IterateOverFullSlots(common_copy, sizeof(slot_type), destroy_slot); + common().set_capacity(common_copy.capacity()); + } else { + IterateOverFullSlots(common(), sizeof(slot_type), destroy_slot); + } + } + + void dealloc() { + ABSL_SWISSTABLE_ASSERT(capacity() > DefaultCapacity()); + // Unpoison before returning the memory to the allocator. + SanitizerUnpoisonMemoryRegion(slot_array(), sizeof(slot_type) * capacity()); + infoz().Unregister(); + DeallocateBackingArray(&char_alloc_ref(), capacity(), control(), + sizeof(slot_type), alignof(slot_type), + common().has_infoz()); + } + + void destructor_impl() { + if (SwisstableGenerationsEnabled() && + capacity() >= InvalidCapacity::kMovedFrom) { + return; + } + if (capacity() == 0) return; + if (is_soo()) { + if (!empty()) { + ABSL_SWISSTABLE_IGNORE_UNINITIALIZED(destroy(soo_slot())); + } + return; + } + destroy_slots(); + dealloc(); + } + + // Erases, but does not destroy, the value pointed to by `it`. + // + // This merely updates the pertinent control byte. This can be used in + // conjunction with Policy::transfer to move the object to another place. + void erase_meta_only(const_iterator it) { + ABSL_SWISSTABLE_ASSERT(!is_soo()); + EraseMetaOnly(common(), static_cast(it.control() - control()), + sizeof(slot_type)); + } + + size_t hash_of(slot_type* slot) const { + return PolicyTraits::apply(HashElement{hash_ref()}, + PolicyTraits::element(slot)); + } + + // Casting directly from e.g. char* to slot_type* can cause compilation errors + // on objective-C. This function converts to void* first, avoiding the issue. + static slot_type* to_slot(void* buf) { return static_cast(buf); } + + // Requires that lhs does not have a full SOO slot. + static void move_common(bool rhs_is_full_soo, CharAlloc& rhs_alloc, + CommonFields& lhs, CommonFields&& rhs) { + if (PolicyTraits::transfer_uses_memcpy() || !rhs_is_full_soo) { + lhs = std::move(rhs); + } else { + lhs.move_non_heap_or_soo_fields(rhs); + rhs.RunWithReentrancyGuard([&] { + lhs.RunWithReentrancyGuard([&] { + PolicyTraits::transfer(&rhs_alloc, to_slot(lhs.soo_data()), + to_slot(rhs.soo_data())); + }); + }); + } + } + + // Swaps common fields making sure to avoid memcpy'ing a full SOO slot if we + // aren't allowed to do so. + void swap_common(raw_hash_set& that) { + using std::swap; + if (PolicyTraits::transfer_uses_memcpy()) { + swap(common(), that.common()); + return; + } + CommonFields tmp = CommonFields(uninitialized_tag_t{}); + const bool that_is_full_soo = that.is_full_soo(); + move_common(that_is_full_soo, that.char_alloc_ref(), tmp, + std::move(that.common())); + move_common(is_full_soo(), char_alloc_ref(), that.common(), + std::move(common())); + move_common(that_is_full_soo, that.char_alloc_ref(), common(), + std::move(tmp)); + } + + void annotate_for_bug_detection_on_move( + ABSL_ATTRIBUTE_UNUSED raw_hash_set& that) { + // We only enable moved-from validation when generations are enabled (rather + // than using NDEBUG) to avoid issues in which NDEBUG is enabled in some + // translation units but not in others. + if (SwisstableGenerationsEnabled()) { + that.common().set_capacity(this == &that ? InvalidCapacity::kSelfMovedFrom + : InvalidCapacity::kMovedFrom); + } + if (!SwisstableGenerationsEnabled() || capacity() == DefaultCapacity() || + capacity() > kAboveMaxValidCapacity) { + return; + } + common().increment_generation(); + if (!empty() && common().should_rehash_for_bug_detection_on_move()) { + ResizeAllocatedTableWithSeedChange(common(), GetPolicyFunctions(), + capacity()); + } + } + + template + raw_hash_set& assign_impl(raw_hash_set&& that) { + // We don't bother checking for this/that aliasing. We just need to avoid + // breaking the invariants in that case. + destructor_impl(); + move_common(that.is_full_soo(), that.char_alloc_ref(), common(), + std::move(that.common())); + hash_ref() = that.hash_ref(); + eq_ref() = that.eq_ref(); + CopyAlloc(char_alloc_ref(), that.char_alloc_ref(), + std::integral_constant()); + that.common() = CommonFields::CreateDefault(); + annotate_for_bug_detection_on_move(that); + return *this; + } + + raw_hash_set& move_elements_allocs_unequal(raw_hash_set&& that) { + const size_t size = that.size(); + if (size == 0) return *this; + reserve(size); + for (iterator it = that.begin(); it != that.end(); ++it) { + insert(std::move(PolicyTraits::element(it.slot()))); + that.destroy(it.slot()); + } + if (!that.is_soo()) that.dealloc(); + that.common() = CommonFields::CreateDefault(); + annotate_for_bug_detection_on_move(that); + return *this; + } + + raw_hash_set& move_assign(raw_hash_set&& that, + std::true_type /*propagate_alloc*/) { + return assign_impl(std::move(that)); + } + raw_hash_set& move_assign(raw_hash_set&& that, + std::false_type /*propagate_alloc*/) { + if (char_alloc_ref() == that.char_alloc_ref()) { + return assign_impl(std::move(that)); + } + // Aliasing can't happen here because allocs would compare equal above. + assert(this != &that); + destructor_impl(); + // We can't take over that's memory so we need to move each element. + // While moving elements, this should have that's hash/eq so copy hash/eq + // before moving elements. + hash_ref() = that.hash_ref(); + eq_ref() = that.eq_ref(); + return move_elements_allocs_unequal(std::move(that)); + } + + template + std::pair find_or_prepare_insert_soo(const K& key) { + ctrl_t soo_slot_ctrl; + if (empty()) { + if (!should_sample_soo()) { + common().set_full_soo(); + return {soo_iterator(), true}; + } + soo_slot_ctrl = ctrl_t::kEmpty; + } else if (PolicyTraits::apply(EqualElement{key, eq_ref()}, + PolicyTraits::element(soo_slot()))) { + return {soo_iterator(), false}; + } else { + soo_slot_ctrl = static_cast(H2(hash_of(soo_slot()))); + } + constexpr bool kUseMemcpy = + PolicyTraits::transfer_uses_memcpy() && SooEnabled(); + size_t index = GrowSooTableToNextCapacityAndPrepareInsert< + kUseMemcpy ? OptimalMemcpySizeForSooSlotTransfer(sizeof(slot_type)) : 0, + kUseMemcpy>(common(), GetPolicyFunctions(), hash_ref()(key), + soo_slot_ctrl); + return {iterator_at(index), true}; + } + + template + std::pair find_or_prepare_insert_non_soo(const K& key) { + ABSL_SWISSTABLE_ASSERT(!is_soo()); + prefetch_heap_block(); + const size_t hash = hash_ref()(key); + auto seq = probe(common(), hash); + const h2_t h2 = H2(hash); + const ctrl_t* ctrl = control(); + while (true) { +#ifndef ABSL_HAVE_MEMORY_SANITIZER + absl::PrefetchToLocalCache(slot_array() + seq.offset()); +#endif + Group g{ctrl + seq.offset()}; + for (uint32_t i : g.Match(h2)) { + if (ABSL_PREDICT_TRUE(PolicyTraits::apply( + EqualElement{key, eq_ref()}, + PolicyTraits::element(slot_array() + seq.offset(i))))) + return {iterator_at(seq.offset(i)), false}; + } + auto mask_empty = g.MaskEmpty(); + if (ABSL_PREDICT_TRUE(mask_empty)) { + size_t target = seq.offset(mask_empty.LowestBitSet()); + return {iterator_at(PrepareInsertNonSoo(common(), GetPolicyFunctions(), + hash, + FindInfo{target, seq.index()})), + true}; + } + seq.next(); + ABSL_SWISSTABLE_ASSERT(seq.index() <= capacity() && "full table!"); + } + } + + protected: + // Asserts for correctness that we run on find/find_or_prepare_insert. + template + void AssertOnFind(ABSL_ATTRIBUTE_UNUSED const K& key) { + AssertHashEqConsistent(key); + AssertNotDebugCapacity(); + } + + // Asserts that the capacity is not a sentinel invalid value. + void AssertNotDebugCapacity() const { +#ifdef NDEBUG + if (!SwisstableGenerationsEnabled()) { + return; + } +#endif + if (ABSL_PREDICT_TRUE(capacity() < + InvalidCapacity::kAboveMaxValidCapacity)) { + return; + } + assert(capacity() != InvalidCapacity::kReentrance && + "Reentrant container access during element construction/destruction " + "is not allowed."); + if constexpr (SwisstableAssertAccessToDestroyedTable()) { + if (capacity() == InvalidCapacity::kDestroyed) { + ABSL_RAW_LOG(FATAL, "Use of destroyed hash table."); + } + } + if (SwisstableGenerationsEnabled() && + ABSL_PREDICT_FALSE(capacity() >= InvalidCapacity::kMovedFrom)) { + if (capacity() == InvalidCapacity::kSelfMovedFrom) { + // If this log triggers, then a hash table was move-assigned to itself + // and then used again later without being reinitialized. + ABSL_RAW_LOG(FATAL, "Use of self-move-assigned hash table."); + } + ABSL_RAW_LOG(FATAL, "Use of moved-from hash table."); + } + } + + // Asserts that hash and equal functors provided by the user are consistent, + // meaning that `eq(k1, k2)` implies `hash(k1)==hash(k2)`. + template + void AssertHashEqConsistent(const K& key) { +#ifdef NDEBUG + return; +#endif + // If the hash/eq functors are known to be consistent, then skip validation. + if (std::is_same::value && + std::is_same::value) { + return; + } + if (std::is_scalar::value && + std::is_same>::value && + std::is_same>::value) { + return; + } + if (empty()) return; + + const size_t hash_of_arg = hash_ref()(key); + const auto assert_consistent = [&](const ctrl_t*, void* slot) { + const value_type& element = + PolicyTraits::element(static_cast(slot)); + const bool is_key_equal = + PolicyTraits::apply(EqualElement{key, eq_ref()}, element); + if (!is_key_equal) return; + + const size_t hash_of_slot = + PolicyTraits::apply(HashElement{hash_ref()}, element); + ABSL_ATTRIBUTE_UNUSED const bool is_hash_equal = + hash_of_arg == hash_of_slot; + assert((!is_key_equal || is_hash_equal) && + "eq(k1, k2) must imply that hash(k1) == hash(k2). " + "hash/eq functors are inconsistent."); + }; + + if (is_soo()) { + assert_consistent(/*unused*/ nullptr, soo_slot()); + return; + } + // We only do validation for small tables so that it's constant time. + if (capacity() > 16) return; + IterateOverFullSlots(common(), sizeof(slot_type), assert_consistent); + } + + // Attempts to find `key` in the table; if it isn't found, returns an iterator + // where the value can be inserted into, with the control byte already set to + // `key`'s H2. Returns a bool indicating whether an insertion can take place. + template + std::pair find_or_prepare_insert(const K& key) { + AssertOnFind(key); + if (is_soo()) return find_or_prepare_insert_soo(key); + return find_or_prepare_insert_non_soo(key); + } + + // Constructs the value in the space pointed by the iterator. This only works + // after an unsuccessful find_or_prepare_insert() and before any other + // modifications happen in the raw_hash_set. + // + // PRECONDITION: iter was returned from find_or_prepare_insert(k), where k is + // the key decomposed from `forward(args)...`, and the bool returned by + // find_or_prepare_insert(k) was true. + // POSTCONDITION: *m.iterator_at(i) == value_type(forward(args)...). + template + void emplace_at(iterator iter, Args&&... args) { + construct(iter.slot(), std::forward(args)...); + + assert(PolicyTraits::apply(FindElement{*this}, *iter) == iter && + "constructed value does not match the lookup key"); + } + + iterator iterator_at(size_t i) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return {control() + i, slot_array() + i, common().generation_ptr()}; + } + const_iterator iterator_at(size_t i) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return const_cast(this)->iterator_at(i); + } + + reference unchecked_deref(iterator it) { return it.unchecked_deref(); } + + private: + friend struct RawHashSetTestOnlyAccess; + + // The number of slots we can still fill without needing to rehash. + // + // This is stored separately due to tombstones: we do not include tombstones + // in the growth capacity, because we'd like to rehash when the table is + // otherwise filled with tombstones: otherwise, probe sequences might get + // unacceptably long without triggering a rehash. Callers can also force a + // rehash via the standard `rehash(0)`, which will recompute this value as a + // side-effect. + // + // See `CapacityToGrowth()`. + size_t growth_left() const { + ABSL_SWISSTABLE_ASSERT(!is_soo()); + return common().growth_left(); + } + + GrowthInfo& growth_info() { + ABSL_SWISSTABLE_ASSERT(!is_soo()); + return common().growth_info(); + } + GrowthInfo growth_info() const { + ABSL_SWISSTABLE_ASSERT(!is_soo()); + return common().growth_info(); + } + + // Prefetch the heap-allocated memory region to resolve potential TLB and + // cache misses. This is intended to overlap with execution of calculating the + // hash for a key. + void prefetch_heap_block() const { + ABSL_SWISSTABLE_ASSERT(!is_soo()); +#if ABSL_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__) + __builtin_prefetch(control(), 0, 1); +#endif + } + + CommonFields& common() { return settings_.template get<0>(); } + const CommonFields& common() const { return settings_.template get<0>(); } + + ctrl_t* control() const { + ABSL_SWISSTABLE_ASSERT(!is_soo()); + return common().control(); + } + slot_type* slot_array() const { + ABSL_SWISSTABLE_ASSERT(!is_soo()); + return static_cast(common().slot_array()); + } + slot_type* soo_slot() { + ABSL_SWISSTABLE_ASSERT(is_soo()); + ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN( + static_cast(common().soo_data())); + } + const slot_type* soo_slot() const { + ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN( + const_cast(this)->soo_slot()); + } + iterator soo_iterator() { + return {SooControl(), soo_slot(), common().generation_ptr()}; + } + const_iterator soo_iterator() const { + return const_cast(this)->soo_iterator(); + } + HashtablezInfoHandle infoz() { + ABSL_SWISSTABLE_ASSERT(!is_soo()); + return common().infoz(); + } + + hasher& hash_ref() { return settings_.template get<1>(); } + const hasher& hash_ref() const { return settings_.template get<1>(); } + key_equal& eq_ref() { return settings_.template get<2>(); } + const key_equal& eq_ref() const { return settings_.template get<2>(); } + CharAlloc& char_alloc_ref() { return settings_.template get<3>(); } + const CharAlloc& char_alloc_ref() const { + return settings_.template get<3>(); + } + + static void* get_char_alloc_ref_fn(CommonFields& common) { + auto* h = reinterpret_cast(&common); + return &h->char_alloc_ref(); + } + static void* get_hash_ref_fn(CommonFields& common) { + auto* h = reinterpret_cast(&common); + // TODO(b/397453582): Remove support for const hasher. + return const_cast*>(&h->hash_ref()); + } + static void transfer_n_slots_fn(void* set, void* dst, void* src, + size_t count) { + auto* src_slot = to_slot(src); + auto* dst_slot = to_slot(dst); + + auto* h = static_cast(set); + for (; count > 0; --count, ++src_slot, ++dst_slot) { + h->transfer(dst_slot, src_slot); + } + } + + // TODO(b/382423690): Try to type erase entire function or at least type erase + // by GetKey + Hash for memcpyable types. + // TODO(b/382423690): Try to type erase for big slots: sizeof(slot_type) > 16. + static void transfer_unprobed_elements_to_next_capacity_fn( + CommonFields& common, const ctrl_t* old_ctrl, void* old_slots, + void* probed_storage, + void (*encode_probed_element)(void* probed_storage, h2_t h2, + size_t source_offset, size_t h1)) { + const size_t new_capacity = common.capacity(); + const size_t old_capacity = PreviousCapacity(new_capacity); + ABSL_ASSUME(old_capacity + 1 >= Group::kWidth); + ABSL_ASSUME((old_capacity + 1) % Group::kWidth == 0); + + auto* set = reinterpret_cast(&common); + slot_type* old_slots_ptr = to_slot(old_slots); + ctrl_t* new_ctrl = common.control(); + slot_type* new_slots = set->slot_array(); + + const PerTableSeed seed = common.seed(); + + for (size_t group_index = 0; group_index < old_capacity; + group_index += Group::kWidth) { + GroupFullEmptyOrDeleted old_g(old_ctrl + group_index); + std::memset(new_ctrl + group_index, static_cast(ctrl_t::kEmpty), + Group::kWidth); + std::memset(new_ctrl + group_index + old_capacity + 1, + static_cast(ctrl_t::kEmpty), Group::kWidth); + // TODO(b/382423690): try to type erase everything outside of the loop. + // We will share a lot of code in expense of one function call per group. + for (auto in_fixed_group_index : old_g.MaskFull()) { + size_t old_index = group_index + in_fixed_group_index; + slot_type* old_slot = old_slots_ptr + old_index; + // TODO(b/382423690): try to avoid entire hash calculation since we need + // only one new bit of h1. + size_t hash = set->hash_of(old_slot); + size_t h1 = H1(hash, seed); + h2_t h2 = H2(hash); + size_t new_index = TryFindNewIndexWithoutProbing( + h1, old_index, old_capacity, new_ctrl, new_capacity); + // Note that encode_probed_element is allowed to use old_ctrl buffer + // till and included the old_index. + if (ABSL_PREDICT_FALSE(new_index == kProbedElementIndexSentinel)) { + encode_probed_element(probed_storage, h2, old_index, h1); + continue; + } + ABSL_SWISSTABLE_ASSERT((new_index & old_capacity) <= old_index); + ABSL_SWISSTABLE_ASSERT(IsEmpty(new_ctrl[new_index])); + new_ctrl[new_index] = static_cast(h2); + auto* new_slot = new_slots + new_index; + SanitizerUnpoisonMemoryRegion(new_slot, sizeof(slot_type)); + set->transfer(new_slot, old_slot); + SanitizerPoisonMemoryRegion(old_slot, sizeof(slot_type)); + } + } + } + + static const PolicyFunctions& GetPolicyFunctions() { + static_assert(sizeof(slot_type) <= (std::numeric_limits::max)(), + "Slot size is too large. Use std::unique_ptr for value type " + "or use absl::node_hash_{map,set}."); + static_assert(alignof(slot_type) <= + size_t{(std::numeric_limits::max)()}); + static_assert(sizeof(key_type) <= + size_t{(std::numeric_limits::max)()}); + static_assert(sizeof(value_type) <= + size_t{(std::numeric_limits::max)()}); + static constexpr size_t kBackingArrayAlignment = + BackingArrayAlignment(alignof(slot_type)); + static constexpr PolicyFunctions value = { + static_cast(sizeof(key_type)), + static_cast(sizeof(value_type)), + static_cast(sizeof(slot_type)), + static_cast(alignof(slot_type)), + static_cast(SooEnabled() ? SooCapacity() : 0), + ShouldSampleHashtablezInfoForAlloc(), + // TODO(b/328722020): try to type erase + // for standard layout and alignof(Hash) <= alignof(CommonFields). + std::is_empty_v ? &GetRefForEmptyClass + : &raw_hash_set::get_hash_ref_fn, + PolicyTraits::template get_hash_slot_fn(), + PolicyTraits::transfer_uses_memcpy() + ? TransferNRelocatable + : &raw_hash_set::transfer_n_slots_fn, + std::is_empty_v ? &GetRefForEmptyClass + : &raw_hash_set::get_char_alloc_ref_fn, + &AllocateBackingArray, + &DeallocateBackingArray, + &raw_hash_set::transfer_unprobed_elements_to_next_capacity_fn}; + return value; + } + + // Bundle together CommonFields plus other objects which might be empty. + // CompressedTuple will ensure that sizeof is not affected by any of the empty + // fields that occur after CommonFields. + absl::container_internal::CompressedTuple + settings_{CommonFields::CreateDefault(), hasher{}, + key_equal{}, CharAlloc{}}; +}; + +// Friend access for free functions in raw_hash_set.h. +struct HashtableFreeFunctionsAccess { + template + static typename Set::size_type EraseIf(Predicate& pred, Set* c) { + if (c->empty()) { + return 0; + } + if (c->is_soo()) { + auto it = c->soo_iterator(); + if (!pred(*it)) { + ABSL_SWISSTABLE_ASSERT(c->size() == 1 && + "hash table was modified unexpectedly"); + return 0; + } + c->destroy(it.slot()); + c->common().set_empty_soo(); + return 1; + } + ABSL_ATTRIBUTE_UNUSED const size_t original_size_for_assert = c->size(); + size_t num_deleted = 0; + using SlotType = typename Set::slot_type; + IterateOverFullSlots( + c->common(), sizeof(SlotType), + [&](const ctrl_t* ctrl, void* slot_void) { + auto* slot = static_cast(slot_void); + if (pred(Set::PolicyTraits::element(slot))) { + c->destroy(slot); + EraseMetaOnly(c->common(), static_cast(ctrl - c->control()), + sizeof(*slot)); + ++num_deleted; + } + }); + // NOTE: IterateOverFullSlots allow removal of the current element, so we + // verify the size additionally here. + ABSL_SWISSTABLE_ASSERT(original_size_for_assert - num_deleted == + c->size() && + "hash table was modified unexpectedly"); + return num_deleted; + } + + template + static void ForEach(Callback& cb, Set* c) { + if (c->empty()) { + return; + } + if (c->is_soo()) { + cb(*c->soo_iterator()); + return; + } + using SlotType = typename Set::slot_type; + using ElementTypeWithConstness = decltype(*c->begin()); + IterateOverFullSlots( + c->common(), sizeof(SlotType), [&cb](const ctrl_t*, void* slot) { + ElementTypeWithConstness& element = + Set::PolicyTraits::element(static_cast(slot)); + cb(element); + }); + } +}; + +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template +typename raw_hash_set::size_type EraseIf( + Predicate& pred, raw_hash_set* c) { + return HashtableFreeFunctionsAccess::EraseIf(pred, c); +} + +// Calls `cb` for all elements in the container `c`. +template +void ForEach(Callback& cb, raw_hash_set* c) { + return HashtableFreeFunctionsAccess::ForEach(cb, c); +} +template +void ForEach(Callback& cb, const raw_hash_set* c) { + return HashtableFreeFunctionsAccess::ForEach(cb, c); +} + +namespace hashtable_debug_internal { +template +struct HashtableDebugAccess> { + using Traits = typename Set::PolicyTraits; + using Slot = typename Traits::slot_type; + + static size_t GetNumProbes(const Set& set, + const typename Set::key_type& key) { + if (set.is_soo()) return 0; + size_t num_probes = 0; + const size_t hash = set.hash_ref()(key); + auto seq = probe(set.common(), hash); + const h2_t h2 = H2(hash); + const ctrl_t* ctrl = set.control(); + while (true) { + container_internal::Group g{ctrl + seq.offset()}; + for (uint32_t i : g.Match(h2)) { + if (Traits::apply( + typename Set::template EqualElement{ + key, set.eq_ref()}, + Traits::element(set.slot_array() + seq.offset(i)))) + return num_probes; + ++num_probes; + } + if (g.MaskEmpty()) return num_probes; + seq.next(); + ++num_probes; + } + } + + static size_t AllocatedByteSize(const Set& c) { + size_t capacity = c.capacity(); + if (capacity == 0) return 0; + size_t m = + c.is_soo() ? 0 : c.common().alloc_size(sizeof(Slot), alignof(Slot)); + + size_t per_slot = Traits::space_used(static_cast(nullptr)); + if (per_slot != ~size_t{}) { + m += per_slot * c.size(); + } else { + for (auto it = c.begin(); it != c.end(); ++it) { + m += Traits::space_used(it.slot()); + } + } + return m; + } +}; + +} // namespace hashtable_debug_internal + +// Extern template instantiations reduce binary size and linker input size. +// Function definition is in raw_hash_set.cc. +extern template size_t GrowSooTableToNextCapacityAndPrepareInsert<0, false>( + CommonFields&, const PolicyFunctions&, size_t, ctrl_t); +extern template size_t GrowSooTableToNextCapacityAndPrepareInsert<1, true>( + CommonFields&, const PolicyFunctions&, size_t, ctrl_t); +extern template size_t GrowSooTableToNextCapacityAndPrepareInsert<4, true>( + CommonFields&, const PolicyFunctions&, size_t, ctrl_t); +extern template size_t GrowSooTableToNextCapacityAndPrepareInsert<8, true>( + CommonFields&, const PolicyFunctions&, size_t, ctrl_t); +#if UINTPTR_MAX == UINT64_MAX +extern template size_t GrowSooTableToNextCapacityAndPrepareInsert<16, true>( + CommonFields&, const PolicyFunctions&, size_t, ctrl_t); +#endif + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#undef ABSL_SWISSTABLE_ENABLE_GENERATIONS +#undef ABSL_SWISSTABLE_IGNORE_UNINITIALIZED +#undef ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN +#undef ABSL_SWISSTABLE_ASSERT + +#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_set_resize_impl.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_set_resize_impl.h new file mode 100644 index 00000000..149d9e82 --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/raw_hash_set_resize_impl.h @@ -0,0 +1,80 @@ +// Copyright 2025 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This is a private implementation detail of resize algorithm of +// raw_hash_set. It is exposed in a separate file for testing purposes. + +#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_RESIZE_IMPL_H_ +#define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_RESIZE_IMPL_H_ + +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Encoding for probed elements used for smaller tables. +// Data is encoded into single integer. +// Storage format for 4 bytes: +// - 7 bits for h2 +// - 12 bits for source_offset +// - 13 bits for h1 +// Storage format for 8 bytes: +// - 7 bits for h2 +// - 28 bits for source_offset +// - 29 bits for h1 +// Storage format for 16 bytes: +// - 7 bits for h2 +// - 57 bits for source_offset +// - 58 bits for h1 +template +struct ProbedItemImpl { + static constexpr IntType kH2Bits = 7; + + static constexpr IntType kMaxOldBits = (kTotalBits - kH2Bits) / 2; + static constexpr IntType kMaxOldCapacity = (IntType{1} << kMaxOldBits) - 1; + + // We always have one bit more for h1. + static constexpr IntType kMaxNewBits = kMaxOldBits + 1; + static constexpr IntType kMaxNewCapacity = (IntType{1} << kMaxNewBits) - 1; + + static constexpr IntType kH2Shift = (kTotalBits - kH2Bits); + static_assert(kMaxNewBits + kMaxOldBits + kH2Bits == kTotalBits); + + ProbedItemImpl() = default; + ProbedItemImpl(uint8_t h2_arg, size_t source_offset_arg, size_t h1_arg) + : h2(h2_arg), + source_offset(static_cast(source_offset_arg)), + h1(static_cast(h1_arg)) {} + + IntType h2 : kH2Bits; + IntType source_offset : kMaxOldBits; + IntType h1 : kMaxNewBits; +}; + +using ProbedItem4Bytes = ProbedItemImpl; +static_assert(sizeof(ProbedItem4Bytes) == 4); +using ProbedItem8Bytes = ProbedItemImpl; +static_assert(sizeof(ProbedItem8Bytes) == 8); +using ProbedItem16Bytes = ProbedItemImpl; +static_assert(sizeof(ProbedItem16Bytes) == 16); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_RESIZE_IMPL_H_ diff --git a/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/test_allocator.h b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/test_allocator.h new file mode 100644 index 00000000..8e365a3c --- /dev/null +++ b/pkg/apm/webrtc/third_party/abseil-cpp/absl/container/internal/test_allocator.h @@ -0,0 +1,387 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_ +#define ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_ + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// This is a stateful allocator, but the state lives outside of the +// allocator (in whatever test is using the allocator). This is odd +// but helps in tests where the allocator is propagated into nested +// containers - that chain of allocators uses the same state and is +// thus easier to query for aggregate allocation information. +template +class CountingAllocator { + public: + using Allocator = std::allocator; + using AllocatorTraits = std::allocator_traits; + using value_type = typename AllocatorTraits::value_type; + using pointer = typename AllocatorTraits::pointer; + using const_pointer = typename AllocatorTraits::const_pointer; + using size_type = typename AllocatorTraits::size_type; + using difference_type = typename AllocatorTraits::difference_type; + + CountingAllocator() = default; + explicit CountingAllocator(int64_t* bytes_used) : bytes_used_(bytes_used) {} + CountingAllocator(int64_t* bytes_used, int64_t* instance_count) + : bytes_used_(bytes_used), instance_count_(instance_count) {} + + template + CountingAllocator(const CountingAllocator& x) + : bytes_used_(x.bytes_used_), instance_count_(x.instance_count_) {} + + pointer allocate( + size_type n, + typename AllocatorTraits::const_void_pointer hint = nullptr) { + Allocator allocator; + pointer ptr = AllocatorTraits::allocate(allocator, n, hint); + if (bytes_used_ != nullptr) { + *bytes_used_ += n * sizeof(T); + } + return ptr; + } + + void deallocate(pointer p, size_type n) { + Allocator allocator; + AllocatorTraits::deallocate(allocator, p, n); + if (bytes_used_ != nullptr) { + *bytes_used_ -= n * sizeof(T); + } + } + + template + void construct(U* p, Args&&... args) { + Allocator allocator; + AllocatorTraits::construct(allocator, p, std::forward(args)...); + if (instance_count_ != nullptr) { + *instance_count_ += 1; + } + } + + template + void destroy(U* p) { + Allocator allocator; + // Ignore GCC warning bug. +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuse-after-free" +#endif + AllocatorTraits::destroy(allocator, p); +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic pop +#endif + if (instance_count_ != nullptr) { + *instance_count_ -= 1; + } + } + + template + class rebind { + public: + using other = CountingAllocator; + }; + + friend bool operator==(const CountingAllocator& a, + const CountingAllocator& b) { + return a.bytes_used_ == b.bytes_used_ && + a.instance_count_ == b.instance_count_; + } + + friend bool operator!=(const CountingAllocator& a, + const CountingAllocator& b) { + return !(a == b); + } + + int64_t* bytes_used_ = nullptr; + int64_t* instance_count_ = nullptr; +}; + +template +struct CopyAssignPropagatingCountingAlloc : public CountingAllocator { + using propagate_on_container_copy_assignment = std::true_type; + + using Base = CountingAllocator; + using Base::Base; + + template + explicit CopyAssignPropagatingCountingAlloc( + const CopyAssignPropagatingCountingAlloc& other) + : Base(other.bytes_used_, other.instance_count_) {} + + template + struct rebind { + using other = CopyAssignPropagatingCountingAlloc; + }; +}; + +template +struct MoveAssignPropagatingCountingAlloc : public CountingAllocator { + using propagate_on_container_move_assignment = std::true_type; + + using Base = CountingAllocator; + using Base::Base; + + template + explicit MoveAssignPropagatingCountingAlloc( + const MoveAssignPropagatingCountingAlloc& other) + : Base(other.bytes_used_, other.instance_count_) {} + + template + struct rebind { + using other = MoveAssignPropagatingCountingAlloc; + }; +}; + +template +struct SwapPropagatingCountingAlloc : public CountingAllocator { + using propagate_on_container_swap = std::true_type; + + using Base = CountingAllocator; + using Base::Base; + + template + explicit SwapPropagatingCountingAlloc( + const SwapPropagatingCountingAlloc& other) + : Base(other.bytes_used_, other.instance_count_) {} + + template + struct rebind { + using other = SwapPropagatingCountingAlloc; + }; +}; + +// Tries to allocate memory at the minimum alignment even when the default +// allocator uses a higher alignment. +template +struct MinimumAlignmentAlloc : std::allocator { + MinimumAlignmentAlloc() = default; + + template + explicit MinimumAlignmentAlloc(const MinimumAlignmentAlloc& /*other*/) {} + + template + struct rebind { + using other = MinimumAlignmentAlloc; + }; + + T* allocate(size_t n) { + T* ptr = std::allocator::allocate(n + 1); + char* cptr = reinterpret_cast(ptr); + cptr += alignof(T); + return reinterpret_cast(cptr); + } + + void deallocate(T* ptr, size_t n) { + char* cptr = reinterpret_cast(ptr); + cptr -= alignof(T); + std::allocator::deallocate(reinterpret_cast(cptr), n + 1); + } +}; + +inline bool IsAssertEnabled() { + // Use an assert with side-effects to figure out if they are actually enabled. + bool assert_enabled = false; + assert([&]() { // NOLINT + assert_enabled = true; + return true; + }()); + return assert_enabled; +} + +template